pub fn gevinv(p: f64, k: f64, sigma: f64, mu: f64) -> f64 {
if p.is_nan() || k.is_nan() || sigma.is_nan() || mu.is_nan() || sigma <= 0.0 {
return f64::NAN;
}
if p < 0.0 || p > 1.0 {
return f64::NAN;
}
if p == 0.0 {
return if k <= 0.0 {
f64::NEG_INFINITY
} else {
mu - sigma / k
};
}
if p == 1.0 {
return if k < 0.0 {
mu - sigma / k
} else {
f64::INFINITY
};
}
let log_p = -p.ln(); let z = if k.abs() < f64::EPSILON {
-log_p.ln()
} else {
let log_log_p = log_p.ln();
(-k * log_log_p).exp_m1() / k
};
mu + sigma * z
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gevcdf;
#[test]
fn test_gev_type1_inverse_limit() {
let p = (-1.0f64).exp();
let res = gevinv(p, 0.0, 1.0, 0.0);
assert!(res.abs() < 1e-15);
}
#[test]
fn test_gev_boundary_cases() {
assert_eq!(gevinv(0.0, 0.5, 1.5, 2.0), -1.0);
assert_eq!(gevinv(1.0, 0.5, 1.5, 2.0), f64::INFINITY);
assert_eq!(gevinv(0.0, -0.5, 1.0, 3.0), f64::NEG_INFINITY);
assert_eq!(gevinv(1.0, -0.5, 1.0, 3.0), 5.0);
}
#[test]
fn test_invalid_inputs() {
assert!(gevinv(-0.01, 0.1, 1.0, 0.0).is_nan());
assert!(gevinv(1.01, 0.1, 1.0, 0.0).is_nan());
assert!(gevinv(0.5, 0.1, -1.0, 0.0).is_nan());
}
#[test]
fn test_round_trip() {
let p_initial = 0.45;
let k = 0.25;
let sigma = 2.0;
let mu = 10.0;
let x = gevinv(p_initial, k, sigma, mu);
let p_recovered = gevcdf(x, k, sigma, mu, false);
assert!((p_initial - p_recovered).abs() < 1e-15);
}
}