use num_traits::Float;
use strafe_type::{
FloatConstraint, LogProbability64, Positive64, Probability64, Rational64, Real64,
};
use crate::{distribution::beta::pnbeta, traits::DPQ};
pub fn qnbeta<
PR: Into<Probability64>,
R1: Into<Rational64>,
R2: Into<Rational64>,
PO: Into<Positive64>,
>(
p: PR,
a: R1,
b: R2,
ncp: PO,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnbeta_inner(p, a, b, ncp, lower_tail, false)
}
pub fn log_qnbeta<
LP: Into<LogProbability64>,
R1: Into<Rational64>,
R2: Into<Rational64>,
P: Into<Positive64>,
>(
p: LP,
a: R1,
b: R2,
ncp: P,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnbeta_inner(p, a, b, ncp, lower_tail, true)
}
pub fn qnbeta_inner<R1: Into<Rational64>, R2: Into<Rational64>, P: Into<Positive64>>(
mut p: f64,
a: R1,
b: R2,
ncp: P,
lower_tail: bool,
log: bool,
) -> Real64 {
let a = a.into().unwrap();
let b = b.into().unwrap();
let ncp = ncp.into().unwrap();
static accu: f64 = 1e-15;
static Eps: f64 = 1e-14;
let mut ux = 0.0;
let mut lx = 0.0;
let mut nx = 0.0;
let mut pp = 0.0;
if !a.is_finite() {
return f64::nan().into();
}
if let Some(ret) = p.q_p01_boundaries(0.0, 1.0, lower_tail, log) {
return ret.into();
}
p = p.dt_qiv(lower_tail, log);
if p > 1.0 - 2.2204460492503131e-16f64 {
return 1.0.into();
}
pp = (p * (1.0 + Eps)).min(1.0 - 2.2204460492503131e-16f64);
ux = 0.5;
while ux < 1.0 - 2.2204460492503131e-16f64 && pnbeta(ux, a, b, ncp, true).unwrap() < pp {
ux = 0.5 * (1.0 + ux)
}
pp = p * (1.0 - Eps);
lx = 0.5;
while lx > 2.2250738585072014e-308 && pnbeta(lx, a, b, ncp, true).unwrap() > pp {
lx *= 0.5
}
loop
{
nx = 0.5 * (lx + ux);
if pnbeta(nx, a, b, ncp, true).unwrap() > p {
ux = nx
} else {
lx = nx
}
if !((ux - lx) / nx > accu) {
break;
}
}
return (0.5 * (ux + lx)).into();
}