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 { 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 { Self { coeffs: coeffs.into_boxed_slice() }
40 }
41
42 pub fn zeros(n: usize) -> Self { 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 #[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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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( &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 { 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 { 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}