mod rng;
mod normal;
mod uniform;
mod exponential;
mod gamma_dist;
mod beta_dist;
mod chi_squared;
mod student_t;
mod bernoulli;
mod binomial;
mod poisson;
#[cfg(test)]
mod tests;
pub use rng::Rng;
pub use normal::Normal;
pub use uniform::Uniform;
pub use exponential::Exponential;
pub use gamma_dist::Gamma;
pub use beta_dist::Beta;
pub use chi_squared::ChiSquared;
pub use student_t::StudentT;
pub use bernoulli::Bernoulli;
pub use binomial::Binomial;
pub use poisson::Poisson;
use crate::traits::FloatScalar;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StatsError {
InvalidParameter,
}
impl core::fmt::Display for StatsError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
StatsError::InvalidParameter => {
write!(f, "distribution parameter out of valid range")
}
}
}
}
pub trait ContinuousDistribution<T> {
fn pdf(&self, x: T) -> T;
fn ln_pdf(&self, x: T) -> T;
fn cdf(&self, x: T) -> T;
fn quantile(&self, p: T) -> T;
fn mean(&self) -> T;
fn variance(&self) -> T;
}
pub trait DiscreteDistribution<T> {
fn pmf(&self, k: u64) -> T;
fn ln_pmf(&self, k: u64) -> T;
fn cdf(&self, k: u64) -> T;
fn quantile(&self, p: T) -> u64;
fn mean(&self) -> T;
fn variance(&self) -> T;
}
pub(crate) fn normal_quantile_standard<T: FloatScalar>(p: T) -> T {
let one = T::one();
let half = T::from(0.5).unwrap();
let two = T::from(2.0).unwrap();
let p_low = T::from(0.02425).unwrap();
let p_high = one - p_low;
let a1 = T::from(-3.969683028665376e+01).unwrap();
let a2 = T::from(2.209460984245205e+02).unwrap();
let a3 = T::from(-2.759285104469687e+02).unwrap();
let a4 = T::from(1.383577518672690e+02).unwrap();
let a5 = T::from(-3.066479806614716e+01).unwrap();
let a6 = T::from(2.506628277459239e+00).unwrap();
let b1 = T::from(-5.447609879822406e+01).unwrap();
let b2 = T::from(1.615858368580409e+02).unwrap();
let b3 = T::from(-1.556989798598866e+02).unwrap();
let b4 = T::from(6.680131188771972e+01).unwrap();
let b5 = T::from(-1.328068155288572e+01).unwrap();
let c1 = T::from(-7.784894002430293e-03).unwrap();
let c2 = T::from(-3.223964580411365e-01).unwrap();
let c3 = T::from(-2.400758277161838e+00).unwrap();
let c4 = T::from(-2.549732539343734e+00).unwrap();
let c5 = T::from(4.374664141464968e+00).unwrap();
let c6 = T::from(2.938163982698783e+00).unwrap();
let d1 = T::from(7.784695709041462e-03).unwrap();
let d2 = T::from(3.224671290700398e-01).unwrap();
let d3 = T::from(2.445134137142996e+00).unwrap();
let d4 = T::from(3.754408661907416e+00).unwrap();
if p < p_low {
let q = (-two * p.ln()).sqrt();
(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
/ ((((d1 * q + d2) * q + d3) * q + d4) * q + one)
} else if p <= p_high {
let q = p - half;
let r = q * q;
(((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q
/ (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + one)
} else {
let q = (-two * (one - p).ln()).sqrt();
-(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
/ ((((d1 * q + d2) * q + d3) * q + d4) * q + one)
}
}
pub(crate) fn discrete_quantile_search<T: FloatScalar>(
cdf_fn: impl Fn(u64) -> T,
p: T,
k0: u64,
) -> u64 {
let mut k = k0;
while cdf_fn(k) < p {
k = k.saturating_add(1);
}
while k > 0 && cdf_fn(k - 1) >= p {
k -= 1;
}
k
}
pub(crate) fn quantile_newton<T: FloatScalar>(
cdf_fn: impl Fn(T) -> T,
pdf_fn: impl Fn(T) -> T,
p: T,
x0: T,
mut lo: T,
mut hi: T,
) -> T {
if p <= T::zero() {
return lo;
}
if p >= T::one() {
return hi;
}
let two = T::one() + T::one();
let tol = T::epsilon() * T::from(1000.0).unwrap();
let mut x = x0.max(lo).min(hi);
for _ in 0..100 {
let f = cdf_fn(x) - p;
if f.abs() < tol {
return x;
}
if f < T::zero() {
lo = x;
} else {
hi = x;
}
let fprime = pdf_fn(x);
if fprime > T::epsilon() {
let x_new = x - f / fprime;
if x_new > lo && x_new < hi {
x = x_new;
} else {
x = (lo + hi) / two;
}
} else {
x = (lo + hi) / two;
}
}
x
}