iterstats 0.7.0

Statistics for rust iterators.
Documentation
//! Calculate the standard deviation.

use crate::Iterstats;

/// Calculate the population standard deviation of an iterator.
pub trait StdDev<A = Self>: Sized {
    /// The type of the output.
    type Output;

    /// Calculate the population standard deviation.
    fn stddev<I>(iter: I) -> Self::Output
    where
        I: Iterator<Item = A>;
}

macro_rules! stddev_impl {
    ($typ:ty) => {
        impl StdDev for $typ {
            type Output = $typ;

            fn stddev<I>(iter: I) -> Self::Output
            where
                I: Iterator<Item = Self>,
            {
                let var = iter.variance();
                var.sqrt()
            }
        }

        impl StdDev for &$typ {
            type Output = $typ;

            fn stddev<I>(iter: I) -> Self::Output
            where
                I: Iterator<Item = Self>,
            {
                iter.map(|i| *i).stddev()
            }
        }
    };
}

stddev_impl!(f64);
stddev_impl!(f32);

#[cfg(test)]
mod tests {
    use super::*;
    use paste::paste;

    macro_rules! test_stddev {
        ( $name:ident: $iterty:ty as $iter:expr ; into_iter => nan) => {
            paste! {
                #[test]
                fn [<$name _into_iter >]() {
                    let stddev = <$iterty>::stddev($iter.into_iter());
                    assert!(stddev.is_nan());
                }
            }
        };
        ( $name:ident: $iterty:ty as $iter:expr ; iter => nan) => {
            paste! {
                #[test]
                fn [<$name _iter >]() {
                    let stddev = <&$iterty>::stddev($iter.iter());
                    assert!(stddev.is_nan());
                }
            }
        };
        ( $name:ident: $iterty:ty as $iter:expr => nan) => {
            test_stddev!($name: $iterty as $iter; into_iter => nan);
            test_stddev!($name: $iterty as $iter; iter => nan);
        };
        ( $name:ident: $iterty:ty as $iter:expr ; into_iter =>  $expected:expr) => {
            paste! {
                #[test]
                fn [<$name _into_iter >]() {
                    let stddev = <$iterty>::stddev($iter.into_iter());
                    assert_eq!(stddev, $expected);
                }
            }
        };
        ( $name:ident: $iterty:ty as $iter:expr ; iter =>  $expected:expr) => {
            paste! {
                #[test]
                fn [<$name _iter >]() {
                    let stddev = <&$iterty>::stddev($iter.iter());
                    assert_eq!(stddev, $expected);
                }
            }
        };
        ( $name:ident: $iterty:ty as $iter:expr =>  $expected:expr) => {
            test_stddev!($name: $iterty as $iter; into_iter => $expected);
            test_stddev!($name: $iterty as $iter; iter => $expected);
        };
    }

    test_stddev!(f64: f64 as [1.0, 2.0, 3.0, 4.0] => 1.25f64.sqrt());
    test_stddev!(f64_max: f64 as [f64::MAX, f64::MAX] => 0.0);
    test_stddev!(f64_min: f64 as [f64::MIN, f64::MIN] => 0.0);
    test_stddev!(f64_minmax: f64 as [f64::MAX, f64::MIN] => nan);
    test_stddev!(f64_nan: f64 as [1.0, 2.0, f64::NAN, 3.0, 4.0] => nan);
    test_stddev!(f64_inf: f64 as [1.0, 2.0, f64::INFINITY, 3.0, 4.0] => nan);
    test_stddev!(f64_neg_inf: f64 as [1.0, 2.0, f64::NEG_INFINITY, 3.0, 4.0] => nan);
    test_stddev!(f32: f32 as [1.0, 2.0, 3.0, 4.0] => 1.25f32.sqrt());
    test_stddev!(f32_max: f32 as [f32::MAX, f32::MAX] => 0.0);
    test_stddev!(f32_min: f32 as [f32::MIN, f32::MIN] => 0.0);
    test_stddev!(f32_minmax: f32 as [f32::MAX, f32::MIN] => nan);
    test_stddev!(f32_nan: f32 as [1.0, 2.0, f32::NAN, 3.0, 4.0] => nan);
    test_stddev!(f32_inf: f32 as [1.0, 2.0, f32::INFINITY, 3.0, 4.0] => nan);
    test_stddev!(f32_neg_inf: f32 as [1.0, 2.0, f32::NEG_INFINITY, 3.0, 4.0] => nan);
}