use crate::{
error::MathError,
integer::{PolyOverZ, Z},
macros::seeded::seedable_function,
traits::SetCoefficient,
utils::{index::evaluate_index, sample::uniform::UniformIntegerSampler},
};
use std::fmt::Display;
impl PolyOverZ {
seedable_function!(
#[unseeded]
#[seeded]
#[optionally_seeded]
#[optionally_seeded]
#[seeded]
#[optionally_seeded]
#[optionally_seeded]
#[unseeded]
#[seeded]
pub(crate) fn sample_uniform(
max_degree: impl TryInto<i64> + Display,
lower_bound: impl Into<Z>,
upper_bound: impl Into<Z>,
seed: Option<[u8; 32]>,
) -> Result<Self, MathError> {
let max_degree = evaluate_index(max_degree)?;
let lower_bound: Z = lower_bound.into();
let upper_bound: Z = upper_bound.into();
let interval_size = &upper_bound - &lower_bound;
let mut uis = UniformIntegerSampler::init(&interval_size, seed)?;
let mut poly_z = PolyOverZ::default();
for index in 0..=max_degree {
let sample = uis.sample();
unsafe { poly_z.set_coeff_unchecked(index, &lower_bound + sample) };
}
Ok(poly_z)
}
);
}
#[cfg(test)]
mod test_sample_uniform {
use crate::utils::sample::test_seed;
use crate::{
integer::{PolyOverZ, Z},
integer_mod_q::Modulus,
traits::GetCoefficient,
};
#[test]
fn boundaries_kept_small() {
for seeded in [false, true] {
let lower_bound = Z::from(17);
let upper_bound = Z::from(32);
let poly_z = PolyOverZ::sample_uniform_optionally_seeded(
32,
&lower_bound,
&upper_bound,
seeded.then(test_seed),
)
.unwrap();
for i in 0..32 {
let sample = poly_z.get_coeff(i).unwrap();
assert!(lower_bound <= sample);
assert!(sample < upper_bound);
}
}
}
#[test]
fn boundaries_kept_large() {
for seeded in [false, true] {
let lower_bound = Z::from(i64::MIN) - Z::from(u64::MAX);
let upper_bound = Z::from(i64::MIN);
let poly_z = PolyOverZ::sample_uniform_optionally_seeded(
256,
&lower_bound,
&upper_bound,
seeded.then(test_seed),
)
.unwrap();
for i in 0..256 {
let sample = poly_z.get_coeff(i).unwrap();
assert!(lower_bound <= sample);
assert!(sample < upper_bound);
}
}
}
#[test]
fn nr_coeffs() {
for seeded in [false, true] {
let degrees = [1, 3, 7, 15, 32, 120];
for degree in degrees {
let res = PolyOverZ::sample_uniform_optionally_seeded(
degree,
1,
15,
seeded.then(test_seed),
)
.unwrap();
assert_eq!(degree, res.get_degree());
}
}
}
#[test]
fn invalid_interval() {
for seeded in [false, true] {
let lb_0 = Z::from(i64::MIN);
let lb_1 = Z::from(i64::MIN);
let lb_2 = Z::ZERO;
let upper_bound = Z::from(i64::MIN);
let res_0 = PolyOverZ::sample_uniform_optionally_seeded(
1,
&lb_0,
&upper_bound,
seeded.then(test_seed),
);
let res_1 = PolyOverZ::sample_uniform_optionally_seeded(
1,
&lb_1,
&upper_bound,
seeded.then(test_seed),
);
let res_2 = PolyOverZ::sample_uniform_optionally_seeded(
1,
&lb_2,
&upper_bound,
seeded.then(test_seed),
);
assert!(res_0.is_err());
assert!(res_1.is_err());
assert!(res_2.is_err());
}
}
#[test]
fn invalid_max_degree() {
for seeded in [false, true] {
let lower_bound = Z::from(0);
let upper_bound = Z::from(15);
let res_0 = PolyOverZ::sample_uniform_optionally_seeded(
-1,
&lower_bound,
&upper_bound,
seeded.then(test_seed),
);
let res_1 = PolyOverZ::sample_uniform_optionally_seeded(
i64::MIN,
&lower_bound,
&upper_bound,
seeded.then(test_seed),
);
assert!(res_0.is_err());
assert!(res_1.is_err());
}
}
#[test]
fn availability() {
for seeded in [false, true] {
let modulus = Modulus::from(7);
let z = Z::from(7);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1u64,
0u16,
7u8,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1i64,
0u32,
7u16,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1u8,
0u64,
7u32,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1u16,
0i8,
7u64,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1u32,
0i16,
7i8,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1i32,
0i32,
7i16,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1i16,
0i64,
7i32,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1i8,
&Z::ZERO,
7i64,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(
1,
0u8,
&modulus,
seeded.then(test_seed),
);
let _ = PolyOverZ::sample_uniform_optionally_seeded(1, 0, &z, seeded.then(test_seed));
if seeded {
let _ = PolyOverZ::sample_uniform_seeded(1u64, 0u16, 7u8, test_seed());
} else {
let _ = PolyOverZ::sample_uniform(1u64, 0u16, 7u8);
}
}
}
#[test]
fn same_seed_same_sample() {
use crate::integer::PolyOverZ;
let sample_0 = PolyOverZ::sample_uniform_seeded(3, 17, 26, [42; 32]).unwrap();
let sample_1 = PolyOverZ::sample_uniform_seeded(3, 17, 26, [42; 32]).unwrap();
assert_eq!(sample_0, sample_1);
}
#[test]
fn known_answer() {
use crate::integer::PolyOverZ;
let sample = PolyOverZ::sample_uniform_seeded(3, 17, 26, [42; 32]).unwrap();
assert_eq!("4 19 20 17 19", sample.to_string());
}
}