numra_stats/distributions/
chi_squared.rs1use numra_core::Scalar;
8use rand::RngCore;
9
10use super::gamma_dist::GammaDist;
11use super::ContinuousDistribution;
12
13#[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}