use crate::error::{StatsError, StatsResult};
use crate::prob::erf;
use crate::utils::constants::SQRT_2;
use num_traits::ToPrimitive;
#[inline]
pub fn cumulative_distrib<T>(x: T, avg: f64, stddev: f64) -> StatsResult<f64>
where
T: ToPrimitive,
{
let x_64 = x.to_f64().ok_or_else(|| StatsError::ConversionError {
message: "prob::cumulative_distrib: Failed to convert x to f64".to_string(),
})?;
if stddev == 0.0 {
return Err(StatsError::InvalidInput {
message: "prob::cumulative_distrib: Standard deviation must be non-zero".to_string(),
});
}
let z = (x_64 - avg) / (stddev * SQRT_2);
Ok((1.0 + erf(z)?) / 2.0)
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-5;
#[test]
fn test_cdf_standard_normal_at_mean() {
let x = 0.0;
let avg = 0.0;
let stddev = 1.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
let expected = 0.5;
assert!(
(result - expected).abs() < EPSILON,
"CDF at the mean should be 0.5"
);
}
#[test]
fn test_cdf_standard_normal_positive() {
let x = 1.0;
let avg = 0.0;
let stddev = 1.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
let expected = 0.841344746;
assert!(
(result - expected).abs() < EPSILON,
"CDF for z = 1.0 should match expected"
);
}
#[test]
fn test_cdf_standard_normal_negative() {
let x = -1.0;
let avg = 0.0;
let stddev = 1.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
let expected = 0.158655254;
assert!(
(result - expected).abs() < EPSILON,
"CDF for z = -1.0 should match expected"
);
}
#[test]
fn test_cdf_non_standard_distribution() {
let x = 12.0;
let avg = 10.0;
let stddev = 2.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
let expected = 0.841344746; assert!(
(result - expected).abs() < EPSILON,
"CDF for x = 12 in normal distribution with mean 10 and stddev 2 should match expected"
);
}
#[test]
fn test_cdf_large_positive_x() {
let x = 5.0;
let avg = 0.0;
let stddev = 1.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
let expected = 0.999999713; assert!(
(result - expected).abs() < EPSILON,
"CDF for x = 5.0 should be very close to 1"
);
}
#[test]
fn test_cdf_large_negative_x() {
let x = -5.0;
let avg = 0.0;
let stddev = 1.0;
let result = cumulative_distrib(x, avg, stddev).unwrap();
println!("resuilit large negatif : {:?}", result);
let expected = 0.000000287; assert!(
(result - expected).abs() < EPSILON,
"CDF for x = -5.0 should be very close to 0"
);
}
#[test]
fn test_cumulative_distrib_stddev_zero() {
let result = cumulative_distrib(0.0, 0.0, 0.0);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
StatsError::InvalidInput { .. }
));
}
}