1use crate::modular::mod_reduce;
7use crate::params::Params;
8use crate::poly::Poly;
9use rand::Rng;
10use rand_distr::{Distribution, Normal};
11
12pub fn sample_gaussian<R: Rng>(params: &Params, rng: &mut R) -> Poly {
15 let sigma = params.sigma_x1000 as f64 / 1000.0;
16 let normal = Normal::new(0.0, sigma).unwrap();
17 let coeffs = (0..params.n)
18 .map(|_| {
19 let sample = normal.sample(rng).round() as i64;
20 mod_reduce(sample, params.q)
21 })
22 .collect();
23 Poly { coeffs }
24}
25
26pub fn sample_uniform<R: Rng>(params: &Params, rng: &mut R) -> Poly {
28 let coeffs = (0..params.n).map(|_| rng.gen_range(0..params.q)).collect();
29 Poly { coeffs }
30}
31
32pub fn sample_ternary<R: Rng>(params: &Params, rng: &mut R) -> Poly {
35 let coeffs = (0..params.n)
36 .map(|_| {
37 let v: i64 = rng.gen_range(-1..=1);
38 mod_reduce(v, params.q)
39 })
40 .collect();
41 Poly { coeffs }
42}
43
44pub fn sample_binary<R: Rng>(params: &Params, rng: &mut R) -> Poly {
46 let coeffs = (0..params.n)
47 .map(|_| rng.gen_range(0..=1u64))
48 .collect();
49 Poly { coeffs }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use rand::SeedableRng;
56 use rand::rngs::StdRng;
57
58 #[test]
59 fn test_gaussian_in_range() {
60 let p = Params::test_tiny();
61 let mut rng = StdRng::seed_from_u64(42);
62 let poly = sample_gaussian(&p, &mut rng);
63 for &c in &poly.coeffs {
64 assert!(c < p.q);
65 }
66 assert_eq!(poly.coeffs.len(), p.n);
67 }
68
69 #[test]
70 fn test_uniform_in_range() {
71 let p = Params::test_tiny();
72 let mut rng = StdRng::seed_from_u64(42);
73 let poly = sample_uniform(&p, &mut rng);
74 for &c in &poly.coeffs {
75 assert!(c < p.q);
76 }
77 }
78
79 #[test]
80 fn test_ternary_values() {
81 let p = Params::test_tiny();
82 let mut rng = StdRng::seed_from_u64(42);
83 let poly = sample_ternary(&p, &mut rng);
84 for &c in &poly.coeffs {
85 assert!(c == 0 || c == 1 || c == p.q - 1, "got {c}");
87 }
88 }
89
90 #[test]
91 fn test_gaussian_centered() {
92 let p = Params::test_small();
94 let mut rng = StdRng::seed_from_u64(123);
95 let poly = sample_gaussian(&p, &mut rng);
96 let centered: Vec<i64> = poly
97 .coeffs
98 .iter()
99 .map(|&c| crate::modular::center(c, p.q))
100 .collect();
101 let mean: f64 = centered.iter().map(|&x| x as f64).sum::<f64>() / p.n as f64;
102 assert!(
103 mean.abs() < 2.0,
104 "mean {mean} too far from 0 for Gaussian"
105 );
106 }
107}