use crate::error::StatError;
#[cfg(feature = "rng")]
use crate::rng::CommonStatsRng;
pub mod continuous;
pub mod discrete;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Bound {
NegInfinity,
Finite(f64),
PosInfinity,
}
impl Bound {
pub fn as_f64(self) -> f64 {
match self {
Bound::NegInfinity => f64::NEG_INFINITY,
Bound::Finite(x) => x,
Bound::PosInfinity => f64::INFINITY,
}
}
}
pub trait Distribution {
fn support_min(&self) -> Bound;
fn support_max(&self) -> Bound;
fn mean(&self) -> Option<f64> { None }
fn variance(&self) -> Option<f64> { None }
fn std_dev(&self) -> Option<f64> { self.variance().map(f64::sqrt) }
fn skewness(&self) -> Option<f64> { None }
fn kurtosis(&self) -> Option<f64> { None }
fn entropy(&self) -> Option<f64> { None }
}
pub trait ContinuousDensity: Distribution {
fn density(&self, x: f64) -> f64;
fn log_density(&self, x: f64) -> f64;
}
pub trait DiscreteMass: Distribution {
fn mass(&self, k: i64) -> f64;
fn log_mass(&self, k: i64) -> f64;
}
pub trait ContinuousCdf: Distribution {
fn cdf(&self, x: f64) -> f64;
fn sf(&self, x: f64) -> f64 { 1.0_f64 - self.cdf(x) }
fn quantile(&self, p: f64) -> Result<f64, StatError>;
}
pub trait DiscreteCdf: Distribution {
fn cdf(&self, k: i64) -> f64;
fn sf(&self, k: i64) -> f64 { 1.0_f64 - self.cdf(k) }
fn quantile(&self, p: f64) -> Result<i64, StatError>;
}
#[cfg(all(feature = "dist", feature = "rng"))]
pub trait Sampler: ContinuousCdf {
fn sample(&self, rng: &mut CommonStatsRng) -> f64;
}
pub(crate) fn norm_quantile(p: f64) -> f64 {
-core::f64::consts::SQRT_2 * crate::special::erfc_inv(2.0 * p)
}
pub(crate) fn gamma_log_density(shape: f64, rate: f64, x: f64) -> f64 {
if x <= 0.0 {
return f64::NEG_INFINITY;
}
shape * rate.ln() + (shape - 1.0) * x.ln() - rate * x - crate::special::lgamma(shape)
}
pub use continuous::Normal;
pub use continuous::StudentT;
pub use continuous::ChiSquared;
pub use continuous::FisherF;
pub use continuous::Uniform;
pub use continuous::Exponential;
pub use continuous::Cauchy;
pub use continuous::Weibull;
pub use continuous::LogNormal;
pub use continuous::Gamma;
pub use continuous::Beta;
pub use discrete::Bernoulli;
pub use discrete::Binomial;
pub use discrete::Poisson;
pub use discrete::Geometric;
pub use discrete::NegBinomial;
pub use discrete::Hypergeometric;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn norm_quantile_matches_known_points() {
assert!(norm_quantile(0.5).abs() < 1e-12);
assert!((norm_quantile(0.975) - 1.959_963_984_540_054).abs() < 1e-9);
assert!((norm_quantile(0.025) + 1.959_963_984_540_054).abs() < 1e-9);
}
#[test]
fn bound_is_copy_and_eq() {
let b = Bound::Finite(1.0);
let c = b; assert_eq!(b, c);
assert_eq!(Bound::NegInfinity, Bound::NegInfinity);
}
#[test]
fn gamma_log_density_normalizes() {
assert!((gamma_log_density(1.0, 1.0, 2.0) + 2.0).abs() < 1e-12);
}
}