use crate::erfcinv;
use std::f64::consts::SQRT_2;
pub fn logninv(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 0.0;
}
if p == 1.0 {
return f64::INFINITY;
}
let z = -SQRT_2 * erfcinv(2.0 * p);
(sigma * z + mu).exp()
}
#[cfg(test)]
mod tests {
use super::*;
const TOL: f64 = 1e-14;
#[test]
fn test_logninv_standard() {
assert!((logninv(0.5, 0.0, 1.0) - 1.0).abs() < TOL);
assert!((logninv(0.8413447460685429, 0.0, 1.0) - (1.0f64).exp()).abs() < TOL);
assert!((logninv(0.158655253931457, 0.0, 1.0) - (-1.0f64).exp()).abs() < TOL);
}
#[test]
fn test_logninv_varied_params() {
assert!((logninv(0.5, 1.0, 0.5) - (1.0f64).exp()).abs() < TOL);
let p_val = 0.9;
let mu_val = 2.0;
let sigma_val = 0.7;
assert!((logninv(p_val, mu_val, sigma_val) - 1.812126473436266e1).abs() < TOL);
}
#[test]
fn test_logninv_boundaries_and_invalid() {
assert_eq!(logninv(0.0, 0.0, 1.0), 0.0);
assert_eq!(logninv(1.0, 0.0, 1.0), f64::INFINITY);
assert!(logninv(-0.1, 0.0, 1.0).is_nan());
assert!(logninv(1.1, 0.0, 1.0).is_nan());
assert!(logninv(0.5, 0.0, -1.0).is_nan());
assert!(logninv(0.5, 0.0, 0.0).is_nan());
assert!(logninv(f64::NAN, 0.0, 1.0).is_nan());
assert!(logninv(0.5, f64::NAN, 1.0).is_nan());
assert!(logninv(0.5, 0.0, f64::NAN).is_nan());
}
}