use std::ops::{Range, RangeInclusive};
use num::Zero;
use rand::distr::{
uniform::{SampleBorrow, SampleRange, SampleUniform, UniformSampler},
Distribution, StandardUniform, Uniform,
};
use crate::Polynomial;
pub struct PolynomialSampler<T, const N: usize>(Uniform<T>)
where
T: Zero + PartialOrd + Ord + SampleUniform;
impl<T, const N: usize> UniformSampler for PolynomialSampler<T, N>
where
T: Zero + PartialOrd + Ord + SampleUniform,
{
type X = Polynomial<T, N>;
fn new<B1, B2>(low: B1, high: B2) -> Result<Self, rand::distr::uniform::Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = match low.borrow().iter().min() {
Some(low) => low,
None => return Err(rand::distr::uniform::Error::EmptyRange),
};
let high = match high.borrow().iter().max() {
Some(high) => high,
None => return Err(rand::distr::uniform::Error::EmptyRange),
};
if high < low {
return Err(rand::distr::uniform::Error::EmptyRange);
}
Uniform::<T>::new(low, high).map(PolynomialSampler)
}
fn new_inclusive<B1, B2>(low: B1, high: B2) -> Result<Self, rand::distr::uniform::Error>
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
UniformSampler::new(low, high)
}
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
let coeffs = (0..N).map(|_| self.0.sample(rng)).collect();
Polynomial::new(coeffs)
}
}
impl<T, const N: usize> SampleUniform for Polynomial<T, N>
where
T: Zero + PartialOrd + Ord + SampleUniform,
{
type Sampler = PolynomialSampler<T, N>;
}
impl<T, const N: usize> Distribution<Polynomial<T, N>> for StandardUniform
where
T: Zero,
StandardUniform: Distribution<T>,
{
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Polynomial<T, N> {
let coeffs = (0..N).map(|_| rng.random()).collect();
Polynomial::new(coeffs)
}
}
#[derive(Clone, Debug)]
pub struct CoeffsRange<T> {
range: Range<T>,
}
impl<T> From<Range<T>> for CoeffsRange<T> {
fn from(range: Range<T>) -> Self {
CoeffsRange { range }
}
}
#[derive(Clone, Debug)]
pub struct CoeffsRangeInclusive<T> {
range: RangeInclusive<T>,
}
impl<T> From<RangeInclusive<T>> for CoeffsRangeInclusive<T> {
fn from(range: RangeInclusive<T>) -> Self {
CoeffsRangeInclusive { range }
}
}
macro_rules! impl_samplerange {
($P:ty, $C:ty) => {
impl<T, const N: usize> SampleRange<$P> for $C
where
T: Clone + PartialOrd + Zero + SampleUniform,
{
fn sample_single<R: rand::RngCore + ?Sized>(
self,
rng: &mut R,
) -> Result<Polynomial<T, N>, rand::distr::uniform::Error> {
let mut coeffs = Vec::with_capacity(N);
for _ in 0..N {
let c = match self.range.clone().sample_single(rng) {
Ok(c) => c,
Err(e) => return Err(e),
};
coeffs.push(c);
}
Ok(Polynomial::new(coeffs))
}
fn is_empty(&self) -> bool {
self.range.is_empty()
}
}
};
}
impl_samplerange!(Polynomial<T, N>, CoeffsRange<T>);
impl_samplerange!(Polynomial<T, N>, CoeffsRangeInclusive<T>);
#[cfg(test)]
mod tests {
use rand::Rng;
use super::*;
#[test]
fn test_random_polynomial() {
let rng = &mut rand::rng();
let p: Polynomial<i32, 4> = rng.random();
assert!(p.deg() <= 3);
}
#[cfg(feature = "zq")]
#[test]
fn test_random_polynomial_over_zq() {
use crate::zq::{ZqI32, ZqI64, ZqU32, ZqU64};
let rng = &mut rand::rng();
let p: Polynomial<ZqI32<7>, 4> = rng.random();
assert!(p.deg() <= 3);
let p: Polynomial<ZqI64<7>, 4> = rng.random();
assert!(p.deg() <= 3);
let p: Polynomial<ZqU32<7>, 4> = rng.random();
assert!(p.deg() <= 3);
let p: Polynomial<ZqU64<7>, 4> = rng.random();
assert!(p.deg() <= 3);
let p1 = Polynomial::<ZqI32<23>, 4>::new(vec![1, 2, 3, 4]);
let p2 = Polynomial::<ZqI32<23>, 4>::new(vec![5, 6, 7, 8]);
assert!(PolynomialSampler::new(p2.clone(), p1.clone()).is_err()); let s = PolynomialSampler::new(p1, p2).unwrap();
let p = s.sample(rng);
p.iter()
.for_each(|c| assert!(ZqI32::<23>::from(1) <= *c && *c < ZqI32::<23>::from(8)));
let r = CoeffsRange::from(ZqI32::<23>::from(-2)..ZqI32::<23>::from(3));
let p: Polynomial<ZqI32<23>, 4> = rng.random_range(r);
p.iter()
.for_each(|c| assert!(ZqI32::<23>::from(-2) <= *c && *c < ZqI32::<23>::from(3)));
let r = CoeffsRangeInclusive::from(ZqI32::<23>::from(-3)..=ZqI32::<23>::from(3));
let p: Polynomial<ZqI32<23>, 4> = rng.random_range(r);
p.iter()
.for_each(|c| assert!(ZqI32::<23>::from(-3) <= *c && *c < ZqI32::<23>::from(3)));
}
}