use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Probability64, Rational64, Real64};
use crate::{
distribution::{
beta::{log_qbeta, qbeta},
chisq::{log_qchisq, qchisq},
},
traits::DPQ,
};
pub fn qf<P: Into<Probability64>, R1: Into<Rational64>, R2: Into<Rational64>>(
p: P,
df1: R1,
df2: R2,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qf_inner(p, df1, df2, lower_tail, false)
}
pub fn log_qf<LP: Into<LogProbability64>, R1: Into<Rational64>, R2: Into<Rational64>>(
p: LP,
df1: R1,
df2: R2,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qf_inner(p, df1, df2, lower_tail, true)
}
fn qf_inner<R1: Into<Rational64>, R2: Into<Rational64>>(
mut p: f64,
df1: R1,
df2: R2,
lower_tail: bool,
log: bool,
) -> Real64 {
let df1 = df1.into().unwrap();
let df2 = df2.into().unwrap();
if let Some(ret) = p.q_p01_boundaries(0.0, f64::infinity(), lower_tail, log) {
return ret.into();
}
if df1 <= df2 && df2 > 4e5 {
if !df1.is_finite() {
return 1.0.into();
}
return if log {
(log_qchisq(p, df1, lower_tail).unwrap() / df1).into()
} else {
(qchisq(p, df1, lower_tail).unwrap() / df1).into()
};
}
if df1 > 4e5 {
return if log {
(df2 / log_qchisq(p, df2, !lower_tail).unwrap()).into()
} else {
(df2 / qchisq(p, df2, !lower_tail).unwrap()).into()
};
}
p =
(1.0 / if log {
log_qbeta(p, df2 / 2.0, df1 / 2.0, !lower_tail).unwrap()
} else {
qbeta(p, df2 / 2.0, df1 / 2.0, !lower_tail).unwrap()
} - 1.0)
* (df2 / df1);
if !p.is_nan() { p } else { f64::nan() }.into()
}