probability-rs 0.1.2

Dependency-free probability distributions; clear APIs, deterministic sampling.
Documentation
use crate::dist::{Continuous, DistError, Distribution, Moments};
use crate::rng::RngCore;

#[derive(Debug, Clone, Copy)]
pub struct Exponential {
    lambda: f64,
}

impl Exponential {
    pub fn new(lambda: f64) -> Result<Self, DistError> {
        if !(lambda > 0.0 && lambda.is_finite()) {
            return Err(DistError::InvalidParameter);
        }
        Ok(Self { lambda })
    }
    #[inline]
    pub fn lambda(&self) -> f64 {
        self.lambda
    }
}

impl Distribution for Exponential {
    type Value = f64;
    fn cdf(&self, x: f64) -> f64 {
        if x <= 0.0 {
            0.0
        } else {
            1.0 - (-self.lambda * x).exp()
        }
    }
    fn in_support(&self, x: f64) -> bool {
        x >= 0.0 && x.is_finite()
    }
    fn sample<R: RngCore>(&self, rng: &mut R) -> f64 {
        let u = rng.next_f64();
        -u.ln() / self.lambda
    }
}

impl Continuous for Exponential {
    fn pdf(&self, x: f64) -> f64 {
        if self.in_support(x) {
            self.lambda * (-self.lambda * x).exp()
        } else {
            0.0
        }
    }
    fn inv_cdf(&self, p: f64) -> f64 {
        debug_assert!(p > 0.0 && p < 1.0);
        -(1.0 - p).ln() / self.lambda
    }
}

impl Moments for Exponential {
    fn mean(&self) -> f64 {
        1.0 / self.lambda
    }
    fn variance(&self) -> f64 {
        1.0 / (self.lambda * self.lambda)
    }
    fn skewness(&self) -> f64 { 2.0 }
    fn kurtosis(&self) -> f64 { 6.0 }
    fn entropy(&self) -> f64 { 1.0 - self.lambda.ln() }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn basic_values() {
        let e = Exponential::new(2.0).unwrap();
        assert!((e.mean() - 0.5).abs() < 1e-15);
        assert!((e.variance() - 0.25).abs() < 1e-15);
        assert!((e.cdf(0.0) - 0.0).abs() < 1e-15);
        assert!((e.pdf(0.0) - 2.0).abs() < 1e-12);
        assert!((e.skewness() - 2.0).abs() < 1e-15);
        assert!((e.kurtosis() - 6.0).abs() < 1e-15);
    }
}