Skip to main content

simple_ring/
polys.rs

1
2#[cfg(feature = "parallel")]
3use rayon::prelude::*;          
4#[cfg(feature = "parallel")]
5use rayon::slice::ParallelSliceMut;
6use crate::RingParams;
7use crate::ntt::{NTTprecaculated, inverse_ntt, forward_ntt};
8
9
10
11
12#[derive(Debug, Clone)]
13pub struct Polynomial { //The polynomial struct, one of the bricks of the project.
14    pub coeffs: Box<[u64]>,    
15}
16
17
18impl Polynomial {
19    pub fn new(coeffs: Vec<u64>) -> Self { //Creates, from existing coefficients, a Polynomial.
20        Self { coeffs: coeffs.into_boxed_slice() }
21    }
22
23    pub fn zeros(n: usize) -> Self { //Creates an empty Polynomial.
24        Self { coeffs: vec![0u64; n].into_boxed_slice() }
25    }
26
27    //Code for calling single or parallel polynomial code
28
29
30    #[cfg(not(feature = "parallel"))]
31    pub fn sum(&self, params: &RingParams, polynomial: &Polynomial) -> Polynomial {
32        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to sum doesn't match ! You may use parameters.n for both of the polynomials");
33
34        Self::polynomial_sum_single( params,  self, polynomial)
35    }
36
37    #[cfg(not(feature = "parallel"))]   
38    pub fn sub(&self, params: &RingParams, polynomial: &Polynomial) -> Polynomial {
39        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to substract doesn't match ! You may use parameters.n for both of the polynomials");
40 
41        Self::polynomial_sub_single( params,  self, polynomial)
42    }
43
44    #[cfg(feature = "parallel")]
45    pub fn sum(&self, params: &RingParams, polynomial: &Polynomial) -> Polynomial {
46        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to sum doesn't match ! You may use parameters.n for both of the polynomials");
47
48        Self::polynomial_sum_multi( params,  self, polynomial)
49    }
50
51    #[cfg(feature = "parallel")]
52    pub fn sub(&self, params: &RingParams, polynomial: &Polynomial) -> Polynomial {
53        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to multiply doesn't match ! You may use parameters.n for both of the polynomials");
54        Self::polynomial_sub_multi( params,  self, polynomial)
55    }
56
57
58    #[inline]
59    pub fn mul(&self, params: &RingParams, polynomial: &Polynomial) -> Self { //Function for naive multiplication, not to be used
60        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to multiply doesn't match ! You may use parameters.n for both of the polynomials");
61
62        let mut b = vec![0u64; 2 * params.n];
63        
64        for i in 0..params.n {
65            for j in 0..params.n {
66                let k = i + j;
67                b[k] += self.coeffs[i]  * polynomial.coeffs[j] ;
68
69            }
70        }
71        
72        let mut coeffs = vec![0u64; params.n];
73        for k in 0..params.n {
74            let low = b[k];
75            let high = if k + params.n < b.len() { b[k + params.n] } else { 0 };
76            
77            let val = ((low as i128) - (high as i128)).rem_euclid(params.q as i128) as u64;
78            coeffs[k] = val;
79        }
80
81        Polynomial::new(coeffs)
82    }
83
84    #[inline]
85    fn polynomial_sum_single(params: &RingParams, first: &Self, second: &Self) -> Self { //The single-thread sum.
86        let mut coeffs = vec![0u64; params.n];
87        for i in 0..params.n {
88            coeffs[i] = ((first.coeffs[i] as u128 + second.coeffs[i] as u128) % params.q as u128) as u64;
89        }
90        Polynomial::new(coeffs)
91    }
92
93    #[inline]
94    fn polynomial_sub_single(params: &RingParams, first: &Self, second: &Self) -> Self { //The single-thread substraction.
95        let mut coeffs = vec![0u64; params.n];
96        for i in 0..params.n {
97            coeffs[i] = ((first.coeffs[i] as i128 - second.coeffs[i] as i128).rem_euclid(params.q as i128)) as u64 ;
98        }
99        Polynomial::new(coeffs)  
100    }
101
102    #[cfg(feature = "parallel")]
103    fn polynomial_sum_multi(params: &RingParams, first: &Self, second: &Self) -> Self { //The multi-thread sum (experimental and not so good...)
104        let mut coeffs = vec![0u64; params.n];
105        coeffs
106            .par_iter_mut()
107            .enumerate()
108            .for_each(|(i, coeff)| {
109                *coeff = ((first.coeffs[i] as u128 + second.coeffs[i] as u128) % params.q as u128) as u64;
110            });
111        Self { coeffs: coeffs.into_boxed_slice() }
112    }
113
114    #[cfg(feature = "parallel")]
115    fn polynomial_sub_multi(params: &RingParams, first: &Self, second: &Self) -> Self { //The multi-thread substraction (experimental and not so good...)
116        let mut coeffs = vec![0u64; params.n];
117        coeffs
118        .par_iter_mut()
119        .enumerate()
120        .for_each(|(i, coeff)| {
121            *coeff = (first.coeffs[i] as i128 - second.coeffs[i] as i128).rem_euclid(params.q as i128) as u64;
122        });
123
124        Self { coeffs: coeffs.into_boxed_slice() }
125        
126    }
127    
128
129    #[inline]
130    pub fn scale(&self, params: &RingParams, lambda: u64) -> Self { //Function to scale a polynomial by a constant.
131        let coeffs: Vec<u64> = self.coeffs
132            .iter()
133            .map(|&coeff| ((coeff as u128 * lambda as u128) % params.q as u128) as u64)
134            .collect::<Vec<u64>>();
135    
136        Polynomial::new(coeffs)
137    }
138
139    #[inline]
140    pub fn opposite(&self, params: &RingParams) -> Self { //Function that gives the opposite of the polynomial ( - polynomial )
141        let q = params.q;
142        let coeffs: Vec<u64> = self.coeffs
143            .iter()
144            .map(|&coeff| ( - (coeff as i128 )).rem_euclid(q as i128) as u64)
145            .collect::<Vec<u64>>();
146        Polynomial::new(coeffs)
147    }
148    
149    #[inline]
150    pub fn reduce(&self, constant: u128) -> Polynomial { //Function that reduce the polynomial by a constant (polynomial mod k)
151        let new = self.coeffs
152            .iter()
153            .map(|c| (*c as u128).rem_euclid(constant) as u64)
154            .collect();
155        Polynomial::new(new)
156    }
157
158    #[inline]
159    pub fn divide_by_constant( //Function that devide by a constant (polynomial / k)
160        &self,
161        constant: u128,
162    ) -> Polynomial {
163
164        let new = self.coeffs
165            .iter()
166            .map(|c| {
167                let x = (*c as u128 + constant / 2) / constant;
168                x as u64
169            })
170            .collect();
171
172        Polynomial::new(new)
173    }
174
175    #[inline]
176    pub fn mul_ntt(&self, params: &RingParams, ntt_tables: &NTTprecaculated, polynomial: &Polynomial) -> Polynomial { //Function that computes the product of two polynomials by passing them in NTTs.
177        assert_eq!(self.coeffs.len(), polynomial.coeffs.len(), "The length of the two polynomials you're trying to multiply doesn't match ! You may use parameters.n for both of the polynomials");
178        let a = forward_ntt(params, self, ntt_tables);
179        let b = forward_ntt(params, polynomial, ntt_tables);
180
181        let mut c_ntt = Polynomial::zeros(params.n);
182
183        for i in 0..params.n {
184            c_ntt.coeffs[i] = ((a.coeffs[i] as u128 * b.coeffs[i] as u128) % params.q as u128) as u64;
185        }
186
187        inverse_ntt(params, &c_ntt, ntt_tables)
188        
189    }
190
191}