pub fn wblinv(p: f64, a: f64, b: f64) -> f64 {
if p.is_nan() || a.is_nan() || b.is_nan() || a <= 0.0 || b <= 0.0 {
return f64::NAN;
}
if p < 0.0 || p > 1.0 {
return f64::NAN;
}
if p == 0.0 {
return 0.0;
}
if p == 1.0 {
return f64::INFINITY;
}
let q = -(-p).log1p();
a * q.powf(1.0 / b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_standard_weibull_inverse() {
let res = wblinv(0.5, 1.0, 1.0);
let expected = -0.5f64.ln();
assert!((res - expected).abs() < 1e-15);
}
#[test]
fn test_boundary_probabilities() {
assert_eq!(wblinv(0.0, 2.5, 1.5), 0.0);
assert_eq!(wblinv(1.0, 2.5, 1.5), f64::INFINITY);
}
#[test]
fn test_invalid_probabilities() {
assert!(wblinv(-0.01, 1.0, 1.0).is_nan());
assert!(wblinv(1.01, 1.0, 1.0).is_nan());
assert!(wblinv(f64::NAN, 1.0, 1.0).is_nan());
}
#[test]
fn test_invalid_parameters() {
assert!(wblinv(0.5, 0.0, 1.0).is_nan());
assert!(wblinv(0.5, 1.0, -2.0).is_nan());
assert!(wblinv(0.5, -1.0, 1.0).is_nan());
}
#[test]
fn test_round_trip() {
let p_initial = 0.75;
let a = 1200.0;
let b = 2.5;
let x = wblinv(p_initial, a, b);
let z = (x / a).powf(b);
let p_recovered = -(-z).exp_m1();
assert!((p_initial - p_recovered).abs() < 1e-15);
}
}