use crate::Iterstats;
pub trait Median<A = Self>: Sized {
type Output;
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.);
}