use crate::{
error::MathError,
integer_mod_q::{Modulus, PolyOverZq},
macros::seeded::seedable_function,
rational::Q,
traits::SetCoefficient,
utils::{
index::evaluate_index,
sample::discrete_gauss::{DiscreteGaussianIntegerSampler, LookupTableSetting, TAILCUT},
},
};
use std::fmt::Display;
impl PolyOverZq {
seedable_function!(
#[seeded]
#[optionally_seeded]
#[optionally_seeded]
#[unseeded]
#[seeded]
pub(crate) fn sample_discrete_gauss(
max_degree: impl TryInto<i64> + Display,
modulus: impl Into<Modulus>,
center: impl Into<Q>,
s: impl Into<Q>,
seed: Option<[u8; 32]>,
) -> Result<Self, MathError> {
let max_degree = evaluate_index(max_degree).unwrap();
let modulus = modulus.into();
let center = center.into();
let s = s.into();
let mut poly = PolyOverZq::from(&modulus);
let mut dgis = DiscreteGaussianIntegerSampler::init(
¢er,
&s,
unsafe { TAILCUT },
LookupTableSetting::FillOnTheFly,
seed,
)?;
for index in 0..=max_degree {
let sample = dgis.sample_z();
unsafe { poly.set_coeff_unchecked(index, sample) };
}
Ok(poly)
}
);
}
#[cfg(test)]
mod test_sample_discrete_gauss {
use crate::utils::sample::test_seed;
use crate::{integer::Z, integer_mod_q::PolyOverZq, rational::Q, traits::GetCoefficient};
#[test]
fn availability() {
for seeded in [false, true] {
let center = Q::ZERO;
let s = Q::ONE;
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1u8,
17u8,
0f32,
1u8,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1u16,
17u16,
0f64,
1u16,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1u32,
17u32,
0f32,
1u32,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1u64,
17u64,
0f64,
1u64,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1i8,
17u8,
0f32,
1i8,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1i8,
17i8,
0f32,
1i16,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1i16,
17i16,
0f32,
1i32,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1i32,
17i32,
0f64,
1i64,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1i64,
17i64,
center,
s,
seeded.then(test_seed),
);
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
1u8,
17u8,
0f32,
1f64,
seeded.then(test_seed),
);
if seeded {
let _ = PolyOverZq::sample_discrete_gauss_seeded(1u8, 17u8, 0f32, 1u8, test_seed());
} else {
let _ = PolyOverZq::sample_discrete_gauss(1u8, 17u8, 0f32, 1u8);
}
}
}
#[test]
fn boundaries_kept_small() {
for seeded in [false, true] {
let modulus = 128;
for _ in 0..32 {
let poly = PolyOverZq::sample_discrete_gauss_optionally_seeded(
3,
modulus,
15,
1,
seeded.then(test_seed),
)
.unwrap();
for i in 0..3 {
let sample: Z = poly.get_coeff(i).unwrap();
assert!(Z::ZERO <= sample);
assert!(sample < modulus);
}
}
}
}
#[test]
fn boundaries_kept_large() {
for seeded in [false, true] {
let modulus = u64::MAX;
for _ in 0..256 {
let poly = PolyOverZq::sample_discrete_gauss_optionally_seeded(
3,
modulus,
1,
1,
seeded.then(test_seed),
)
.unwrap();
for i in 0..3 {
let sample: Z = poly.get_coeff(i).unwrap();
assert!(Z::ZERO <= sample);
assert!(sample < modulus);
}
}
}
}
#[test]
fn nr_coeffs() {
for seeded in [false, true] {
let degrees = [1, 3, 7, 15, 32, 120];
for degree in degrees {
let res = PolyOverZq::sample_discrete_gauss_optionally_seeded(
degree,
u64::MAX,
i64::MAX,
1,
seeded.then(test_seed),
)
.unwrap();
assert_eq!(
res.get_degree(),
degree,
"Could fail with negligible probability."
);
}
}
}
#[test]
fn invalid_max_degree() {
for seeded in [false, true] {
let result = std::panic::catch_unwind(|| {
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
-1,
17,
0,
1,
seeded.then(test_seed),
)
.unwrap();
});
assert!(result.is_err());
}
}
#[test]
fn invalid_modulus() {
for seeded in [false, true] {
let result = std::panic::catch_unwind(|| {
let _ = PolyOverZq::sample_discrete_gauss_optionally_seeded(
3,
1,
0,
1,
seeded.then(test_seed),
)
.unwrap();
});
assert!(result.is_err());
}
}
#[test]
fn same_seed_same_sample() {
use crate::integer_mod_q::PolyOverZq;
let sample_0 = PolyOverZq::sample_discrete_gauss_seeded(2, 17, 0, 1, [42; 32]).unwrap();
let sample_1 = PolyOverZq::sample_discrete_gauss_seeded(2, 17, 0, 1, [42; 32]).unwrap();
assert_eq!(sample_0, sample_1);
}
#[test]
fn known_answer() {
use crate::integer_mod_q::PolyOverZq;
let sample = PolyOverZq::sample_discrete_gauss_seeded(2, 17, 0, 1, [42; 32]).unwrap();
assert_eq!("1 1 mod 17", sample.to_string());
}
}