use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Probability64, Rational64, Real64};
use crate::{
distribution::{
norm::{log_qnorm, qnorm},
t::{log_qt, pnt, qt},
},
traits::DPQ,
};
pub fn qnt<P: Into<Probability64>, RA: Into<Rational64>, RE: Into<Real64>>(
p: P,
df: RA,
ncp: RE,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnt_inner(p, df, ncp, lower_tail, false)
}
pub fn log_qnt<LP: Into<LogProbability64>, RA: Into<Rational64>, RE: Into<Real64>>(
p: LP,
df: RA,
ncp: RE,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnt_inner(p, df, ncp, lower_tail, true)
}
fn qnt_inner<RA: Into<Rational64>, RE: Into<Real64>>(
mut p: f64,
df: RA,
ncp: RE,
lower_tail: bool,
log: bool,
) -> Real64 {
let df = df.into().unwrap();
let ncp = ncp.into().unwrap();
static accu: f64 = 1e-13;
static Eps: f64 = 1e-11;
let mut ux = 0.0;
let mut lx = 0.0;
let mut nx = 0.0;
let mut pp = 0.0;
if ncp == 0.0 && df >= 1.0 {
return if log {
log_qt(p, df, lower_tail)
} else {
qt(p, df, lower_tail)
};
}
if let Some(ret) = p.q_p01_boundaries(f64::NEG_INFINITY, f64::infinity(), lower_tail, log) {
return ret.into();
}
if !df.is_finite() {
return if log {
log_qnorm(p, ncp, 1.0, lower_tail)
} else {
qnorm(p, ncp, 1.0, lower_tail)
};
}
p = p.dt_qiv(lower_tail, log);
if p > 1.0 - 2.2204460492503131e-16 {
return f64::infinity().into();
}
pp = (p * (1.0 + Eps)).min(1.0 - 2.2204460492503131e-16);
ux = ncp.max(1.0);
while ux < f64::MAX && pnt(ux, df, ncp, true).unwrap() < pp {
ux *= 2.0
}
pp = p * (1.0 - Eps);
lx = (-ncp).min(-1.0);
while lx > -f64::MAX && pnt(lx, df, ncp, true).unwrap() > pp {
lx *= 2.0
}
loop {
nx = 0.5 * (lx + ux); if pnt(nx, df, ncp, true).unwrap() > p {
ux = nx
} else {
lx = nx
}
if !(ux - lx > accu * lx.abs().max(ux.abs())) {
break;
}
}
(0.5 * (lx + ux)).into()
}