ring_math/
polynomial_ring.rs1use std::fmt::Debug;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::ops::Add;
5use std::ops::AddAssign;
6use std::ops::Div;
7use std::ops::Mul;
8use std::ops::MulAssign;
9use std::ops::Neg;
10use std::ops::Sub;
11use std::ops::SubAssign;
12use std::str::FromStr;
13
14use scalarff::BigUint;
15use scalarff::FieldElement;
16
17use super::polynomial::Polynomial;
18use super::Matrix2D;
19use super::Vector;
20
21pub trait PolynomialRingElement:
27 FieldElement
28 + Add<Output = Self>
29 + AddAssign
30 + Div<Output = Self>
31 + Mul<Output = Self>
32 + MulAssign
33 + Neg<Output = Self>
34 + Sub<Output = Self>
35 + SubAssign
36 + FromStr
37 + PartialEq
38 + Clone
39 + Hash
40 + Debug
41 + From<u64>
42 + From<Polynomial<Self::F>>
43 + Display
44{
45 type F: FieldElement;
46
47 fn modulus() -> Polynomial<Self::F>;
55
56 fn polynomial(&self) -> &Polynomial<Self::F>;
59
60 fn from_polynomial(p: Polynomial<Self::F>) -> Self;
63
64 fn to_scalar(&self) -> anyhow::Result<Self::F> {
67 if self.polynomial().degree() == 0 {
68 Ok(self.polynomial().coefficients[0].clone())
69 } else {
70 anyhow::bail!("Cannot convert polynomial of degree > 0 to scalar")
71 }
72 }
73
74 fn norm_l1(&self) -> BigUint {
77 self.coef().norm_l1()
78 }
79
80 fn norm_l2(&self) -> BigUint {
86 self.coef().norm_l2()
87 }
88
89 fn norm_max(&self) -> BigUint {
92 self.coef().norm_max()
93 }
94
95 fn coef(&self) -> Vector<Self::F> {
98 let modulus = Self::modulus();
99 let target_degree = modulus.degree();
100 let poly_coefs = self.polynomial().coef_vec().to_vec();
101 let poly_coefs_len = poly_coefs.len();
102 Vector::from_vec(
103 [
104 poly_coefs,
105 vec![Self::F::zero(); target_degree - poly_coefs_len],
106 ]
107 .concat(),
108 )
109 }
110
111 fn rot(&self) -> Matrix2D<Self::F> {
116 let modulus = Self::modulus();
117 let degree = modulus.degree();
118 let mut values = vec![Self::F::zero(); degree * degree];
119 for i in 0..degree {
127 let mut coefs = self.coef().to_vec();
128 coefs.rotate_right(i);
129 for j in 0..i {
130 coefs[j] = -coefs[j].clone();
131 }
132 for j in 0..degree {
133 values[j * degree + i] = coefs[j].clone();
134 }
135 }
136 Matrix2D {
137 dimensions: (degree, degree),
138 values,
139 }
140 }
141}
142
143#[macro_export]
164macro_rules! polynomial_ring {
165 ( $name: ident, $field_element: ident, $modulus: expr, $name_str: expr ) => {
166 #[derive(Clone, Debug, PartialEq, Eq, std::hash::Hash)]
167 pub struct $name(pub Polynomial<$field_element>);
168
169 impl PolynomialRingElement for $name {
170 type F = $field_element;
171
172 fn modulus() -> Polynomial<Self::F> {
173 $modulus
174 }
175
176 fn polynomial(&self) -> &Polynomial<Self::F> {
177 &self.0
178 }
179
180 fn from_polynomial(p: Polynomial<Self::F>) -> Self {
181 $name(p)
182 }
183 }
184
185 impl From<Polynomial<$field_element>> for $name {
186 fn from(p: Polynomial<$field_element>) -> Self {
187 $name(p.div(&Self::modulus()).1)
188 }
189 }
190
191 impl FieldElement for $name {
192 fn zero() -> Self {
193 $name(Polynomial {
194 coefficients: vec![$field_element::zero()],
195 })
196 }
197
198 fn one() -> Self {
199 $name(Polynomial::identity())
200 }
201
202 fn byte_len() -> usize {
203 Self::modulus().degree() * $field_element::byte_len()
204 }
205
206 fn prime() -> scalarff::BigUint {
207 panic!("cannot retrieve a scalar prime for a polynomial field");
208 }
209
210 fn name_str() -> &'static str {
211 $name_str
212 }
213
214 fn from_usize(value: usize) -> Self {
217 $name(Polynomial {
218 coefficients: vec![$field_element::from_usize(value)],
219 })
220 }
221
222 fn to_biguint(&self) -> scalarff::BigUint {
223 panic!("cannot retrieve a scalar representation for a polynomial field element");
224 }
225
226 fn from_biguint(_v: &scalarff::BigUint) -> Self {
227 panic!();
228 }
229
230 fn from_bytes_le(bytes: &[u8]) -> Self {
231 $name(Polynomial {
232 coefficients: bytes
233 .chunks($field_element::byte_len())
234 .map(|chunk| $field_element::from_bytes_le(chunk))
235 .collect::<Vec<_>>(),
236 })
237 }
238
239 fn to_bytes_le(&self) -> Vec<u8> {
240 self.0
241 .coefficients
242 .iter()
243 .flat_map(|v| v.to_bytes_le())
244 .collect()
245 }
246 }
247
248 impl std::fmt::Display for $name {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 write!(
251 f,
252 "{}",
253 self.0
254 .coefficients
255 .iter()
256 .map(|v| v.to_string())
257 .collect::<Vec<_>>()
258 .join(",")
259 )
260 }
261 }
262
263 impl std::str::FromStr for $name {
264 type Err = <$field_element as std::str::FromStr>::Err;
265
266 fn from_str(s: &str) -> Result<Self, Self::Err> {
267 Ok(Self(Polynomial {
268 coefficients: s
269 .split(',')
270 .map(|v| $field_element::from_str(v))
271 .collect::<Result<Vec<_>, _>>()?,
272 }))
273 }
274 }
275
276 impl From<u64> for $name {
277 fn from(value: u64) -> Self {
278 Self::from(Polynomial {
279 coefficients: vec![$field_element::from(value)],
280 })
281 }
282 }
283
284 impl std::ops::Add for $name {
285 type Output = Self;
286
287 fn add(self, other: Self) -> Self {
288 Self::from(self.0 + other.0)
289 }
290 }
291
292 impl std::ops::Sub for $name {
293 type Output = Self;
294
295 fn sub(self, other: Self) -> Self {
296 Self::from(self.0 - other.0)
297 }
298 }
299
300 impl std::ops::Mul for $name {
301 type Output = Self;
302
303 fn mul(self, other: Self) -> Self {
304 Self::from(self.0 * other.0)
305 }
306 }
307
308 impl std::ops::Div for $name {
309 type Output = Self;
310
311 fn div(self, other: Self) -> Self {
312 Self::from(self.0.div(&other.0).0)
314 }
315 }
316
317 impl std::ops::AddAssign for $name {
318 fn add_assign(&mut self, other: Self) {
319 *self = self.clone() + other;
320 }
321 }
322
323 impl std::ops::MulAssign for $name {
324 fn mul_assign(&mut self, other: Self) {
325 *self = self.clone() * other;
326 }
327 }
328
329 impl std::ops::SubAssign for $name {
330 fn sub_assign(&mut self, other: Self) {
331 *self = self.clone() - other;
332 }
333 }
334
335 impl std::ops::Neg for $name {
336 type Output = Self;
337
338 fn neg(self) -> Self {
339 $name(-self.0.clone())
340 }
341 }
342 };
343}
344
345#[cfg(test)]
346mod test {
347 use scalarff::FieldElement;
348 use scalarff::OxfoiFieldElement;
349
350 use super::Polynomial;
351 use super::PolynomialRingElement;
352
353 polynomial_ring!(
354 Poly64,
355 OxfoiFieldElement,
356 {
357 let mut p = Polynomial::new(vec![OxfoiFieldElement::one()]);
358 p.term(&OxfoiFieldElement::one(), 64);
359 p
360 },
361 "Poly64"
362 );
363
364 #[test]
365 fn scalar_math_in_ring() {
366 for x in 100..500 {
367 for y in 200..600 {
368 let z_scalar = OxfoiFieldElement::from(x) * OxfoiFieldElement::from(y);
369 let z_poly = Poly64::from(x) * Poly64::from(y);
370 assert_eq!(z_poly.polynomial().degree(), 0);
371 assert_eq!(z_poly.polynomial().coefficients[0], z_scalar);
372 }
373 }
374 }
375
376 #[test]
377 fn poly_coefs() {
378 let poly = Poly64::one();
379 let c = poly.coef();
382 assert_eq!(c.len(), Poly64::modulus().degree());
383 }
384
385 #[test]
386 #[cfg(feature = "rand")]
387 fn poly_rot() {
388 let a = Poly64::sample_uniform(&mut rand::thread_rng());
395 let b = Poly64::sample_uniform(&mut rand::thread_rng());
396 let rot_mat = a.rot();
398 let b_coef = b.coef();
399
400 let expected_coef = (a * b).coef();
402 let actual_coef = rot_mat * b_coef.clone();
403 for i in 0..b_coef.len() {
404 assert_eq!(expected_coef[i], actual_coef[i]);
405 }
406 }
407}