use crate::{error::MathError, rational::Q};
use probability::{
distribution::{Gaussian, Sample},
source,
};
use rand::Rng;
impl Q {
pub fn sample_gauss(center: impl Into<Q>, sigma: impl Into<f64>) -> Result<Q, MathError> {
let center = center.into();
let sigma = sigma.into();
if sigma <= 0.0 {
return Err(MathError::NonPositive(format!(
"The sigma has to be positive and not zero, but the provided value is {sigma}."
)));
}
let mut rng = rand::rng();
let mut source = source::default(rng.next_u64());
let sampler = Gaussian::new(0.0, sigma);
let sample = center + Q::from(sampler.sample(&mut source));
Ok(sample)
}
}
#[cfg(test)]
mod test_sample_gauss {
use crate::rational::Q;
#[test]
fn in_concentration_bound() {
let range = 3;
for (mu, sigma) in [(i64::MAX, 1), (0, 20), (i64::MIN, 100)] {
assert!(range * sigma >= (Q::from(mu) - Q::sample_gauss(mu, sigma).unwrap()).abs())
}
}
#[test]
fn non_positive_sigma() {
for (mu, sigma) in [(0, 0), (0, -1)] {
assert!(Q::sample_gauss(mu, sigma).is_err())
}
}
}