stats_claw/distributions/symmetric/
uniform.rs1use super::super::{Cdf, Moments, Pdf, Quantile, Sample};
23use crate::distributions::UniformDistribution;
24use crate::rng::SplitMix64;
25
26impl UniformDistribution {
27 fn width(&self) -> f64 {
29 self.upper_bound - self.lower_bound
30 }
31}
32
33impl Pdf for UniformDistribution {
34 fn pdf(&self, x: f64) -> f64 {
35 if x < self.lower_bound || x > self.upper_bound {
36 0.0
37 } else {
38 1.0 / self.width()
39 }
40 }
41}
42
43impl Cdf for UniformDistribution {
44 fn cdf(&self, x: f64) -> f64 {
45 if x <= self.lower_bound {
46 0.0
47 } else if x >= self.upper_bound {
48 1.0
49 } else {
50 (x - self.lower_bound) / self.width()
51 }
52 }
53}
54
55impl Quantile for UniformDistribution {
56 fn quantile(&self, p: f64) -> f64 {
57 self.width().mul_add(p, self.lower_bound)
58 }
59}
60
61impl Moments for UniformDistribution {
62 fn mean(&self) -> Option<f64> {
63 Some(0.5 * (self.lower_bound + self.upper_bound))
64 }
65 fn variance(&self) -> Option<f64> {
66 let w = self.width();
67 Some(w * w / 12.0)
68 }
69}
70
71impl Sample for UniformDistribution {
72 fn sample(&self, rng: &mut SplitMix64) -> f64 {
73 self.width().mul_add(rng.next_f64(), self.lower_bound)
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
83 fn density_is_reciprocal_width_inside() {
84 let d = UniformDistribution {
85 lower_bound: 0.0,
86 upper_bound: 4.0,
87 ..Default::default()
88 };
89 assert!((d.pdf(2.0) - 0.25).abs() < 1e-12);
90 assert!(d.pdf(-1.0).abs() < 1e-12, "outside support must be 0");
91 }
92
93 #[test]
95 fn moments_are_closed_form() {
96 let d = UniformDistribution {
97 lower_bound: -2.0,
98 upper_bound: 3.0,
99 ..Default::default()
100 };
101 assert_eq!(d.mean(), Some(0.5));
102 assert!(matches!(d.variance(), Some(v) if (v - 25.0 / 12.0).abs() < 1e-12));
103 }
104}