use num_traits::Float;
use strafe_type::{FloatConstraint, Positive64, Real64};
use crate::{distribution::binom::dbinom_raw, func::lbeta, traits::DPQ};
pub fn dbeta<R: Into<Real64>, P1: Into<Positive64>, P2: Into<Positive64>>(
x: R,
a: P1,
b: P2,
log: bool,
) -> Real64 {
let x = x.into().unwrap();
let a = a.into().unwrap();
let b = b.into().unwrap();
if !(0.0..=1.0).contains(&x) {
return f64::d_0(log).into();
}
if a == 0.0 || b == 0.0 || !a.is_finite() || !b.is_finite() {
if a == 0.0 && b == 0.0 {
return if x == 0.0 || x == 1.0 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
if a == 0.0 || a / b == 0.0 {
return if x == 0.0 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
if b == 0.0 || b / a == 0.0 {
return if x == 1.0 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
return if x == 0.5 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
if x == 0.0 {
if a > 1.0 {
return f64::d_0(log).into();
}
if a < 1.0 {
return f64::infinity().into();
}
return b.d_val(log).into();
}
if x == 1.0 {
if b > 1.0 {
return f64::d_0(log).into();
}
if b < 1.0 {
return f64::infinity().into();
}
return a.d_val(log).into();
}
let mut lval = 0.0;
if a <= 2.0 || b <= 2.0 {
lval = (a - 1.0) * x.ln() + (b - 1.0) * (-x).ln_1p() - lbeta(a, b).unwrap()
} else {
lval = (a + b - 1.0).ln() + dbinom_raw(a - 1.0, a + b - 2.0, x, 1.0 - x, true)
}
return lval.d_exp(log).into();
}