use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Positive64, Probability64, Real64};
use super::{log_pnbinom_mu, pnbinom_mu};
use crate::{
distribution::{
func::discrete_body,
pois::{log_qpois, qpois},
},
traits::DPQ,
};
pub fn qnbinom_mu<PR: Into<Probability64>, PO1: Into<Positive64>, PO2: Into<Positive64>>(
p: PR,
size: PO1,
mu: PO2,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnbinom_mu_inner(p, size, mu, lower_tail, false)
}
pub fn log_qnbinom_mu<LP: Into<LogProbability64>, PO1: Into<Positive64>, PO2: Into<Positive64>>(
p: LP,
size: PO1,
mu: PO2,
lower_tail: bool,
) -> Real64 {
let p = p.into().unwrap();
qnbinom_mu_inner(p, size, mu, lower_tail, true)
}
pub fn qnbinom_mu_inner<PO1: Into<Positive64>, PO2: Into<Positive64>>(
p: f64,
size: PO1,
mu: PO2,
lower_tail: bool,
log: bool,
) -> Real64 {
let size = size.into().unwrap();
if size == f64::infinity() {
return if log {
log_qpois(p, mu, lower_tail)
} else {
qpois(p, mu, lower_tail)
};
}
let mu = mu.into().unwrap();
if mu == 0.0 || size == 0.0 {
return 0.0.into();
}
if let Some(ret) = p.q_p01_boundaries(0.0, f64::infinity(), lower_tail, log) {
return ret.into();
}
let Q = 1.0 + mu / size; let P = mu / size; let sigma = (size * P * Q).sqrt();
let gamma = (Q + P) / sigma;
let y = discrete_body(
mu,
sigma,
gamma,
p,
None,
lower_tail,
log,
&|p| pnbinom_mu(p, size, mu, lower_tail).unwrap(),
&|p| log_pnbinom_mu(p, size, mu, lower_tail).unwrap(),
);
y.into()
}