use num_traits::Float;
use strafe_type::{FloatConstraint, Rational64, Real64};
use crate::{
distribution::{binom::dbinom_raw, gamma::dgamma},
traits::DPQ,
};
pub fn df<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>>(
x: RE,
m: RA1,
n: RA2,
log: bool,
) -> Real64 {
let x = x.into().unwrap();
let m = m.into().unwrap();
let n = n.into().unwrap();
let mut p = 0.0;
let mut q = 0.0;
let mut f = 0.0;
let mut dens = 0.0;
if x < 0.0 {
return f64::d_0(log).into();
}
if x == 0.0 {
return if m > 2.0 {
f64::d_0(log)
} else if m == 2.0 {
f64::d_1(log)
} else {
f64::infinity()
}
.into();
}
if !m.is_finite() && !n.is_finite() {
return if x == 1.0 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
if !n.is_finite() {
return dgamma(x, m / 2.0, 2.0 / m, log);
}
if m > 1e14 {
dens = dgamma(1.0 / x, n / 2.0, 2.0 / n, log).unwrap();
return if log {
dens - 2.0 * x.ln()
} else {
dens / (x * x)
}
.into();
}
f = 1.0 / (n + x * m);
q = n * f;
p = x * m * f;
if m >= 2.0 {
f = m * q / 2.0;
dens = dbinom_raw((m - 2.0) / 2.0, (m + n - 2.0) / 2.0, p, q, log)
} else {
f = m * m * q / (2.0 * p * (m + n));
dens = dbinom_raw(m / 2.0, (m + n) / 2.0, p, q, log)
}
if log { (f.ln()) + dens } else { f * dens }.into()
}