iterstats/
stddev.rs

1//! Calculate the standard deviation.
2
3use crate::Iterstats;
4
5/// Calculate the population standard deviation of an iterator.
6pub trait StdDev<A = Self>: Sized {
7    /// The type of the output.
8    type Output;
9
10    /// Calculate the population standard deviation.
11    fn stddev<I>(iter: I) -> Self::Output
12    where
13        I: Iterator<Item = A>;
14}
15
16macro_rules! stddev_impl {
17    ($typ:ty) => {
18        impl StdDev for $typ {
19            type Output = $typ;
20
21            fn stddev<I>(iter: I) -> Self::Output
22            where
23                I: Iterator<Item = Self>,
24            {
25                let var = iter.variance();
26                var.sqrt()
27            }
28        }
29
30        impl StdDev for &$typ {
31            type Output = $typ;
32
33            fn stddev<I>(iter: I) -> Self::Output
34            where
35                I: Iterator<Item = Self>,
36            {
37                iter.map(|i| *i).stddev()
38            }
39        }
40    };
41}
42
43stddev_impl!(f64);
44stddev_impl!(f32);
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use paste::paste;
50
51    macro_rules! test_stddev {
52        ( $name:ident: $iterty:ty as $iter:expr ; into_iter => nan) => {
53            paste! {
54                #[test]
55                fn [<$name _into_iter >]() {
56                    let stddev = <$iterty>::stddev($iter.into_iter());
57                    assert!(stddev.is_nan());
58                }
59            }
60        };
61        ( $name:ident: $iterty:ty as $iter:expr ; iter => nan) => {
62            paste! {
63                #[test]
64                fn [<$name _iter >]() {
65                    let stddev = <&$iterty>::stddev($iter.iter());
66                    assert!(stddev.is_nan());
67                }
68            }
69        };
70        ( $name:ident: $iterty:ty as $iter:expr => nan) => {
71            test_stddev!($name: $iterty as $iter; into_iter => nan);
72            test_stddev!($name: $iterty as $iter; iter => nan);
73        };
74        ( $name:ident: $iterty:ty as $iter:expr ; into_iter =>  $expected:expr) => {
75            paste! {
76                #[test]
77                fn [<$name _into_iter >]() {
78                    let stddev = <$iterty>::stddev($iter.into_iter());
79                    assert_eq!(stddev, $expected);
80                }
81            }
82        };
83        ( $name:ident: $iterty:ty as $iter:expr ; iter =>  $expected:expr) => {
84            paste! {
85                #[test]
86                fn [<$name _iter >]() {
87                    let stddev = <&$iterty>::stddev($iter.iter());
88                    assert_eq!(stddev, $expected);
89                }
90            }
91        };
92        ( $name:ident: $iterty:ty as $iter:expr =>  $expected:expr) => {
93            test_stddev!($name: $iterty as $iter; into_iter => $expected);
94            test_stddev!($name: $iterty as $iter; iter => $expected);
95        };
96    }
97
98    test_stddev!(f64: f64 as [1.0, 2.0, 3.0, 4.0] => 1.25f64.sqrt());
99    test_stddev!(f64_max: f64 as [f64::MAX, f64::MAX] => 0.0);
100    test_stddev!(f64_min: f64 as [f64::MIN, f64::MIN] => 0.0);
101    test_stddev!(f64_minmax: f64 as [f64::MAX, f64::MIN] => nan);
102    test_stddev!(f64_nan: f64 as [1.0, 2.0, f64::NAN, 3.0, 4.0] => nan);
103    test_stddev!(f64_inf: f64 as [1.0, 2.0, f64::INFINITY, 3.0, 4.0] => nan);
104    test_stddev!(f64_neg_inf: f64 as [1.0, 2.0, f64::NEG_INFINITY, 3.0, 4.0] => nan);
105    test_stddev!(f32: f32 as [1.0, 2.0, 3.0, 4.0] => 1.25f32.sqrt());
106    test_stddev!(f32_max: f32 as [f32::MAX, f32::MAX] => 0.0);
107    test_stddev!(f32_min: f32 as [f32::MIN, f32::MIN] => 0.0);
108    test_stddev!(f32_minmax: f32 as [f32::MAX, f32::MIN] => nan);
109    test_stddev!(f32_nan: f32 as [1.0, 2.0, f32::NAN, 3.0, 4.0] => nan);
110    test_stddev!(f32_inf: f32 as [1.0, 2.0, f32::INFINITY, 3.0, 4.0] => nan);
111    test_stddev!(f32_neg_inf: f32 as [1.0, 2.0, f32::NEG_INFINITY, 3.0, 4.0] => nan);
112}