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