use crate::{
error::MathError,
integer::{PolyOverZ, Z},
rational::Q,
traits::SetCoefficient,
utils::{index::evaluate_index, sample::binomial::BinomialSampler},
};
use std::fmt::Display;
impl PolyOverZ {
pub fn sample_binomial(
max_degree: impl TryInto<i64> + Display,
n: impl Into<Z>,
p: impl Into<Q>,
) -> Result<Self, MathError> {
Self::sample_binomial_with_offset(max_degree, 0, n, p)
}
pub fn sample_binomial_with_offset(
max_degree: impl TryInto<i64> + Display,
offset: impl Into<Z>,
n: impl Into<Z>,
p: impl Into<Q>,
) -> Result<Self, MathError> {
let max_degree = evaluate_index(max_degree)?;
let offset: Z = offset.into();
let mut poly_z = PolyOverZ::default();
let mut bin_sampler = BinomialSampler::init(n, p)?;
for index in 0..=max_degree {
let mut sample = bin_sampler.sample();
sample += &offset;
unsafe { poly_z.set_coeff_unchecked(index, sample) };
}
Ok(poly_z)
}
}
#[cfg(test)]
mod test_sample_binomial {
use super::{PolyOverZ, Q, Z};
use crate::traits::GetCoefficient;
#[test]
fn boundaries_kept() {
for _ in 0..8 {
let poly = PolyOverZ::sample_binomial(0, 2, 0.5).unwrap();
let sample = poly.get_coeff(0).unwrap();
assert!(Z::ZERO <= sample);
assert!(sample <= 2);
}
}
#[test]
fn nr_coeffs() {
let degrees = [1, 3, 7, 15, 32, 120];
for degree in degrees {
let res = PolyOverZ::sample_binomial(degree, 256, 0.99999).unwrap();
assert_eq!(
degree,
res.get_degree(),
"This test can fail with probability close to 0."
);
}
}
#[test]
fn invalid_max_degree() {
let res_0 = PolyOverZ::sample_binomial(-1, 2, 0.5);
let res_1 = PolyOverZ::sample_binomial(i64::MIN, 2, 0.5);
assert!(res_0.is_err());
assert!(res_1.is_err());
}
#[test]
fn availability() {
let _ = PolyOverZ::sample_binomial(1, 1u16, 7u8);
let _ = PolyOverZ::sample_binomial(1, 1u32, 7u16);
let _ = PolyOverZ::sample_binomial(1, 1u64, 7u32);
let _ = PolyOverZ::sample_binomial(1, 1i8, 7u64);
let _ = PolyOverZ::sample_binomial(1, 1i16, 7i8);
let _ = PolyOverZ::sample_binomial(1, 1i32, 7i16);
let _ = PolyOverZ::sample_binomial(1, 1i64, 7i32);
let _ = PolyOverZ::sample_binomial(1, Z::ONE, 7i64);
let _ = PolyOverZ::sample_binomial(1, 1u8, 0.5f32);
let _ = PolyOverZ::sample_binomial(1, 1u8, 0.5f64);
let _ = PolyOverZ::sample_binomial(1, 1, Q::from((1, 2)));
}
}
#[cfg(test)]
mod test_sample_binomial_with_offset {
use super::{PolyOverZ, Q, Z};
use crate::traits::GetCoefficient;
#[test]
fn boundaries_kept() {
for _ in 0..8 {
let poly = PolyOverZ::sample_binomial_with_offset(0, -1, 2, 0.5).unwrap();
let sample = poly.get_coeff(0).unwrap();
assert!(Z::MINUS_ONE <= sample);
assert!(sample <= Z::ONE);
}
}
#[test]
fn nr_coeffs() {
let degrees = [1, 3, 7, 15, 32, 120];
for degree in degrees {
let res = PolyOverZ::sample_binomial_with_offset(degree, 1, 2, 0.5).unwrap();
assert_eq!(degree, res.get_degree());
}
}
#[test]
fn invalid_max_degree() {
let res_0 = PolyOverZ::sample_binomial_with_offset(-1, -1, 2, 0.5);
let res_1 = PolyOverZ::sample_binomial_with_offset(i64::MIN, -1, 2, 0.5);
assert!(res_0.is_err());
assert!(res_1.is_err());
}
#[test]
fn availability() {
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1u16, 7u8);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1u32, 7u16);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1u64, 7u32);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1i8, 7u64);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1i16, 7i8);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1i32, 7i16);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1i64, 7i32);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, Z::ONE, 7i64);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1u8, 0.5f32);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1u8, 0.5f64);
let _ = PolyOverZ::sample_binomial_with_offset(1, 0, 1, Q::from((1, 2)));
}
}