use strafe_type::{FloatConstraint, Rational64, Real64};
use crate::{
distribution::{
func::{bd0, stirlerr},
norm::dnorm,
},
traits::DPQ,
};
pub fn dt<RE: Into<Real64>, RA: Into<Rational64>>(x: RE, n: RA, log: bool) -> Real64 {
let x = x.into().unwrap();
let n = n.into().unwrap();
if !x.is_finite() {
return f64::d_0(log).into();
}
if !n.is_finite() {
return dnorm(x, 0.0, 1.0, log);
}
let mut u = 0.0;
let t = -bd0(n / 2.0, (n + 1.0) / 2.0).unwrap() + stirlerr((n + 1.0) / 2.0) - stirlerr(n / 2.0);
let x2n = x * x / n;
let mut ax = 0.0;
let mut l_x2n = 0.0;
let lrg_x2n = x2n > 1.0 / 2.2204460492503131e-16;
if lrg_x2n {
ax = x.abs(); l_x2n = ax.ln() - n.ln() / 2.0;
u = n * l_x2n
} else if x2n > 0.2 {
l_x2n = (1.0 + x2n).ln() / 2.0;
u = n * l_x2n
} else {
l_x2n = x2n.ln_1p() / 2.0;
u = -bd0(n / 2.0, (n + x * x) / 2.0).unwrap() + x * x / 2.0
}
if log {
return (t - u - (strafe_consts::LN_SQRT_2TPI + l_x2n)).into();
}
let I_sqrt_ = if lrg_x2n {
(n.sqrt()) / ax
} else {
(-l_x2n).exp()
};
return ((t - u).exp() * strafe_consts::_1DSQRT_2TPI * I_sqrt_).into();
}