From 0d1e1c4f12fa82a1c36e2dd2a41f62c46450564e Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Wed, 24 May 2017 19:16:52 +0200 Subject: [PATCH] Implement `Max` --- src/lib.rs | 2 +- src/minmax.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/max.rs | 34 +++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 tests/max.rs diff --git a/src/lib.rs b/src/lib.rs index 8f494b9..0665592 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,4 +41,4 @@ mod reduce; pub use average::{Average, AverageWithError}; pub use weighted_average::{WeightedAverage, WeightedAverageWithError}; -pub use minmax::Min; +pub use minmax::{Min, Max}; diff --git a/src/minmax.rs b/src/minmax.rs index 893427d..e83229c 100644 --- a/src/minmax.rs +++ b/src/minmax.rs @@ -7,6 +7,11 @@ fn min(a: f64, b: f64) -> f64 { a.min(b) } +/// Calculate the maximum of `a` and `b`. +fn max(a: f64, b: f64) -> f64 { + a.max(b) +} + /// Estimate the minimum of a sequence of numbers ("population"). /// /// Everything is calculated iteratively using constant memory, so the sequence @@ -86,3 +91,83 @@ impl core::iter::FromIterator for Min { a } } + +/// Estimate the maximum of a sequence of numbers ("population"). +/// +/// Everything is calculated iteratively using constant memory, so the sequence +/// of numbers can be an iterator. +/// +/// +/// ## Example +/// +/// ``` +/// use average::Max; +/// +/// let a: Max = (1..6).map(Into::into).collect(); +/// assert_eq!(a.max(), 5.); +/// ``` +#[derive(Debug, Clone)] +pub struct Max { + r: Reduce f64>, +} + +impl Max { + /// Create a new maxium estimator from a given value. + #[inline] + pub fn from_value(x: f64) -> Max { + Max { + r: Reduce::from_value_and_fn(x, max), + } + } + + /// Create a new maximum estimator. + #[inline] + pub fn new() -> Max { + Max::from_value(::core::f64::NEG_INFINITY) + } + + /// Add an element sampled from the population. + #[inline] + pub fn add(&mut self, x: f64) { + self.r.add(x); + } + + /// Estimate the maxium of the population. + #[inline] + pub fn max(&self) -> f64 { + self.r.reduction() + } + + /// Merge another sample into this one. + /// + /// + /// ## Example + /// + /// ``` + /// use average::Max; + /// + /// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.]; + /// let (left, right) = sequence.split_at(3); + /// let max_total: Max = sequence.iter().map(|x| *x).collect(); + /// let mut max_left: Max = left.iter().map(|x| *x).collect(); + /// let max_right: Max = right.iter().map(|x| *x).collect(); + /// max_left.merge(&max_right); + /// assert_eq!(max_total.max(), max_left.max()); + /// ``` + #[inline] + pub fn merge(&mut self, other: &Max) { + self.r.merge(&other.r); + } +} + +impl core::iter::FromIterator for Max { + fn from_iter(iter: T) -> Max + where T: IntoIterator + { + let mut a = Max::new(); + for i in iter { + a.add(i); + } + a + } +} diff --git a/tests/max.rs b/tests/max.rs new file mode 100644 index 0000000..7ee026e --- /dev/null +++ b/tests/max.rs @@ -0,0 +1,34 @@ +extern crate average; + +extern crate core; + +use core::iter::Iterator; + +use average::Max; + +#[test] +fn trivial() { + let mut m = Max::new(); + m.add(2.); + m.add(1.); + assert_eq!(m.max(), 2.); + m.add(3.); + m.add(1.); + assert_eq!(m.max(), 3.) +} + +#[test] +fn merge() { + let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.]; + for mid in 1..sequence.len() { + let (left, right) = sequence.split_at(mid); + let max_total: Max = sequence.iter().map(|x| *x).collect(); + assert_eq!(max_total.max(), 9.); + let mut max_left: Max = left.iter().map(|x| *x).collect(); + assert_eq!(max_left.max(), sequence[mid - 1]); + let max_right: Max = right.iter().map(|x| *x).collect(); + assert_eq!(max_right.max(), 9.); + max_left.merge(&max_right); + assert_eq!(max_total.max(), max_left.max()); + } +}