use crate::{
error::MathError,
rational::{PolyOverQ, Q},
traits::SetCoefficient,
utils::index::evaluate_index,
};
use probability::{
prelude::{Gaussian, Sample},
source,
};
use rand::Rng;
use std::fmt::Display;
impl PolyOverQ {
pub fn sample_gauss(
max_degree: impl TryInto<i64> + Display,
center: impl Into<Q>,
sigma: impl Into<f64>,
) -> Result<Self, MathError> {
let max_degree = evaluate_index(max_degree).unwrap();
let center = center.into();
let sigma: f64 = sigma.into();
let mut poly = PolyOverQ::default();
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);
for index in 0..=max_degree {
let mut sample = Q::from(sampler.sample(&mut source));
sample += ¢er;
unsafe { poly.set_coeff_unchecked(index, &sample) };
}
Ok(poly)
}
}
#[cfg(test)]
mod test_sample_gauss {
use crate::{
integer::Z,
rational::{PolyOverQ, Q},
};
#[test]
fn availability() {
let center_q = Q::ZERO;
let center_z = Z::ZERO;
let _ = PolyOverQ::sample_gauss(1u8, 16u8, 0f32);
let _ = PolyOverQ::sample_gauss(1u16, 16u16, 0f64);
let _ = PolyOverQ::sample_gauss(1u32, 16u32, 0f32);
let _ = PolyOverQ::sample_gauss(1u64, 16u64, 0f64);
let _ = PolyOverQ::sample_gauss(1i8, 16u8, 0f32);
let _ = PolyOverQ::sample_gauss(1i16, 16i16, 0f32);
let _ = PolyOverQ::sample_gauss(1i32, 16i32, 0f32);
let _ = PolyOverQ::sample_gauss(1i64, 16i64, 0f64);
let _ = PolyOverQ::sample_gauss(1u8, center_q, 0f32);
let _ = PolyOverQ::sample_gauss(1u8, center_z, 0f64);
}
#[test]
fn nr_coeffs() {
let degrees = [1, 3, 7, 15, 32, 120];
for degree in degrees {
let res = PolyOverQ::sample_gauss(degree, i64::MAX, 1).unwrap();
assert_eq!(
res.get_degree(),
degree,
"Could fail with negligible probability."
);
}
}
#[test]
fn invalid_sigma() {
assert!(PolyOverQ::sample_gauss(1, 0, 0).is_err());
assert!(PolyOverQ::sample_gauss(1, 0, -1).is_err());
}
#[test]
#[should_panic]
fn invalid_max_degree() {
let _ = PolyOverQ::sample_gauss(-1, 0, 1).unwrap();
}
}