use num_traits::Float;
use strafe_type::{
FloatConstraint, LogProbability64, Positive64, Probability64, Rational64, Real64,
};
use crate::{
distribution::{
beta::pnbeta2,
chisq::{log_pnchisq, pnchisq},
},
traits::DPQ,
};
pub fn pnf<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>, P: Into<Positive64>>(
x: RE,
df1: RA1,
df2: RA2,
ncp: P,
lower_tail: bool,
) -> Probability64 {
pnf_inner(x, df1, df2, ncp, lower_tail, false).into()
}
pub fn log_pnf<
RE: Into<Real64>,
RA1: Into<Rational64>,
RA2: Into<Rational64>,
P: Into<Positive64>,
>(
x: RE,
df1: RA1,
df2: RA2,
ncp: P,
lower_tail: bool,
) -> LogProbability64 {
pnf_inner(x, df1, df2, ncp, lower_tail, true).into()
}
fn pnf_inner<
RE: Into<Real64>,
RA1: Into<Rational64>,
RA2: Into<Rational64>,
P: Into<Positive64>,
>(
x: RE,
df1: RA1,
df2: RA2,
ncp: P,
lower_tail: bool,
log: bool,
) -> f64 {
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;
if !ncp.is_finite() {
return f64::nan();
}
if !df1.is_finite() && !df2.is_finite() {
return f64::nan();
}
if let Some(ret) = x.p_bounds_01(0.0, f64::infinity(), lower_tail, log) {
return ret;
}
if df2 > 1e8 {
return if log {
log_pnchisq(x * df1, df1, ncp, lower_tail).unwrap()
} else {
pnchisq(x * df1, df1, ncp, lower_tail).unwrap()
};
}
y = df1 / df2 * x;
pnbeta2(
y / (1.0 + y),
1.0 / (1.0 + y),
df1 / 2.0,
df2 / 2.0,
ncp,
lower_tail,
log,
)
}