pub fn gevcdf(x: f64, k: f64, sigma: f64, mu: f64, upper: bool) -> f64 {
if x.is_nan() || k.is_nan() || sigma.is_nan() || mu.is_nan() || sigma <= 0.0 {
return f64::NAN;
}
let z = (x - mu) / sigma;
if k.abs() < f64::EPSILON {
return if upper {
-(-(-z).exp()).exp_m1()
} else {
(-(-z).exp()).exp()
};
}
let t = z * k;
if t <= -1.0 {
return if upper {
if k >= 0.0 { 1.0 } else { 0.0 }
} else {
if k < 0.0 { 1.0 } else { 0.0 }
};
}
let log_argument = -(1.0 / k) * t.ln_1p();
let inner_exponent = log_argument.exp();
if upper {
(-inner_exponent).exp_m1().abs()
} else {
(-inner_exponent).exp()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gev_type1_limit() {
let res_lower = gevcdf(0.0, 0.0, 1.0, 0.0, false);
let expected_lower = (-1.0f64).exp();
assert!((res_lower - expected_lower).abs() < 1e-15);
let res_upper = gevcdf(0.0, 0.0, 1.0, 0.0, true);
let expected_upper = 1.0 - (-1.0f64).exp();
assert!((res_upper - expected_upper).abs() < 1e-15);
}
#[test]
fn test_gev_type2_frechet() {
let k = 0.5;
let sigma = 1.5;
let mu = 2.0;
assert_eq!(gevcdf(-2.0, k, sigma, mu, false), 0.0);
assert_eq!(gevcdf(-2.0, k, sigma, mu, true), 1.0);
let res = gevcdf(5.0, k, sigma, mu, false);
assert!(res > 0.0 && res < 1.0);
}
#[test]
fn test_gev_type3_weibull_mirror() {
let k = -0.5;
let sigma = 1.0;
let mu = 3.0;
assert_eq!(gevcdf(6.0, k, sigma, mu, false), 1.0);
assert_eq!(gevcdf(6.0, k, sigma, mu, true), 0.0);
}
#[test]
fn test_invalid_parameters() {
assert!(gevcdf(1.0, 0.1, 0.0, 0.0, false).is_nan());
assert!(gevcdf(1.0, 0.1, -1.0, 0.0, false).is_nan());
assert!(gevcdf(f64::NAN, 0.1, 1.0, 0.0, false).is_nan());
}
}