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