iterstats 0.7.0

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

use crate::Iterstats;

/// Calculate the range (max & min) of an iterator in one pass.
pub trait Range<A = Self>: Sized {
    /// The type of the output.
    type Output;

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

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

            fn range<I: Iterator<Item = Self>>(iter: I) -> Option<(Self::Output, Self::Output)> {
                let mut range = None;
                for item in iter {
                    if let Some((min, max)) = &mut range {
                        if item < *min {
                            *min = item;
                        }
                        if item > *max {
                            *max = item;
                        }
                    } else {
                        range = Some((item, item));
                    }
                }
                range
            }
        }

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

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

range_impl!(f64);
range_impl!(f32);
range_impl!(u128);
range_impl!(u64);
range_impl!(u32);
range_impl!(u16);
range_impl!(u8);
range_impl!(usize);
range_impl!(i128);
range_impl!(i64);
range_impl!(i32);
range_impl!(i16);
range_impl!(i8);
range_impl!(isize);

#[cfg(test)]
mod tests {

    use super::*;
    use paste::paste;

    macro_rules! test_range {
        ($name:ident: $typ:ty as $iter:expr ; iter => None) => {
            paste! {
                #[test]
                fn [<$name _iter>]() {
                    let range = <&$typ>::range($iter.iter());
                    assert!(range.is_none())
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr ; into_iter => None) => {
            paste! {
                #[test]
                fn [<$name _into_iter>]() {
                    let range = $typ::range($iter.into_iter());
                    assert!(range.is_none())
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr  => None) => {
            test_range!($name: $typ as $iter; iter => None);
            test_range!($name: $typ as $iter; into_iter => None);
        };
        ($name:ident: $typ:ty as $iter:expr ; iter => ($min:expr, $max:expr)) => {
            paste! {
                #[test]
                fn [<$name _iter>]() {
                    let (min, max) = <&$typ>::range($iter.iter()).unwrap();
                    assert_eq!(min, $min);
                    assert_eq!(max, $max)
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr ; into_iter => ($min:expr, $max:expr)) => {
            paste! {
                #[test]
                fn [<$name _into_iter>]() {
                    let (min, max) = $typ::range($iter.into_iter()).unwrap();
                    assert_eq!(min, $min);
                    assert_eq!(max, $max)
                }
            }
        };
        ($name:ident: $typ:ty as $iter:expr  => ($min:expr, $max:expr)) => {
            test_range!($name: $typ as $iter; iter => ($min, $max));
            test_range!($name: $typ as $iter; into_iter => ($min, $max));
        };
    }

    test_range!(empty: u32 as Vec::new() => None);

    test_range!(f64: f64 as [0.1, -32.1, 23.2, 0., f64::NAN] => (-32.1, 23.2));
    test_range!(f64_with_inf: f64 as [0.1, f64::NEG_INFINITY, -32.1, 23.2, f64::INFINITY, 0., f64::NAN] => (f64::NEG_INFINITY, f64::INFINITY));
    test_range!(f32: f32 as [0.1, -32.1, 23.2, 0., f32::NAN] => (-32.1, 23.2));
    test_range!(f32_with_inf: f32 as [0.1, f32::NEG_INFINITY, -32.1, 23.2, f32::INFINITY, 0., f32::NAN] => (f32::NEG_INFINITY, f32::INFINITY));
    test_range!(u128: u128 as [1, 32, 23, 0] => (0, 32));
    test_range!(u64: u64 as [1, 32, 23, 0] => (0, 32));
    test_range!(u32: u32 as [1, 32, 23, 0] => (0, 32));
    test_range!(u16: u16 as [1, 32, 23, 0] => (0, 32));
    test_range!(u8: u8 as [1, 32, 23, 0] => (0, 32));
    test_range!(usize: usize as [1, 32, 23, 0] => (0, 32));
    test_range!(i128: i128 as [-4, -34, -1, -3] => (-34, -1));
    test_range!(i64: i64 as [-4, -34, -1, -3] => (-34, -1));
    test_range!(i32: i32 as [-4, -34, -1, -3] => (-34, -1));
    test_range!(i16: i16 as [-4, -34, -1, -3] => (-34, -1));
    test_range!(i8: i8 as [-4, -34, -1, -3] => (-34, -1));
    test_range!(isize: isize as [-4, -34, -1, -3] => (-34, -1));
}