use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Probability64, Rational64, Real64};
use crate::{
distribution::{
beta::{log_pbeta, pbeta},
chisq::{log_pchisq, pchisq},
},
traits::DPQ,
};
pub fn pf<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>>(
x: RE,
df1: RA1,
df2: RA2,
lower_tail: bool,
) -> Probability64 {
pf_inner(x, df1, df2, lower_tail, false).into()
}
pub fn log_pf<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>>(
x: RE,
df1: RA1,
df2: RA2,
lower_tail: bool,
) -> LogProbability64 {
pf_inner(x, df1, df2, lower_tail, true).into()
}
fn pf_inner<RE: Into<Real64>, RA1: Into<Rational64>, RA2: Into<Rational64>>(
x: RE,
df1: RA1,
df2: RA2,
lower_tail: bool,
log: bool,
) -> f64 {
let mut x = x.into().unwrap();
let df1 = df1.into().unwrap();
let df2 = df2.into().unwrap();
if let Some(ret) = x.p_bounds_01(0.0, f64::infinity(), lower_tail, log) {
return ret;
}
if df2 == f64::infinity() {
if df1 == f64::infinity() {
if x < 1.0 {
return f64::dt_0(lower_tail, log);
}
if x == 1.0 {
return if log { -std::f64::consts::LN_2 } else { 0.5 };
}
if x > 1.0 {
return f64::dt_1(lower_tail, log);
}
}
return if log {
log_pchisq(x * df1, df1, lower_tail).into()
} else {
pchisq(x * df1, df1, lower_tail).into()
};
}
if df1 == f64::infinity() {
return if log {
log_pchisq(df2 / x, df2, !lower_tail).into()
} else {
pchisq(df2 / x, df2, !lower_tail).into()
};
}
if df1 * x > df2 {
x = if log {
log_pbeta(df2 / (df2 + df1 * x), df2 / 2.0, df1 / 2.0, !lower_tail).into()
} else {
pbeta(df2 / (df2 + df1 * x), df2 / 2.0, df1 / 2.0, !lower_tail).into()
}
} else {
x = if log {
log_pbeta(df1 * x / (df2 + df1 * x), df1 / 2.0, df2 / 2.0, lower_tail).into()
} else {
pbeta(df1 * x / (df2 + df1 * x), df1 / 2.0, df2 / 2.0, lower_tail).into()
}
}
x
}