use super::super::{Cdf, Moments, Pdf, Quantile, Sample};
use crate::distributions::UniformDistribution;
use crate::rng::SplitMix64;
impl UniformDistribution {
fn width(&self) -> f64 {
self.upper_bound - self.lower_bound
}
}
impl Pdf for UniformDistribution {
fn pdf(&self, x: f64) -> f64 {
if x < self.lower_bound || x > self.upper_bound {
0.0
} else {
1.0 / self.width()
}
}
}
impl Cdf for UniformDistribution {
fn cdf(&self, x: f64) -> f64 {
if x <= self.lower_bound {
0.0
} else if x >= self.upper_bound {
1.0
} else {
(x - self.lower_bound) / self.width()
}
}
}
impl Quantile for UniformDistribution {
fn quantile(&self, p: f64) -> f64 {
self.width().mul_add(p, self.lower_bound)
}
}
impl Moments for UniformDistribution {
fn mean(&self) -> Option<f64> {
Some(0.5 * (self.lower_bound + self.upper_bound))
}
fn variance(&self) -> Option<f64> {
let w = self.width();
Some(w * w / 12.0)
}
}
impl Sample for UniformDistribution {
fn sample(&self, rng: &mut SplitMix64) -> f64 {
self.width().mul_add(rng.next_f64(), self.lower_bound)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn density_is_reciprocal_width_inside() {
let d = UniformDistribution {
lower_bound: 0.0,
upper_bound: 4.0,
..Default::default()
};
assert!((d.pdf(2.0) - 0.25).abs() < 1e-12);
assert!(d.pdf(-1.0).abs() < 1e-12, "outside support must be 0");
}
#[test]
fn moments_are_closed_form() {
let d = UniformDistribution {
lower_bound: -2.0,
upper_bound: 3.0,
..Default::default()
};
assert_eq!(d.mean(), Some(0.5));
assert!(matches!(d.variance(), Some(v) if (v - 25.0 / 12.0).abs() < 1e-12));
}
}