use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Positive64, Probability64, Real64};
use crate::{
distribution::{
beta::{log_pbeta, pbeta},
func::bratio,
pois::{log_ppois, ppois},
},
traits::DPQ,
};
pub fn pnbinom<R: Into<Real64>, PO: Into<Positive64>, PR: Into<Probability64>>(
x: R,
size: PO,
prob: PR,
lower_tail: bool,
) -> Probability64 {
pnbinom_inner(x, size, prob, lower_tail, false).into()
}
pub fn log_pnbinom<R: Into<Real64>, PO: Into<Positive64>, PR: Into<Probability64>>(
x: R,
size: PO,
prob: PR,
lower_tail: bool,
) -> LogProbability64 {
pnbinom_inner(x, size, prob, lower_tail, true).into()
}
fn pnbinom_inner<R: Into<Real64>, PO: Into<Positive64>, PR: Into<Probability64>>(
x: R,
size: PO,
prob: PR,
lower_tail: bool,
log: bool,
) -> f64 {
let mut x = x.into().unwrap();
let size = size.into().unwrap();
let prob = prob.into().unwrap();
if !size.is_finite() || !prob.is_finite() {
return f64::nan();
}
if prob <= 0.0 {
return f64::nan();
}
if size == 0.0 {
return if x >= 0.0 {
f64::dt_1(lower_tail, log)
} else {
f64::dt_0(lower_tail, log)
};
}
if x < 0.0 {
return f64::dt_0(lower_tail, log);
}
if !x.is_finite() {
return f64::dt_1(lower_tail, log);
}
x = (x + 1e-7).floor();
if log {
log_pbeta(prob, size, x + 1.0, lower_tail).unwrap()
} else {
pbeta(prob, size, x + 1.0, lower_tail).unwrap()
}
}
pub fn pnbinom_mu<R: Into<Real64>, PO1: Into<Positive64>, PO2: Into<Positive64>>(
x: R,
size: PO1,
mu: PO2,
lower_tail: bool,
) -> Probability64 {
pnbinom_mu_inner(x, size, mu, lower_tail, false).into()
}
pub fn log_pnbinom_mu<R: Into<Real64>, PO1: Into<Positive64>, PO2: Into<Positive64>>(
x: R,
size: PO1,
mu: PO2,
lower_tail: bool,
) -> LogProbability64 {
pnbinom_mu_inner(x, size, mu, lower_tail, true).into()
}
pub fn pnbinom_mu_inner<R: Into<Real64>, PO1: Into<Positive64>, PO2: Into<Positive64>>(
x: R,
size: PO1,
mu: PO2,
lower_tail: bool,
log: bool,
) -> f64 {
let mut x = x.into().unwrap();
let size = size.into().unwrap();
let mu = mu.into().unwrap();
if !mu.is_finite() {
return f64::nan();
}
if size == 0.0 {
return if x >= 0.0 {
f64::dt_1(lower_tail, log)
} else {
f64::dt_0(lower_tail, log)
};
}
if x < 0.0 {
return f64::dt_0(lower_tail, log);
}
if !x.is_finite() {
return f64::dt_1(lower_tail, log);
}
if !size.is_finite() {
return if log {
log_ppois(x, mu, lower_tail).unwrap()
} else {
ppois(x, mu, lower_tail).unwrap()
};
}
x = (x + 1e-7).floor();
let mut w = f64::nan();
let mut wc = f64::nan();
let mut ierr = 0;
bratio(
size,
x + 1.0,
size / (size + mu),
mu / (size + mu),
&mut w,
&mut wc,
&mut ierr,
log,
);
if ierr != 0 {
panic!("Error in bratio {}", ierr);
}
if lower_tail {
w
} else {
wc
}
}