use crate::error::{Error, Result};
use crate::random::randf;
pub fn randexpf(lambda: f32) -> Result<f32> {
if lambda <= 0.0 {
return Err(Error::Range(format!("randexpf({}) has invalid range", lambda)));
}
let u = loop {
let u = randf();
if u != 0.0 {
break u;
}
};
Ok(-u.ln() / lambda)
}
pub fn randexpf_pdf(x: f32, lambda: f32) -> Result<f32> {
if lambda <= 0.0 {
return Err(Error::Range(format!("randexpf_pdf({},{}) has invalid range", x, lambda)));
}
if x < 0.0 {
Ok(0.0)
} else {
Ok(lambda * (-lambda * x).exp())
}
}
pub fn randexpf_cdf(x: f32, lambda: f32) -> Result<f32> {
if lambda <= 0.0 {
return Err(Error::Range(format!("randexpf_cdf({},{}) has invalid range", x, lambda)));
}
if x < 0.0 {
Ok(0.0)
} else {
Ok(1.0 - (-lambda * x).exp())
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
use approx::assert_relative_eq;
#[test]
#[autotest_annotate(autotest_randexpf)]
fn test_randexpf() {
const NUM_TRIALS: usize = 100_000;
const TOL: f32 = 0.1;
let lambda: f32 = 2.3f32;
let mut m1: f32 = 0.0;
let mut m2: f32 = 0.0;
for _ in 0..NUM_TRIALS {
let x = randexpf(lambda).unwrap();
m1 += x;
m2 += x * x;
}
m1 /= NUM_TRIALS as f32;
m2 = (m2 / NUM_TRIALS as f32) - m1 * m1;
let m1_exp = 1.0 / lambda;
let m2_exp = 1.0 / (lambda * lambda);
println!("m1 = {:.6} (expected {:.6})", m1, m1_exp);
println!("m2 = {:.6} (expected {:.6})", m2, m2_exp);
assert_relative_eq!(m1, m1_exp, epsilon = TOL);
assert_relative_eq!(m2, m2_exp, epsilon = TOL);
}
}