Skip to main content

numra_stats/distributions/
chi_squared.rs

1//! Chi-squared distribution.
2//!
3//! Author: Moussa Leblouba
4//! Date: 9 February 2026
5//! Modified: 2 May 2026
6
7use numra_core::Scalar;
8use rand::RngCore;
9
10use super::gamma_dist::GammaDist;
11use super::ContinuousDistribution;
12
13/// Chi-squared distribution with k degrees of freedom.
14///
15/// This is a special case of Gamma(k/2, 1/2).
16#[derive(Clone, Debug)]
17pub struct ChiSquared<S: Scalar> {
18    pub df: S,
19    gamma: GammaDist<S>,
20}
21
22impl<S: Scalar> ChiSquared<S> {
23    pub fn new(df: S) -> Self {
24        let half = S::HALF;
25        Self {
26            df,
27            gamma: GammaDist::new(df * half, half),
28        }
29    }
30}
31
32impl<S: Scalar> ContinuousDistribution<S> for ChiSquared<S> {
33    fn pdf(&self, x: S) -> S {
34        self.gamma.pdf(x)
35    }
36
37    fn cdf(&self, x: S) -> S {
38        self.gamma.cdf(x)
39    }
40
41    fn quantile(&self, p: S) -> S {
42        self.gamma.quantile(p)
43    }
44
45    fn mean(&self) -> S {
46        self.df
47    }
48
49    fn variance(&self) -> S {
50        self.df * S::TWO
51    }
52
53    fn sample(&self, rng: &mut dyn RngCore) -> S {
54        self.gamma.sample(rng)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_chi2_mean_variance() {
64        let c = ChiSquared::new(5.0_f64);
65        assert!((c.mean() - 5.0).abs() < 1e-14);
66        assert!((c.variance() - 10.0).abs() < 1e-14);
67    }
68
69    #[test]
70    fn test_chi2_cdf_at_zero() {
71        let c = ChiSquared::new(3.0_f64);
72        assert!(c.cdf(0.0).abs() < 1e-10);
73    }
74
75    #[test]
76    fn test_chi2_quantile_roundtrip() {
77        let c = ChiSquared::new(4.0_f64);
78        for &p in &[0.1, 0.5, 0.9] {
79            let x = c.quantile(p);
80            let p2 = c.cdf(x);
81            assert!((p - p2).abs() < 1e-5, "p={}, p2={}", p, p2);
82        }
83    }
84}