iterstats 0.7.0

Statistics for rust iterators.
Documentation
//! Calculate the median.

use crate::Iterstats;

/// Calculate the mean of an iterator.
pub trait Median<A = Self>: Sized {
    /// The type of the output.
    type Output;

    /// Calculate the mean.
    fn median<I: Iterator<Item = A>>(iter: I) -> Self::Output;
}

macro_rules! median_impl {
    (partial_ord: $typ:ty) => {
        impl Median for $typ {
            type Output = $typ;

            fn median<I: Iterator<Item = Self>>(iter: I) -> Self::Output {
                let mut v = iter.filter(|i| !i.is_nan()).collect::<Vec<_>>();
                v.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
                if v.len() % 2 == 0 {
                    (v[v.len() / 2 - 1] + v[v.len() / 2]) / 2.
                } else {
                    v[v.len() / 2]
                }
            }
        }

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

            fn median<I: Iterator<Item = Self>>(iter: I) -> Self::Output {
                iter.map(|i| *i).median()
            }
        }
    };
    (ord: $typ:ty) => {
        impl Median for $typ {
            type Output = $typ;

            fn median<I: Iterator<Item = Self>>(iter: I) -> Self::Output {
                let mut v = iter.collect::<Vec<_>>();
                v.sort_unstable();
                if v.len() % 2 == 0 {
                    (v[v.len() / 2 - 1] + v[v.len() / 2]) / 2
                } else {
                    v[v.len() / 2]
                }
            }
        }

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

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

median_impl!(partial_ord: f64);
median_impl!(partial_ord: f32);
median_impl!(ord: u128);
median_impl!(ord: u64);
median_impl!(ord: u32);
median_impl!(ord: u16);
median_impl!(ord: u8);
median_impl!(ord: usize);
median_impl!(ord: i128);
median_impl!(ord: i64);
median_impl!(ord: i32);
median_impl!(ord: i16);
median_impl!(ord: i8);
median_impl!(ord: isize);

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

    macro_rules! test_median {
        ($name:ident: $typ:ty as $iter:expr ; into_iter => $expected:expr) => {
            paste! {
                #[test]
                fn [<$name _into_iter>]() {
                    let median = $typ::median($iter.into_iter());
                    assert_eq!(median, $expected)
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr ; iter => $expected:expr) => {
            paste! {
                #[test]
                fn [<$name _iter>]() {
                    let median = <&$typ>::median($iter.iter());
                    assert_eq!(median, $expected)
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr => $expected:expr) => {
            test_median!($name: $typ as $iter; iter => $expected);
            test_median!($name: $typ as $iter; into_iter=> $expected);
        };
    }

    test_median!(f64_odd: f64 as [12., 17., 11., 8., 9.] => 11.);
    test_median!(f32_odd: f32 as [12., 17., 11., 8., 9.] => 11.);
    test_median!(u128_odd: u128 as [12, 17, 11, 8, 9] => 11);
    test_median!(u64_odd: u64 as [12, 17, 11, 8, 9] => 11);
    test_median!(u32_odd: u32 as [12, 17, 11, 8, 9] => 11);
    test_median!(u16_odd: u16 as [12, 17, 11, 8, 9] => 11);
    test_median!(u8_odd: u8 as [12, 17, 11, 8, 9] => 11);
    test_median!(usize_odd: usize as [12, 17, 11, 8, 9] => 11);
    test_median!(i128_odd: i128 as [12, 17, 11, 8, 9] => 11);
    test_median!(i64_odd: i64 as [12, 17, 11, 8, 9] => 11);
    test_median!(i32_odd: i32 as [12, 17, 11, 8, 9] => 11);
    test_median!(i16_odd: i16 as [12, 17, 11, 8, 9] => 11);
    test_median!(i8_odd: i8 as [12, 17, 11, 8, 9] => 11);
    test_median!(isize_odd: isize as [12, 17, 11, 8, 9] => 11);

    test_median!(f64_even: f64 as [12., 17., 8., 9.] => 10.5);
    test_median!(f32_even: f32 as [12., 17., 8., 9.] => 10.5);
    test_median!(u128_even: u128 as [12, 17, 8, 9] => 10);
    test_median!(u64_even: u64 as [12, 17, 8, 9] => 10);
    test_median!(u32_even: u32 as [12, 17, 8, 9] => 10);
    test_median!(u16_even: u16 as [12, 17, 8, 9] => 10);
    test_median!(u8_even: u8 as [12, 17, 8, 9] => 10);
    test_median!(usize_even: usize as [12, 17, 8, 9] => 10);
    test_median!(i128_even: i128 as [12, 17, 8, 9] => 10);
    test_median!(i64_even: i64 as [12, 17, 8, 9] => 10);
    test_median!(i32_even: i32 as [12, 17, 8, 9] => 10);
    test_median!(i16_even: i16 as [12, 17, 8, 9] => 10);
    test_median!(i8_even: i8 as [12, 17, 8, 9] => 10);
    test_median!(isize_even: isize as [12, 17, 8, 9] => 10);

    test_median!(f64_with_inf: f64 as [12., f64::INFINITY, 11., 8., 9.] => 11.);
    test_median!(f64_with_neg_inf: f64 as [12., 16., 11., f64::NEG_INFINITY, 9.] => 11.);
    test_median!(f64_with_nan: f64 as [12., f64::NAN, 16., 11., 8., 9.] => 11.);
    test_median!(f32_with_inf: f32 as [12., f32::INFINITY, 11., 8., 9.] => 11.);
    test_median!(f32_with_neg_inf: f32 as [12., 16., 11., f32::NEG_INFINITY, 9.] => 11.);
    test_median!(f32_with_nan: f32 as [12., f32::NAN, 16., 11., 8., 9.] => 11.);
}