use num_traits::Float;
use strafe_type::{
FloatConstraint, LogProbability64, Positive64, Probability64, Rational64, Real64,
};
use crate::{
distribution::{
beta::{log_qnbeta, qnbeta},
chisq::{log_qnchisq, qnchisq},
},
traits::DPQ,
};
pub fn qnf<
PR: Into<Probability64>,
R1: Into<Rational64>,
R2: Into<Rational64>,
PO: Into<Positive64>,
>(
p: PR,
df1: R1,
df2: R2,
ncp: PO,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnf_inner(p, df1, df2, ncp, lower_tail, false)
}
pub fn log_qnf<
LP: Into<LogProbability64>,
R1: Into<Rational64>,
R2: Into<Rational64>,
P: Into<Positive64>,
>(
p: LP,
df1: R1,
df2: R2,
ncp: P,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnf_inner(p, df1, df2, ncp, lower_tail, true)
}
fn qnf_inner<R1: Into<Rational64>, R2: Into<Rational64>, P: Into<Positive64>>(
p: f64,
df1: R1,
df2: R2,
ncp: P,
lower_tail: bool,
log: bool,
) -> Real64 {
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().into();
}
if !df1.is_finite() && !df2.is_finite() {
return f64::nan().into();
}
if let Some(ret) = p.q_p01_boundaries(0.0, f64::infinity(), lower_tail, log) {
return ret.into();
}
if df2 > 1e8 {
return if log {
log_qnchisq(p, df1, ncp, lower_tail).unwrap() / df1
} else {
qnchisq(p, df1, ncp, lower_tail).unwrap() / df1
}
.into();
}
if log {
y = log_qnbeta(p, df1 / 2.0, df2 / 2.0, ncp, lower_tail).unwrap();
} else {
y = qnbeta(p, df1 / 2.0, df2 / 2.0, ncp, lower_tail).unwrap();
}
(y / (1.0 - y) * (df2 / df1)).into()
}