use rand::Rng;
use rand_distr::{Distribution as _, Normal, StandardNormal};
#[allow(
clippy::cast_possible_truncation,
reason = "narrowing f64 -> f32 is the intended tail-precision convention"
)]
pub(crate) fn standard_normal<R: Rng + ?Sized>(rng: &mut R) -> f32 {
let x: f64 = StandardNormal.sample(rng);
x as f32
}
pub(crate) fn normal_or_mean<R: Rng + ?Sized>(mean: f32, std: f32, rng: &mut R) -> f32 {
match Normal::new(mean, std) {
Ok(d) => d.sample(rng),
Err(_) => mean,
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
use rand::rngs::StdRng;
#[test]
fn standard_normal_is_finite() {
let mut rng: StdRng = StdRng::seed_from_u64(7);
for _ in 0..1_000 {
let x: f32 = standard_normal(&mut rng);
assert!(x.is_finite(), "standard_normal produced non-finite {x}");
}
}
#[test]
fn standard_normal_same_seed_is_deterministic() {
let mut a: StdRng = StdRng::seed_from_u64(42);
let mut b: StdRng = StdRng::seed_from_u64(42);
for _ in 0..64 {
let xa: f32 = standard_normal(&mut a);
let xb: f32 = standard_normal(&mut b);
assert_eq!(xa.to_bits(), xb.to_bits());
}
}
#[test]
fn standard_normal_different_seeds_differ() {
let mut a: StdRng = StdRng::seed_from_u64(1);
let mut b: StdRng = StdRng::seed_from_u64(2);
let seq_a: Vec<u32> = (0..16).map(|_| standard_normal(&mut a).to_bits()).collect();
let seq_b: Vec<u32> = (0..16).map(|_| standard_normal(&mut b).to_bits()).collect();
assert_ne!(seq_a, seq_b);
}
#[test]
fn normal_or_mean_falls_back_on_nonfinite_std() {
let mean: f32 = 3.5;
for std in [f32::INFINITY, f32::NEG_INFINITY, f32::NAN] {
let mut rng: StdRng = StdRng::seed_from_u64(11);
let out: f32 = normal_or_mean(mean, std, &mut rng);
assert_eq!(
out.to_bits(),
mean.to_bits(),
"expected fallback to mean for std = {std}"
);
}
}
#[test]
fn normal_or_mean_zero_std_yields_exact_mean() {
let mean: f32 = -2.25;
let mut rng: StdRng = StdRng::seed_from_u64(17);
let out: f32 = normal_or_mean(mean, 0.0, &mut rng);
assert_eq!(out.to_bits(), mean.to_bits());
}
#[test]
fn normal_or_mean_samples_finite_for_finite_std() {
let mut rng: StdRng = StdRng::seed_from_u64(13);
for _ in 0..1_000 {
let x: f32 = normal_or_mean(0.0, 1.0, &mut rng);
let y: f32 = normal_or_mean(0.0, -1.0, &mut rng);
assert!(x.is_finite(), "normal_or_mean produced non-finite {x}");
assert!(y.is_finite(), "normal_or_mean produced non-finite {y}");
}
}
}