use num_traits::Float;
use strafe_type::{FloatConstraint, PositiveInteger64, Real64};
use crate::{distribution::binom::dbinom_raw, traits::DPQ};
pub fn dhyper<
R: Into<Real64>,
P1: Into<PositiveInteger64>,
P2: Into<PositiveInteger64>,
P3: Into<PositiveInteger64>,
>(
x: R,
r: P1,
b: P2,
n: P3,
log: bool,
) -> Real64 {
let mut x = x.into().unwrap();
let mut r = r.into().unwrap();
let mut b = b.into().unwrap();
let mut n = n.into().unwrap();
let mut p = 0.0;
let mut q = 0.0;
let mut p1 = 0.0;
let mut p2 = 0.0;
let mut p3 = 0.0;
if n > r + b {
return f64::nan().into();
}
if x < 0.0 {
return f64::d_0(log).into();
}
if x.is_non_integer() {
warn!("non-integer x = {}", x);
return f64::d_0(log).into();
}
x = x.round();
r = r.round();
b = b.round();
n = n.round();
if n < x || r < x || n - x > b {
return f64::d_0(log).into();
}
if n == 0.0 {
return if x == 0.0 {
f64::d_1(log)
} else {
f64::d_0(log)
}
.into();
}
p = n / (r + b);
q = (r + b - n) / (r + b);
p1 = dbinom_raw(x, r, p, q, log);
p2 = dbinom_raw(n - x, b, p, q, log);
p3 = dbinom_raw(n, r + b, p, q, log);
if log { (p1 + p2) - p3 } else { (p1 * p2) / p3 }.into()
}