use strafe_type::{FloatConstraint, PositiveInteger64, Probability64, Real64};
use crate::{
distribution::func::{bd0, stirlerr},
traits::DPQ,
};
pub fn dbinom_raw(x: f64, n: f64, p: f64, q: f64, log: bool) -> f64 {
let mut lf = 0.0;
let mut lc = 0.0;
if p == 0.0 {
return if x == 0.0 {
f64::d_1(log)
} else {
f64::d_0(log)
};
}
if q == 0.0 {
return if x == n { f64::d_1(log) } else { f64::d_0(log) };
}
if x == 0.0 {
if n == 0.0 {
return f64::d_1(log);
}
lc = if p < 0.1 {
-bd0(n, n * q).unwrap() - n * p
} else {
n * q.ln()
};
return lc.d_exp(log);
}
if x == n {
lc = if q < 0.1 {
-bd0(n, n * p).unwrap() - n * q
} else {
n * p.ln()
};
return lc.d_exp(log);
}
if x < 0.0 || x > n {
return f64::d_0(log);
}
lc = stirlerr(n)
- stirlerr(x)
- stirlerr(n - x)
- bd0(x, n * p).unwrap()
- bd0(n - x, n * q).unwrap();
lf = strafe_consts::LN_2TPI + x.ln() + (-x / n).ln_1p();
return (lc - 0.5 * lf).d_exp(log);
}
pub fn dbinom<R: Into<Real64>, PO: Into<PositiveInteger64>, PR: Into<Probability64>>(
x: R,
n: PO,
p: PR,
log: bool,
) -> Real64 {
let mut x = x.into().unwrap();
let mut n = n.into().unwrap();
let p = p.into().unwrap();
if x.is_non_integer() {
warn!("non-integer x = {}", x);
return f64::d_0(log).into();
}
if x < 0.0 || !x.is_finite() {
return f64::d_0(log).into();
}
n = n.round();
x = x.round();
dbinom_raw(x, n, p, 1.0 - p, log).into()
}