use crate::error::{StatsError, StatsResult};
use num_traits::ToPrimitive;
use std::fmt::Debug;
#[inline]
pub fn variance<T>(data: &[T]) -> StatsResult<f64>
where
T: ToPrimitive + Debug,
{
if data.is_empty() {
return Err(StatsError::empty_data(
"prob::variance: Cannot calculate variance of empty dataset",
));
}
let mut mean = 0.0;
let mut m2 = 0.0;
let mut n = 0.0;
for (i, x) in data.iter().enumerate() {
let value = x.to_f64().ok_or_else(|| {
StatsError::conversion_error(format!(
"prob::variance: Failed to convert value at index {} to f64",
i
))
})?;
n += 1.0;
let delta = value - mean;
mean += delta / n;
let delta2 = value - mean;
m2 += delta * delta2;
}
Ok(m2 / n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_variance_integers() {
let data = [1, 2, 3, 4, 5];
let variance = variance(&data).unwrap();
assert!(!variance.is_nan());
}
#[test]
fn test_variance_floats() {
let data = [1.0, 2.0, 3.0, 4.0, 5.0];
let variance = variance(&data).unwrap();
assert!(!variance.is_nan());
}
#[test]
fn test_variance_mixed_floats() {
let data = [1.0, 2.5, 3.0, 4.5, 5.0];
let variance = variance(&data).unwrap();
assert!(!variance.is_nan());
}
#[test]
fn test_variance_single_value() {
let data = [5];
let variance = variance(&data).unwrap();
assert_eq!(variance, 0.0);
}
#[test]
fn test_variance_empty_slice() {
let data: &[f64] = &[];
let variance = variance(data);
assert!(variance.is_err());
assert!(matches!(
variance.unwrap_err(),
StatsError::EmptyData { .. }
));
}
#[test]
fn test_variance_identical_values() {
let data = [2.0; 10];
let variance = variance(&data).unwrap();
assert_eq!(variance, 0.0);
}
}