Skip to main content

simple_ring/
sampling.rs

1//Code for sampling of polynomials, and generation of coefficients
2use crate::{Polynomial, RingParams};
3use rand::{Rng, RngCore, rngs::OsRng};
4use std::ops::Deref;
5
6#[derive(Clone, Debug)]
7pub struct Sample(pub Vec<i32>);
8
9impl Deref for Sample {
10    type Target = Vec<i32>;
11
12    fn deref(&self) -> &Self::Target {
13        &self.0
14    }
15}
16
17impl Sample {
18    pub fn to_poly(self, q:u64) -> Polynomial {
19        let coeffs = self.iter().map(|v| (*v).rem_euclid(q as i32) as u64).collect();
20        Polynomial::new(coeffs)
21    }
22}
23
24
25
26#[inline]
27pub fn generate_cbd_sample(n: usize, eta: usize) -> Sample { //Inter function to create Centered Binomial Distribution
28
29    /*
30    This is how it works (on the left, explanations and on the right, example) :
31    For each coefficient, we randomly choose bits for two parts, a and b.           | a = [0, 1, 1, 1]           b = [1, 0, 1, 1]    
32    Then, we sum this coefficients for each part.                                   | a = 0 + 1 + 1 + 1 = 3      b = 1 + 0 + 1 + 1 = 3
33    Finally, the value of our coefficient is equal to a - b                         | coeff = a - b = 3 - 3 = 0
34    */
35
36    let mut coeffs = vec![0i32; n]; // So first we create an empty vector which will contain the coefficients.
37
38    let bits_per_coeff = eta; //Then we choose the number of bits for each part (a and b) -> if bit_number == 4, a and be will be the sum of 4 bits, like in the example
39
40    let total_bits = 2 * coeffs.len() * bits_per_coeff; 
41
42    let total_bytes = (total_bits + 7) / 8; 
43
44    let mut rng_buf = vec![0u8; total_bytes];
45
46    OsRng.fill_bytes(&mut rng_buf); //We generate a single vector of u8s of which we will choose the bits.
47    let mut bit_index = 0;
48    for coeff in coeffs.iter_mut() {
49        let mut a = 0;
50        let mut b = 0;
51
52        for _ in 0..bits_per_coeff { //Then we choose the bits in the sequence of bytes generated before. (for example : generated = [00001111; 10101010; ...], then we pick the bits one by one as first bit = 0, second one = 0...)
53            let byte = rng_buf[bit_index / 8];
54            let bit = (byte >> (bit_index % 8)) & 1; 
55            a += bit as u64;
56            bit_index += 1;
57        }
58
59        for _ in 0..bits_per_coeff { //We continue picking
60            let byte = rng_buf[bit_index / 8];
61            let bit = (byte >> (bit_index % 8)) & 1;
62            b += bit as u64;
63            bit_index += 1;
64        }
65
66        *coeff = a as i32 - b as i32; //And we finally do a - b
67    }
68    
69    Sample(coeffs)
70}
71
72
73#[inline]
74pub fn generate_small_sample(params: &RingParams) -> Sample { //Intern function that generates the small-coeffs sample
75    let mut rng = OsRng; 
76    let mut coeffs: Vec<i32> = Vec::with_capacity(params.n);
77    
78    for _ in 0..params.n {
79        let small: i32 = rng.gen_range(-1..=1); //We generate coeffs in the alphabet A = {-1, 0, 1}
80        coeffs.push(small);
81    }
82    
83    Sample(coeffs)
84}
85
86#[inline]
87pub fn generate_uniform_polynomial(params: &RingParams) -> Polynomial { //Intern function for generating a polynomial with uniform distribution, in the ring
88    let mut rng = OsRng;
89    let mut coeffs = Vec::with_capacity(params.n);
90
91    for _ in 0..params.n {
92        let uniform: u64 = rng.gen_range(0..=params.q - 1); // We generate values that are in the ring 
93        coeffs.push(uniform);
94    }
95
96    Polynomial { coeffs: coeffs.into_boxed_slice() }
97}