use crate::{welford_online::welford_online, Iterstats};
pub trait Variance<A = Self>: Sized {
type Output;
fn variance<I>(iter: I) -> Self::Output
where
I: Iterator<Item = A>;
}
macro_rules! variance_impl {
($typ:ty) => {
impl Variance for $typ {
type Output = $typ;
fn variance<I>(iter: I) -> Self::Output
where
I: Iterator<Item = Self>,
{
let wo = welford_online(iter);
wo.sum_of_squares / wo.count
}
}
impl Variance for &$typ {
type Output = $typ;
fn variance<I>(iter: I) -> Self::Output
where
I: Iterator<Item = Self>,
{
iter.map(|i| *i).variance()
}
}
};
}
variance_impl!(f64);
variance_impl!(f32);
#[cfg(test)]
mod tests {
use super::*;
use paste::paste;
macro_rules! test_variance {
( $name:ident: $iterty:ty as $iter:expr; into_iter => nan ) => {
paste! {
#[test]
fn [<$name _into_iter >]() {
let var = <$iterty>::variance($iter.into_iter());
assert!(var.is_nan());
}
}
};
( $name:ident: $iterty:ty as $iter:expr; iter => nan ) => {
paste! {
#[test]
fn [<$name _iter >]() {
let var = <&$iterty>::variance($iter.iter());
assert!(var.is_nan());
}
}
};
( $name:ident: $iterty:ty as $iter:expr => nan ) => {
test_variance!($name: $iterty as $iter ; into_iter => nan);
test_variance!($name: $iterty as $iter ; iter => nan);
};
( $name:ident: $iterty:ty as $iter:expr; into_iter => $expected:expr ) => {
paste! {
#[test]
fn [<$name _into_iter >]() {
let var = <$iterty>::variance($iter.into_iter());
assert_eq!(var, $expected);
}
}
};
( $name:ident: $iterty:ty as $iter:expr; iter => $expected:expr ) => {
paste! {
#[test]
fn [<$name _iter >]() {
let var = <&$iterty>::variance($iter.iter());
assert_eq!(var, $expected);
}
}
};
( $name:ident: $iterty:ty as $iter:expr => $expected:expr ) => {
test_variance!($name: $iterty as $iter ; into_iter => $expected);
test_variance!($name: $iterty as $iter ; iter => $expected);
};
}
test_variance!(f64: f64 as [1.0, 2.0, 3.0, 4.0] => 1.25);
test_variance!(f64_max: f64 as [f64::MAX, f64::MAX] => 0.0);
test_variance!(f64_min: f64 as [f64::MIN, f64::MIN] => 0.0);
test_variance!(f64_minmax: f64 as [f64::MAX, f64::MIN] => f64::NEG_INFINITY);
test_variance!(f64_nan: f64 as [1.0, 2.0, f64::NAN, 3.0, 4.0] => nan);
test_variance!(f64_inf: f64 as [1.0, 2.0, f64::INFINITY, 3.0, 4.0] => nan);
test_variance!(f64_neg_inf: f64 as [1.0, 2.0, f64::NEG_INFINITY, 3.0, 4.0] => nan);
test_variance!(f32: f32 as [1.0, 2.0, 3.0, 4.0] => 1.25);
test_variance!(f32_max: f32 as [f32::MAX, f32::MAX] => 0.0);
test_variance!(f32_min: f32 as [f32::MIN, f32::MIN] => 0.0);
test_variance!(f32_minmax: f32 as [f32::MAX, f32::MIN] => f32::NEG_INFINITY);
test_variance!(f32_nan: f32 as [1.0, 2.0, f32::NAN, 3.0, 4.0] => nan);
test_variance!(f32_inf: f32 as [1.0, 2.0, f32::INFINITY, 3.0, 4.0] => nan);
test_variance!(f32_neg_inf: f32 as [1.0, 2.0, f32::NEG_INFINITY, 3.0, 4.0] => nan);
}