use crate::stirlerr::stirlerr;
use crate::binodeviance::binodeviance;
use crate::gammaln;
fn f(z: f64, a: f64) -> f64 {
if a <= f64::MIN_POSITIVE * z {
return f64::exp(-z);
}
if z < f64::MIN_POSITIVE * a {
return f64::exp(a * f64::ln(z) -z - gammaln(a + 1.0));
}
let lnsr2pi = 0.9189385332046727; f64::exp(-lnsr2pi - 0.5 * f64::ln(a) - stirlerr(a) - binodeviance(a,z))
}
pub fn gampdf(x: f64, a: f64, b: f64) -> f64 {
if a < 0.0 || b <= 0.0 || a.is_nan() || b.is_nan() || x.is_nan() {
return f64::NAN;
}
let z = x / b;
if z == 0.0 && a == 1.0 && b > 0.0 {
return 1.0 / b;
}
if z == 0.0 && a < 1.0 && b > 0.0 {
return f64::INFINITY;
}
if z > 0.0 && z < f64::INFINITY && a > 0.0 && b > 0.0 {
let a = a - 1.0;
match a < 0.0 {
true => f(z, a + 1.0) * f64::exp(f64::ln(a + 1.0) - f64::ln(z)) / b,
false => f(z, a) / b,
}
} else {
0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gampdf_invalid_params() {
assert!(gampdf(1.0, -1.0, 1.0).is_nan());
assert!(gampdf(1.0, 1.0, 0.0).is_nan());
assert!(gampdf(f64::NAN, 1.0, 1.0).is_nan());
}
#[test]
fn test_gampdf_exponential_case() {
let b = 2.0;
let x = 1.0;
let expected = (1.0 / b) * f64::exp(-x / b);
assert!((gampdf(x, 1.0, b) - expected).abs() < 1e-15);
assert!((gampdf(0.0, 1.0, b) - 1.0/b).abs() < 1e-15);
}
#[test]
fn test_gampdf_poles_at_zero() {
assert_eq!(gampdf(0.0, 0.5, 1.0), f64::INFINITY);
assert_eq!(gampdf(0.0, 2.0, 1.0), 0.0);
}
#[test]
fn test_gampdf_known_values() {
let tol = 1e-14;
assert!((gampdf(2.0, 3.0, 1.0) - 0.2706705664732254).abs() < tol);
assert!((gampdf(1.0, 0.5, 2.0) - 0.2419707245191433).abs() < tol);
}
#[test]
fn test_gampdf_large_params() {
assert!(gampdf(100.0, 100.0, 1.0).is_finite());
assert!(gampdf(1000.0, 500.0, 2.0).is_finite());
}
}