use petir::mathf::RealMath;
pub fn erf_inv(y: f64) -> f64 {
const A: f64 = 0.147;
let pi = std::f64::consts::PI;
let k = 2.0 / (A * pi) + 0.5 * (1.0 - y * y).r_ln();
let h = (1.0 - y * y).r_ln() / A;
let x = (-k + (k * k - h).sqrt()).sqrt();
if y < 0.0 {
-x
} else {
x
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_values() {
assert!(erf_inv(0.0).abs() < 1e-10);
for &y_target in &[0.1_f64, 0.5, 0.8, 0.95, -0.3, -0.7] {
let pos = erf_inv(y_target.abs());
let neg = erf_inv(-y_target.abs());
assert!(
(pos + neg).abs() < 1e-14,
"erf_inv not odd at |y|={}",
y_target.abs()
);
assert!(pos > 0.0, "positive input gives positive output");
}
}
#[test]
fn monotone_and_positive() {
let pts = [0.1, 0.3, 0.5, 0.7, 0.9, 0.99];
let mut prev = erf_inv(pts[0]);
for &y in &pts[1..] {
let cur = erf_inv(y);
assert!(cur > prev, "erf_inv not monotone: {} < {}", cur, prev);
prev = cur;
}
}
}