use crate::gammainc;
pub fn poisscdf(x: f64, lambda: f64, upper: bool) -> f64 {
if x.is_nan() || lambda.is_nan() {
return f64::NAN;
}
let x_floor = x.floor();
if lambda < 0.0 || (x_floor == f64::INFINITY && lambda == f64::INFINITY) {
return f64::NAN;
}
if x_floor == f64::INFINITY && lambda > 0.0 && lambda.is_finite() {
return if upper { 0.0 } else { 1.0 };
}
if x_floor < 0.0 && lambda > 0.0 && lambda.is_finite() {
return if upper { 1.0 } else { 0.0 };
}
if x_floor < 0.0 || !lambda.is_finite() {
return 0.0;
}
let alpha = x_floor + 1.0;
if upper {
gammainc(lambda, alpha, true, false)
} else {
gammainc(lambda, alpha, false, false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_poisscdf_basic() {
let tol = 1e-15;
assert!((poisscdf(2.0, 3.0, false) - 0.42319008112684364).abs() < tol);
assert!((poisscdf(2.0, 3.0, true) - 0.5768099188731564).abs() < tol);
assert!((poisscdf(0.0, 1.0, false) - 0.36787944117144233).abs() < tol);
}
#[test]
fn test_poisscdf_lambda_zero() {
assert_eq!(poisscdf(0.0, 0.0, false), 1.0);
assert_eq!(poisscdf(-1.0, 0.0, false), 0.0);
assert_eq!(poisscdf(0.0, 0.0, true), 0.0);
}
#[test]
fn test_poisscdf_boundaries() {
assert_eq!(poisscdf(-1.0, 5.0, false), 0.0);
assert_eq!(poisscdf(-1.0, 5.0, true), 1.0);
assert_eq!(poisscdf(f64::INFINITY, 5.0, false), 1.0);
assert_eq!(poisscdf(f64::INFINITY, 5.0, true), 0.0);
assert_eq!(poisscdf(5.0, f64::INFINITY, false), 0.0);
assert_eq!(poisscdf(5.0, f64::INFINITY, true), 0.0);
}
#[test]
fn test_poisscdf_invalid() {
assert!(poisscdf(2.0, -1.0, false).is_nan());
assert!(poisscdf(f64::NAN, 3.0, false).is_nan());
assert!(poisscdf(2.0, f64::NAN, false).is_nan());
assert!(poisscdf(f64::INFINITY, f64::INFINITY, false).is_nan());
}
}