reductor/reductors/
mean.rs

1use super::state::NonEmptyState;
2use crate::Reductor;
3
4/// Reductor that computes the [arithmetic mean] of items yielded by an iterator.
5///
6/// The generic type `F` must be one of [`f32`] or [`f64`], but the iterator's item type
7/// can be any type that implements [`Into<F>`], e.g. it is possible to compute a mean
8/// of type `f32` from an iterator yielding `i16`s.
9///
10/// [arithmetic mean]: https://en.wikipedia.org/wiki/Arithmetic_mean
11///
12/// # Examples
13/// ```rust
14/// use reductor::{Reduce, Mean};
15///
16/// let Mean::<f32>(mean) = [2i16, -1, -23, 42, 13]
17///     .into_iter()
18///     .reduce_with::<Option<_>>()
19///     .unwrap();
20/// assert!((mean - 6.6).abs() < f32::EPSILON);
21/// ```
22#[repr(transparent)]
23#[allow(clippy::derive_partial_eq_without_eq)] // `F` never impls `Eq`
24#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
25pub struct Mean<F>(pub F);
26
27macro_rules! impl_mean {
28    ($f:ty) => {
29        impl<T> Reductor<T> for Mean<$f>
30        where
31            T: Into<$f>,
32        {
33            type State = NonEmptyState<($f, usize)>;
34
35            #[inline]
36            fn new(item: T) -> Self::State {
37                NonEmptyState((item.into(), 1))
38            }
39
40            #[inline]
41            fn reduce(NonEmptyState((mean, count)): Self::State, item: T) -> Self::State {
42                NonEmptyState((
43                    mean.mul_add(count as $f, item.into()) / (count + 1) as $f,
44                    count + 1,
45                ))
46            }
47
48            #[inline]
49            fn into_result(NonEmptyState((mean, _)): Self::State) -> Self {
50                Self(mean)
51            }
52        }
53    };
54}
55
56impl_mean!(f32);
57impl_mean!(f64);
58
59#[cfg(test)]
60mod tests {
61    use crate::Reduce;
62
63    use super::*;
64
65    #[test]
66    fn test_mean() {
67        macro_rules! test {
68            ($f:ty) => {
69                let Mean::<$f>(mean) = [0.48, 3., 2.64]
70                    .into_iter()
71                    .reduce_with::<Option<_>>()
72                    .unwrap();
73                assert!((mean - 2.04).abs() < <$f>::EPSILON);
74            };
75        }
76
77        test!(f32);
78        test!(f64);
79    }
80}