use rand::Rng;
pub(crate) fn log_normal(median: f64, sigma: f64, rng: &mut impl Rng) -> f64 {
let (u1, u2): (f64, f64) = (rng.gen_range(f64::EPSILON..1.0), rng.gen());
let z = (-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos();
median * (sigma * z).exp()
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[test]
fn log_normal_median_matches_parameter() {
let mut rng = StdRng::seed_from_u64(1);
let mut v: Vec<f64> = (0..4001)
.map(|_| log_normal(200.0, 0.5, &mut rng))
.collect();
v.sort_by(f64::total_cmp);
let median = v[2000];
assert!((185.0..=215.0).contains(&median), "{median}");
assert!(v.iter().all(|&x| x > 0.0));
}
}