use crate::{
error::MathError,
integer::Z,
macros::seeded::seedable_function,
rational::Q,
utils::sample::discrete_gauss::{DiscreteGaussianIntegerSampler, LookupTableSetting, TAILCUT},
};
impl Z {
seedable_function!(
#[seeded]
#[optionally_seeded]
#[optionally_seeded]
#[unseeded]
#[seeded]
pub(crate) fn sample_discrete_gauss(
center: impl Into<Q>,
s: impl Into<Q>,
seed: Option<[u8; 32]>,
) -> Result<Self, MathError> {
let center: Q = center.into();
let s: Q = s.into();
let mut dgis = DiscreteGaussianIntegerSampler::init(
¢er,
&s,
unsafe { TAILCUT },
LookupTableSetting::NoLookup,
seed,
)?;
Ok(dgis.sample_z())
}
);
}
#[cfg(test)]
mod test_sample_discrete_gauss {
use crate::utils::sample::test_seed;
use crate::{integer::Z, rational::Q};
#[test]
fn availability() {
for seeded in [false, true] {
let z = Z::from(2);
let q = Q::from(2);
let _ = Z::sample_discrete_gauss_optionally_seeded(7u8, 1u16, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7u16, 1u8, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7u32, 1u32, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7u64, 1u64, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7i8, 1i64, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7i16, 1i32, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7i32, 1i16, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(7i64, 1i8, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(&q, 1i64, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(0i8, &z, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(&z, &q, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(1f32, 1f64, seeded.then(test_seed));
let _ = Z::sample_discrete_gauss_optionally_seeded(1f64, 1f32, seeded.then(test_seed));
if seeded {
let _ = Z::sample_discrete_gauss_seeded(7u8, 1u16, test_seed());
} else {
let _ = Z::sample_discrete_gauss(7u8, 1u16);
}
}
}
#[test]
fn distribution() {
for seeded in [false, true] {
let mut counts = [0; 20];
for _ in 0..200 {
let sample =
Z::sample_discrete_gauss_optionally_seeded(10, 2, seeded.then(test_seed))
.unwrap();
let sample_int = i64::try_from(&sample).unwrap() as usize;
counts[sample_int] += 1;
}
let expl_text = String::from("This test can fail with probability close to 0.
It fails if the sampled occurrences do not look like a typical discrete Gaussian random distribution.
If this happens, rerun the tests several times and check whether this issue comes up again.");
assert!(counts[10] > 70, "{expl_text}");
assert!(counts[10] < 130, "{expl_text}");
assert!(counts[9] > 20, "{expl_text}");
assert!(counts[9] < 70, "{expl_text}");
assert!(counts[11] > 20, "{expl_text}");
assert!(counts[11] < 70, "{expl_text}");
assert!(counts[8] < 20, "{expl_text}");
assert!(counts[12] < 20, "{expl_text}");
for count in counts.iter().take(8) {
assert!(count < &10, "{expl_text}");
}
for count in counts.iter().skip(13) {
assert!(count < &10, "{expl_text}");
}
}
}
#[test]
fn same_seed_same_sample() {
use crate::integer::Z;
let sample_0 = Z::sample_discrete_gauss_seeded(0, 1, [42; 32]).unwrap();
let sample_1 = Z::sample_discrete_gauss_seeded(0, 1, [42; 32]).unwrap();
assert_eq!(sample_0, sample_1);
}
#[test]
fn known_answer() {
use crate::integer::Z;
let sample = Z::sample_discrete_gauss_seeded(0, 1, [42; 32]).unwrap();
assert_eq!("1", sample.to_string());
}
}