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 { pub coeffs: Box<[u64]>,
15}
16
17
18impl Polynomial {
19 pub fn new(coeffs: Vec<u64>) -> Self { Self { coeffs: coeffs.into_boxed_slice() }
21 }
22
23 pub fn zeros(n: usize) -> Self { Self { coeffs: vec![0u64; n].into_boxed_slice() }
25 }
26
27 #[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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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( &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 { 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}