use qfall_math::{
integer::{MatPolyOverZ, MatZ, PolyOverZ, Z},
rational::Q,
traits::MatrixSetEntry,
};
use serde::{Deserialize, Serialize};
#[typetag::serde]
pub trait TrapdoorDistribution {
fn sample(&self, m_bar: &Z, w: &Z) -> MatZ;
}
#[typetag::serde]
pub trait TrapdoorDistributionRing {
fn sample(&self, n: &Z, nr_cols: &Z, s: &Q) -> MatPolyOverZ;
}
#[derive(Serialize, Deserialize)]
pub struct PlusMinusOneZero;
#[derive(Serialize, Deserialize)]
pub struct SampleZ;
#[typetag::serde]
impl TrapdoorDistribution for PlusMinusOneZero {
fn sample(&self, m_bar: &Z, w: &Z) -> MatZ {
let mat_1 = MatZ::sample_uniform(m_bar, w, 0, 2).unwrap();
let mat_2 = MatZ::sample_uniform(m_bar, w, 0, 2).unwrap();
mat_1 - mat_2
}
}
#[typetag::serde]
impl TrapdoorDistributionRing for SampleZ {
fn sample(&self, n: &Z, nr_cols: &Z, s: &Q) -> MatPolyOverZ {
let n = i64::try_from(n).unwrap();
let nr_cols = i64::try_from(nr_cols).unwrap();
let mut out_mat = MatPolyOverZ::new(1, nr_cols);
for j in 0..nr_cols {
let sample = PolyOverZ::sample_discrete_gauss(n - 1, 0, s).unwrap();
out_mat.set_entry(0, j, &sample).unwrap();
}
out_mat
}
}
#[cfg(test)]
mod test_pm_one_zero {
use super::PlusMinusOneZero;
use super::TrapdoorDistribution;
use qfall_math::integer::Z;
use qfall_math::traits::*;
#[test]
fn correct_range() {
let sample = PlusMinusOneZero.sample(&10.into(), &5.into());
for i in 0..10 {
for j in 0..5 {
assert!(
Z::MINUS_ONE <= sample.get_entry(i, j).unwrap()
&& Z::ONE >= sample.get_entry(i, j).unwrap()
);
}
}
}
}
#[cfg(test)]
mod test_sample_z {
use super::{SampleZ, TrapdoorDistributionRing};
use qfall_math::traits::*;
#[test]
fn correct_range_high_prob() {
for _ in 0..20 {
let s = 5.into();
let sample = SampleZ.sample(&10.into(), &15.into(), &s);
let coeff_embedding = sample.transpose().into_coefficient_embedding(10);
assert!(
coeff_embedding.norm_eucl_sqrd().unwrap()
<= s.pow(2).unwrap() * coeff_embedding.get_num_rows()
);
}
}
}