use crate::erfc;
use std::f64::consts::SQRT_2;
pub fn normcdf(x: f64, mu: f64, sigma: f64, upper: bool) -> f64 {
if x.is_nan() || mu.is_nan() || sigma.is_nan() || sigma < 0.0 {
return f64::NAN;
}
if sigma == 0.0 {
return if upper {
if x < mu { 1.0 } else { 0.0 }
} else {
if x < mu { 0.0 } else { 1.0 }
};
}
let z = (x - mu) / sigma;
if upper {
0.5 * erfc(z / SQRT_2)
} else {
0.5 * erfc(-z / SQRT_2)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normcdf_standard() {
let tol = 1e-14;
assert!((normcdf(0.0, 0.0, 1.0, false) - 0.5).abs() < tol);
assert!((normcdf(1.959963984540054, 0.0, 1.0, false) - 0.975).abs() < tol);
assert!((normcdf(-1.959963984540054, 0.0, 1.0, false) - 0.025).abs() < tol);
}
#[test]
fn test_normcdf_upper() {
let tol = 1e-14;
assert!((normcdf(0.0, 0.0, 1.0, true) - 0.5).abs() < tol);
assert!((normcdf(1.959963984540054, 0.0, 1.0, true) - 0.025).abs() < tol);
assert!((normcdf(-1.959963984540054, 0.0, 1.0, true) - 0.975).abs() < tol);
}
#[test]
fn test_normcdf_zero_sigma() {
assert_eq!(normcdf(5.0, 5.0, 0.0, false), 1.0);
assert_eq!(normcdf(4.99, 5.0, 0.0, false), 0.0);
assert_eq!(normcdf(4.99, 5.0, 0.0, true), 1.0);
}
#[test]
fn test_normcdf_invalid() {
assert!(normcdf(0.5, 0.0, -1.0, false).is_nan());
}
}