use num_traits::Float;
use strafe_type::{FloatConstraint, Positive64, Rational64, Real64};
use crate::{
distribution::{beta::dnbeta, chisq::dnchisq, gamma::dgamma},
traits::DPQ,
};
pub fn dnf<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>, P: Into<Positive64>>(
x: RE,
df1: RA1,
df2: RA2,
ncp: P,
log: bool,
) -> Real64 {
let x = x.into().unwrap();
let df1 = df1.into().unwrap();
let df2 = df2.into().unwrap();
let ncp = ncp.into().unwrap();
let mut y = 0.0;
let mut z = 0.0;
let mut f = 0.0;
if x < 0.0 {
return f64::d_0(log).into();
}
if !ncp.is_finite() {
return f64::nan().into();
}
if !df1.is_finite() && !df2.is_finite() {
return if x == 1.0 {
f64::infinity()
} else {
f64::d_0(log)
}
.into();
}
if !df2.is_finite() {
return (df1 * dnchisq(x * df1, df1, ncp, log).unwrap()).into();
}
if df1 > 1e14 && ncp < 1e7 {
f = 1.0 + ncp / df1;
z = dgamma(1.0 / x / f, df2 / 2.0, 2.0 / df2, log).unwrap();
return if log {
(z - 2.0 * x.ln()) - f.ln()
} else {
(z / (x * x)) / f
}
.into();
}
y = df1 / df2 * x;
z = dnbeta(y / (1.0 + y), df1 / 2.0, df2 / 2.0, ncp, log).unwrap();
if log {
(z + df1.ln() - df2.ln()) - 2.0 * y.ln_1p()
} else {
(z * (df1 / df2) / (1.0 + y)) / (1.0 + y)
}
.into()
}