use crate::erfcinv;
use std::f64::consts::SQRT_2;
pub fn norminv(p: f64, mu: f64, sigma: f64) -> f64 {
if p.is_nan() || mu.is_nan() || sigma.is_nan() || !(0.0..=1.0).contains(&p) || sigma <= 0.0 {
return f64::NAN;
}
if p == 0.0 {
return f64::NEG_INFINITY;
}
if p == 1.0 {
return f64::INFINITY;
}
let z = -SQRT_2 * erfcinv(2.0 * p);
mu + sigma * z
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_norminv_standard() {
let tol = 1e-14;
assert!((norminv(0.5, 0.0, 1.0) - 0.0).abs() < tol);
assert!((norminv(0.975, 0.0, 1.0) - 1.959963984540054).abs() < tol);
assert!((norminv(0.158655253931457, 0.0, 1.0) + 1.0).abs() < tol);
}
#[test]
fn test_norminv_boundaries_and_invalid() {
assert_eq!(norminv(0.0, 0.0, 1.0), f64::NEG_INFINITY);
assert_eq!(norminv(1.0, 0.0, 1.0), f64::INFINITY);
assert!(norminv(-0.1, 0.0, 1.0).is_nan());
assert!(norminv(1.1, 0.0, 1.0).is_nan());
assert!(norminv(0.5, 0.0, -1.0).is_nan());
assert!(norminv(0.5, 0.0, 0.0).is_nan());
}
}