labrador/ring/
rq.rs

1//! Polynomial ring **R = Z_q[X]/(X^d + 1)** with `d = 64` and `q = u32::MAX`.
2
3use crate::ring::zq::Zq;
4use crate::ring::Norms;
5use core::ops::{Add, Mul, Sub};
6use rand::distr::{Distribution, Uniform};
7use rand::{CryptoRng, Rng};
8use rustfft::num_complex::Complex;
9use rustfft::FftPlanner;
10
11/// This module provides implementations for various operations
12/// in the polynomial ring R = Z_q\[X\] / (X^d + 1).
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct Rq {
15    coeffs: [Zq; Self::DEGREE],
16}
17
18impl Rq {
19    pub const DEGREE: usize = 64;
20    /// Constructor for the polynomial ring
21    pub const fn new(coeffs: [Zq; Self::DEGREE]) -> Self {
22        Self { coeffs }
23    }
24
25    /// Generate zero polynomial
26    pub const fn zero() -> Self {
27        Self {
28            coeffs: [Zq::ZERO; Self::DEGREE],
29        }
30    }
31
32    pub fn into_coeffs(self) -> [Zq; Self::DEGREE] {
33        self.coeffs
34    }
35
36    /// Get the coefficients as a vector
37    pub fn coeffs(&self) -> &[Zq; Self::DEGREE] {
38        &self.coeffs
39    }
40
41    /// Random coefficients in `(0, Zq::MAX)`.
42    pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
43        let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::NEG_ONE).unwrap();
44        let mut coeffs = [Zq::ZERO; Self::DEGREE];
45        coeffs.iter_mut().for_each(|c| *c = uniform.sample(rng));
46        Self { coeffs }
47    }
48
49    /// Random coefficients in `(-bound, bound)`.
50    pub fn random_with_bound<R: Rng + CryptoRng>(rng: &mut R, bound: u32) -> Self {
51        let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::new(bound)).unwrap();
52        let mut coeffs = [Zq::ZERO; Self::DEGREE];
53        coeffs.iter_mut().for_each(|c| {
54            *c = if rng.random_bool(0.5) {
55                -uniform.sample(rng)
56            } else {
57                uniform.sample(rng)
58            }
59        });
60        Self { coeffs }
61    }
62
63    /// Decomposes a polynomial into base-B representation:
64    /// p = p⁽⁰⁾ + p⁽¹⁾·B + p⁽²⁾·B² + ... + p⁽ᵗ⁻¹⁾·B^(t-1)
65    /// Where each p⁽ⁱ⁾ has small coefficients, using centered representatives
66    pub fn decompose(&self, base: Zq, num_parts: usize) -> Vec<Rq> {
67        let mut result = vec![Rq::zero(); num_parts];
68        self.coeffs.iter().enumerate().for_each(|(index, coeff)| {
69            coeff
70                .decompose(base, num_parts)
71                .into_iter()
72                .zip(result.iter_mut())
73                .for_each(|(decomposed_coeff, decomposed_vec)| {
74                    decomposed_vec.coeffs[index] = decomposed_coeff;
75                });
76        });
77        result
78    }
79
80    /// Compute the conjugate automorphism \sigma_{-1} of vector based on B) Constraints..., Page 21.
81    /// σ_{-1}(a_0, a_1, …, a_{n-1}) = (a_0, −a_{n-1}, −a_{n-2}, …, −a_1)
82    pub fn conjugate_automorphism(&self) -> Self {
83        let original_coefficients = self.coeffs();
84
85        let mut conjugated_coeffs = [Zq::ZERO; Rq::DEGREE];
86        conjugated_coeffs[0] = original_coefficients[0];
87
88        for (conj_coeff, original_coeff) in conjugated_coeffs
89            .iter_mut()
90            .skip(1)
91            .zip(original_coefficients.iter().rev())
92        {
93            *conj_coeff = original_coeff * Zq::NEG_ONE;
94        }
95
96        Self::new(conjugated_coeffs)
97    }
98
99    /// Compute the operator norm of a polynomial given its coefficients.
100    /// The operator norm is defined as the maximum magnitude of the DFT (eigenvalues)
101    /// of the coefficient vector.
102    ///
103    /// Note that: The operator norm only affects the coefficients of the random PolyRings generated from the challenge space.
104    /// Prover and Verifier will not do the operator norm check, because random PolyRings are determined after generation.
105    /// Both party will have access to the same PolyRings through transcript,
106    #[allow(clippy::as_conversions)]
107    pub fn operator_norm(&self) -> f64 {
108        let coeffs = self.coeffs();
109        let n = coeffs.len();
110        let mut planner = FftPlanner::new();
111        let fft = planner.plan_fft_forward(n);
112
113        // Convert coefficients into complex numbers (with zero imaginary parts)
114        let mut buffer: Vec<Complex<f64>> = coeffs
115            .iter()
116            .map(|&x| {
117                let half = Zq::NEG_ONE.div_floor_by(2);
118                let converted_value = if x > half {
119                    x.to_u128() as f64 - Zq::NEG_ONE.to_u128() as f64 - 1.0
120                } else {
121                    x.to_u128() as f64
122                };
123                Complex {
124                    re: converted_value,
125                    im: 0.0,
126                }
127            })
128            .collect();
129
130        // Compute the FFT (this gives the eigenvalues of the circulant matrix)
131        fft.process(&mut buffer);
132
133        // Return the maximum absolute value (norm) among the eigenvalues
134        buffer
135            .iter()
136            .map(|c| c.norm())
137            .fold(0.0, |max, x| max.max(x))
138    }
139}
140
141impl Add<&Rq> for &Rq {
142    type Output = Rq;
143    /// Add two polynomials
144    fn add(self, other: &Rq) -> Rq {
145        let mut coeffs = [Zq::ZERO; Rq::DEGREE];
146        for (r, (a, b)) in coeffs
147            .iter_mut()
148            .zip(self.coeffs.iter().zip(other.coeffs.iter()))
149        {
150            *r = *a + *b;
151        }
152        Rq::new(coeffs)
153    }
154}
155
156impl Sub<&Rq> for &Rq {
157    type Output = Rq;
158    /// Add two polynomials
159    fn sub(self, other: &Rq) -> Rq {
160        let mut coeffs = [Zq::ZERO; Rq::DEGREE];
161        for (r, (a, b)) in coeffs
162            .iter_mut()
163            .zip(self.coeffs.iter().zip(other.coeffs.iter()))
164        {
165            *r = *a - *b;
166        }
167        Rq::new(coeffs)
168    }
169}
170
171impl Mul<&Rq> for &Rq {
172    type Output = Rq;
173    /// Polynomial multiplication modulo x^D + 1
174    fn mul(self, other: &Rq) -> Rq {
175        let mut result = [Zq::ZERO; Rq::DEGREE];
176        let mut out_of_field = [Zq::ZERO; Rq::DEGREE];
177        for (i, &self_coeff) in self.coeffs.iter().enumerate() {
178            for (j, &other_coeff) in other.coeffs.iter().enumerate() {
179                if i + j < Rq::DEGREE {
180                    result[i + j] += self_coeff * other_coeff;
181                } else {
182                    out_of_field[(i + j) % Rq::DEGREE] += self_coeff * other_coeff;
183                }
184            }
185        }
186        // Process excess terms with sign adjustment
187        for i in (0..Rq::DEGREE).rev() {
188            result[i] -= out_of_field[i];
189        }
190        Rq::new(result)
191    }
192}
193
194impl Mul<&Zq> for &Rq {
195    type Output = Rq;
196    /// Scalar multiplication of a polynomial
197    fn mul(self, other: &Zq) -> Rq {
198        let mut copied_coeffs = self.coeffs;
199        for elem in copied_coeffs.iter_mut() {
200            *elem *= *other;
201        }
202        Rq::new(copied_coeffs)
203    }
204}
205
206impl Norms for Rq {
207    type NormType = u128;
208
209    #[allow(clippy::as_conversions)]
210    fn l2_norm_squared(&self) -> Self::NormType {
211        self.coeffs.l2_norm_squared()
212    }
213
214    fn linf_norm(&self) -> Self::NormType {
215        self.coeffs.linf_norm()
216    }
217}
218
219#[cfg(test)]
220pub mod tests {
221    use super::*;
222
223    pub fn generate_rq_from_zq_vector(vec: Vec<Zq>) -> Rq {
224        let mut temp = [Zq::ZERO; Rq::DEGREE];
225        // Process excess terms with sign adjustment
226        for i in (0..vec.len()).rev() {
227            let m = i / Rq::DEGREE;
228            let r = i % Rq::DEGREE;
229            let sign = if m % 2 == 0 { 1 } else { -1 };
230            if sign == 1 {
231                temp[r] += vec[i];
232            } else {
233                temp[r] -= vec[i];
234            }
235        }
236        Rq::new(temp)
237    }
238
239    pub mod helper {
240        use super::*;
241        pub fn padded(prefix: &[Zq]) -> [Zq; Rq::DEGREE] {
242            assert!(
243                prefix.len() <= Rq::DEGREE,
244                "too many coefficients for degree {}",
245                Rq::DEGREE
246            );
247
248            let mut out = [Zq::ZERO; Rq::DEGREE];
249            out[..prefix.len()].copy_from_slice(prefix);
250            out
251        }
252
253        pub fn rq_from(prefix: &[Zq]) -> Rq {
254            Rq {
255                coeffs: padded(prefix),
256            }
257        }
258    }
259
260    // Test new() and polynomial creation
261    #[test]
262    fn test_new_and_create_poly() {
263        let coeffs = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
264        let poly = helper::rq_from(&coeffs);
265        assert_eq!(poly.coeffs, helper::padded(&coeffs));
266
267        // Direct conversion
268        let coeffs2 = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
269        let poly_from_vec_direct: Rq = generate_rq_from_zq_vector(coeffs.to_vec());
270        assert_eq!(poly_from_vec_direct.coeffs, helper::padded(&coeffs2));
271    }
272
273    #[test]
274    fn test_into_coeffs_returns_correct() {
275        let mut coeffs = [Zq::ZERO; Rq::DEGREE];
276        coeffs[0] = Zq::ONE;
277        coeffs[0] = Zq::new(20);
278        coeffs[0] = Zq::new(3121);
279        coeffs[0] = Zq::new(40);
280        let poly: Rq = generate_rq_from_zq_vector(coeffs.to_vec());
281
282        let result = Rq::into_coeffs(poly); // or whatever constructor you have
283        assert_eq!(result, coeffs);
284    }
285
286    // Test addition of polynomials
287    #[test]
288    fn test_add() {
289        // Within bounds
290        let poly1: Rq =
291            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
292        let poly2: Rq =
293            generate_rq_from_zq_vector(vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE]);
294        let result = &poly1 + &poly2;
295        assert_eq!(
296            result.coeffs,
297            helper::padded(&[Zq::new(5), Zq::new(5), Zq::new(5), Zq::new(5)])
298        );
299
300        // Outside of bounds
301        let poly3: Rq =
302            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
303        let poly4: Rq =
304            generate_rq_from_zq_vector(vec![Zq::NEG_ONE, Zq::new(3), Zq::NEG_ONE, Zq::ONE]);
305        let result2 = &poly3 + &poly4;
306        assert_eq!(
307            result2.coeffs,
308            helper::padded(&[Zq::ZERO, Zq::new(5), Zq::new(2), Zq::new(5)])
309        );
310        // Addition with zero polynomial
311        let poly5: Rq =
312            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
313        let poly6: Rq = generate_rq_from_zq_vector(vec![Zq::ZERO]);
314        let result3 = &poly5 + &poly6;
315        assert_eq!(
316            result3.coeffs,
317            helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
318        );
319        // Addition with high coefficients
320        let poly7: Rq =
321            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::NEG_ONE]);
322        let poly8: Rq =
323            generate_rq_from_zq_vector(vec![Zq::NEG_ONE, Zq::NEG_ONE, Zq::NEG_ONE, Zq::NEG_ONE]);
324        let result3 = &poly7 + &poly8;
325        assert_eq!(
326            result3.coeffs,
327            helper::padded(&[Zq::ZERO, Zq::ONE, Zq::new(2), -Zq::new(2)])
328        );
329    }
330
331    // Test multiplication of polynomials
332    #[test]
333    fn test_mul() {
334        // Multiplication with wrapping
335        let poly1: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ONE, Zq::new(2)]);
336        let poly2: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ONE]);
337        let result = &poly1 * &poly2;
338        assert_eq!(
339            result.coeffs,
340            helper::padded(&[Zq::new(1), Zq::new(2), Zq::new(3), Zq::new(2)])
341        );
342
343        // Multiplication with zero polynomial
344        let poly3: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ONE, Zq::new(2)]);
345        let poly4: Rq = generate_rq_from_zq_vector(vec![Zq::ZERO]);
346        let result2 = &poly3 * &poly4;
347        assert_eq!(
348            result2.coeffs,
349            helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO])
350        );
351
352        // Needs to be revised later
353        // // Multiplication with wrapping higher order
354        // let poly5: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ONE, Zq::new(2)]);
355        // let poly6: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(7), Zq::new(5)].into();
356        // let result3 = poly5 * poly6;
357        // assert_eq!(
358        //     result3.coeffs,
359        //     helper::padded(&[Zq::new(u32::MAX - 12), Zq::new(u32::MAX - 16), Zq::ZERO])
360        // );
361    }
362
363    // Test subtraction of polynomials
364    #[test]
365    fn test_sub() {
366        // within bounds
367        let poly1: Rq =
368            generate_rq_from_zq_vector(vec![Zq::new(5), Zq::new(10), Zq::new(15), Zq::new(20)]);
369        let poly2: Rq =
370            generate_rq_from_zq_vector(vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)]);
371        let result = &poly1 - &poly2;
372        assert_eq!(
373            result.coeffs,
374            helper::padded(&[Zq::new(3), Zq::new(6), Zq::new(9), Zq::new(12)])
375        );
376
377        // Outside of bounds
378        let poly3: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ONE, Zq::new(3), Zq::new(2)]);
379        let poly4: Rq =
380            generate_rq_from_zq_vector(vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)]);
381        let result2 = &poly3 - &poly4;
382        assert_eq!(
383            result2.coeffs,
384            helper::padded(&[
385                Zq::ZERO - Zq::new(1),
386                Zq::ZERO - Zq::new(3),
387                Zq::ZERO - Zq::new(3),
388                Zq::ZERO - Zq::new(6),
389            ])
390        );
391        // Subtraction with zero polynomial
392        let poly5: Rq =
393            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
394        let poly6: Rq = generate_rq_from_zq_vector(vec![Zq::ZERO]);
395        let result3 = &poly6 - &poly5;
396        let result4 = &poly5 - &poly6;
397        assert_eq!(
398            result3.coeffs,
399            helper::padded(&[
400                Zq::ZERO - Zq::new(1),
401                Zq::ZERO - Zq::new(2),
402                Zq::ZERO - Zq::new(3),
403                Zq::ZERO - Zq::new(4),
404            ])
405        );
406        assert_eq!(
407            result4.coeffs,
408            helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
409        );
410    }
411
412    // Test scalar multiplication
413    #[test]
414    fn test_scalar_mul() {
415        let poly: Rq =
416            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
417        let result = &poly * &Zq::new(2);
418        assert_eq!(
419            result.coeffs,
420            helper::padded(&[Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)])
421        );
422    }
423
424    // Test coefficient extraction
425    #[test]
426    fn test_get_coefficient() {
427        let poly: Rq = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::NEG_ONE]);
428        let vec = helper::padded(&[Zq::ONE, Zq::ZERO, Zq::new(5), Zq::NEG_ONE]).to_vec();
429        assert!(poly.coeffs().to_vec() == vec);
430
431        let poly_zero: Rq =
432            generate_rq_from_zq_vector(vec![Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO]);
433        let vec_zero = helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO]).to_vec();
434        assert!(poly_zero.coeffs().to_vec() == vec_zero);
435    }
436
437    #[test]
438    fn test_conjugate_automorphism() {
439        use crate::core::inner_product::compute_linear_combination;
440
441        let poly1 = helper::rq_from(&[Zq::ONE, Zq::TWO, Zq::new(3)]);
442        let poly2 = helper::rq_from(&[Zq::new(4), Zq::new(5), Zq::new(6)]);
443        let inner_12 = compute_linear_combination(poly1.coeffs(), poly2.coeffs());
444        let conjugated_1 = poly1.conjugate_automorphism();
445        let inner_conjugated_12 = &conjugated_1 * &poly2;
446
447        assert_eq!(inner_conjugated_12.coeffs.len(), Rq::DEGREE);
448        assert_eq!(inner_conjugated_12.coeffs()[0], Zq::new(32));
449        assert_eq!(inner_conjugated_12.coeffs()[1], Zq::new(17));
450        assert_eq!(inner_conjugated_12.coeffs()[2], Zq::new(6));
451
452        // ct<\sigma_{-1}(poly1), poly2> ?= <poly1, poly2>
453        let ct_inner_conjugated_12 = inner_conjugated_12.coeffs()[0];
454        assert_eq!(ct_inner_conjugated_12, inner_12);
455    }
456}
457
458#[cfg(test)]
459mod norm_tests {
460    use super::tests::generate_rq_from_zq_vector;
461    use crate::ring::{rq::Rq, zq::Zq, Norms};
462
463    // Test the square of the norm
464    #[test]
465    fn test_l2_norm() {
466        let poly1 = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::NEG_ONE]);
467        let poly2 = generate_rq_from_zq_vector(vec![Zq::ZERO, Zq::ZERO, Zq::new(5), Zq::ONE]);
468        let poly3 = generate_rq_from_zq_vector(vec![Zq::new(5), Zq::ONE, -Zq::new(6), Zq::ZERO]);
469        let poly4 = Rq::zero();
470
471        assert_eq!(poly1.l2_norm_squared(), 27);
472        assert_eq!(poly2.l2_norm_squared(), 26);
473        assert_eq!(poly3.l2_norm_squared(), 62);
474        assert_eq!(poly4.l2_norm_squared(), 0);
475    }
476
477    #[test]
478    fn test_linf_norm() {
479        let poly1 = generate_rq_from_zq_vector(vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::NEG_ONE]);
480        let poly2 = generate_rq_from_zq_vector(vec![Zq::ZERO, Zq::ZERO, -Zq::new(5), Zq::ONE]);
481        let poly3 = generate_rq_from_zq_vector(vec![Zq::new(5), Zq::ONE, -Zq::new(6), Zq::ZERO]);
482        let poly4 = Rq::zero();
483
484        assert_eq!(poly1.linf_norm(), 5);
485        assert_eq!(poly2.linf_norm(), 5);
486        assert_eq!(poly3.linf_norm(), 6);
487        assert_eq!(poly4.linf_norm(), 0);
488    }
489}
490
491#[cfg(test)]
492mod decomposition_tests {
493    use crate::ring::rq::tests::{generate_rq_from_zq_vector, helper};
494
495    use super::*;
496
497    #[test]
498    fn failure_test() {
499        let poly1 =
500            generate_rq_from_zq_vector(vec![-Zq::new(192), -Zq::new(0), -Zq::new(0), -Zq::new(0)]);
501        let decomposed = poly1.decompose(Zq::new(1802), 10);
502
503        let mut result = Rq::zero();
504        let mut exponential_base = Zq::new(1);
505        for elem in decomposed {
506            result = &result + &(&elem * &exponential_base);
507            exponential_base *= Zq::new(24);
508        }
509        assert_eq!(result, poly1);
510    }
511
512    #[test]
513    fn test_base2_decomposition() {
514        // Test case 1: Base 2 decomposition
515        let poly: Rq =
516            generate_rq_from_zq_vector(vec![Zq::new(5), Zq::new(3), Zq::new(7), Zq::new(1)]);
517        let parts = poly.decompose(Zq::TWO, 4);
518
519        // Part 0: remainders mod 2 (no centering needed for base 2)
520        assert_eq!(
521            parts[0].coeffs,
522            helper::padded(&[
523                Zq::ONE, // 5 mod 2 = 1
524                Zq::ONE, // 3 mod 2 = 1
525                Zq::ONE, // 7 mod 2 = 1
526                Zq::ONE, // 1 mod 2 = 1
527            ])
528        );
529
530        // Part 1: quotients after division by 2
531        assert_eq!(
532            parts[1].coeffs,
533            helper::padded(&[Zq::ZERO, Zq::ONE, Zq::ONE, Zq::ZERO,])
534        );
535
536        // Part 2
537        assert_eq!(
538            parts[2].coeffs,
539            helper::padded(&[
540                Zq::ONE,  // 5 div 2 = 2
541                Zq::ZERO, // 3 div 2 = 1
542                Zq::ONE,  // 7 div 2 = 3
543                Zq::ZERO, // 1 div 2 = 0
544            ])
545        );
546
547        // Verify Base 2 reconstruction coefficient by coefficient
548        let mut result = Rq::zero();
549        let mut expo_base = Zq::ONE;
550        for part in parts {
551            result = &result + &(&part * &expo_base);
552            expo_base *= Zq::TWO;
553        }
554        assert_eq!(poly, result);
555    }
556
557    #[test]
558    fn test_decomposition_edge_cases() {
559        // Test zero polynomial
560        let zero_poly: Rq = generate_rq_from_zq_vector(vec![Zq::ZERO; 4]);
561        let parts = zero_poly.decompose(Zq::TWO, 2);
562        assert!(
563            // Check any polynomial is zero
564            parts
565                .iter()
566                .all(|p| p.coeffs().iter().all(|&coeff| coeff == Zq::ZERO)),
567            "Zero polynomial decomposition failed"
568        );
569
570        // Test single part decomposition
571        let simple_poly: Rq =
572            generate_rq_from_zq_vector(vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)]);
573        let parts = simple_poly.decompose(Zq::TWO, 1);
574        assert_eq!(parts.len(), 1, "Single part decomposition length incorrect");
575    }
576
577    #[test]
578    fn test_decomposition_real_values() {
579        let poly1 = Rq {
580            coeffs: [
581                Zq::new(23071),
582                Zq::new(4294941461),
583                Zq::new(4084),
584                Zq::new(23413),
585                Zq::new(26902),
586                Zq::new(4294960817),
587                Zq::new(4294957829),
588                Zq::new(18248),
589                Zq::new(15293),
590                Zq::new(45978),
591                Zq::new(27197),
592                Zq::new(11233),
593                Zq::new(4294962536),
594                Zq::new(1196),
595                Zq::new(17083),
596                Zq::new(4294960822),
597                Zq::new(4294967103),
598                Zq::new(4294949429),
599                Zq::new(2926),
600                Zq::new(2742),
601                Zq::new(27552),
602                Zq::new(4294955871),
603                Zq::new(2917),
604                Zq::new(4294938904),
605                Zq::new(2288),
606                Zq::new(4294948781),
607                Zq::new(4294966588),
608                Zq::new(4294951307),
609                Zq::new(4294961210),
610                Zq::new(19355),
611                Zq::new(4294956913),
612                Zq::new(14940),
613                Zq::new(4294934068),
614                Zq::new(4294961161),
615                Zq::new(4294959017),
616                Zq::new(3339),
617                Zq::new(4294932967),
618                Zq::new(4294938251),
619                Zq::new(4294943006),
620                Zq::new(4294965167),
621                Zq::new(4294960800),
622                Zq::new(4161),
623                Zq::new(9213),
624                Zq::new(4294962556),
625                Zq::new(14299),
626                Zq::new(44418),
627                Zq::new(2438),
628                Zq::new(6583),
629                Zq::new(4294957783),
630                Zq::new(16),
631                Zq::new(4294941724),
632                Zq::new(4294966309),
633                Zq::new(4294966984),
634                Zq::new(4294956138),
635                Zq::new(1779),
636                Zq::new(29598),
637                Zq::new(16393),
638                Zq::new(728),
639                Zq::new(4294944371),
640                Zq::new(4294951792),
641                Zq::new(4294943824),
642                Zq::new(4294937618),
643                Zq::new(4294955208),
644                Zq::new(11235),
645            ],
646        };
647        let base = Zq::new(1802);
648        let decomposed = poly1.decompose(base, 2);
649
650        // Verify reconstruction
651        let reconstructed = &decomposed[0] + &(&decomposed[1] * &base);
652
653        assert_eq!(decomposed.len(), 2);
654        assert_eq!(reconstructed, poly1, "Base {base}: Reconstruction failed");
655    }
656
657    #[test]
658    fn test_linf_norm() {
659        let poly1 = Rq {
660            coeffs: [
661                Zq::new(23071),
662                Zq::new(4294941461),
663                Zq::new(4084),
664                Zq::new(23413),
665                Zq::new(26902),
666                Zq::new(4294960817),
667                Zq::new(4294957829),
668                Zq::new(18248),
669                Zq::new(15293),
670                Zq::new(45978),
671                Zq::new(27197),
672                Zq::new(11233),
673                Zq::new(4294962536),
674                Zq::new(1196),
675                Zq::new(17083),
676                Zq::new(4294960822),
677                Zq::new(4294967103),
678                Zq::new(4294949429),
679                Zq::new(2926),
680                Zq::new(2742),
681                Zq::new(27552),
682                Zq::new(4294955871),
683                Zq::new(2917),
684                Zq::new(4294938904),
685                Zq::new(2288),
686                Zq::new(4294948781),
687                Zq::new(4294966588),
688                Zq::new(4294951307),
689                Zq::new(4294961210),
690                Zq::new(19355),
691                Zq::new(4294956913),
692                Zq::new(14940),
693                Zq::new(4294934068),
694                Zq::new(4294961161),
695                Zq::new(4294959017),
696                Zq::new(3339),
697                Zq::new(4294932967),
698                Zq::new(4294938251),
699                Zq::new(4294943006),
700                Zq::new(4294965167),
701                Zq::new(4294960800),
702                Zq::new(4161),
703                Zq::new(9213),
704                Zq::new(4294962556),
705                Zq::new(14299),
706                Zq::new(44418),
707                Zq::new(2438),
708                Zq::new(6583),
709                Zq::new(4294957783),
710                Zq::new(16),
711                Zq::new(4294941724),
712                Zq::new(4294966309),
713                Zq::new(4294966984),
714                Zq::new(4294956138),
715                Zq::new(1779),
716                Zq::new(29598),
717                Zq::new(16393),
718                Zq::new(728),
719                Zq::new(4294944371),
720                Zq::new(4294951792),
721                Zq::new(4294943824),
722                Zq::new(4294937618),
723                Zq::new(4294955208),
724                Zq::new(11235),
725            ],
726        };
727        let base = Zq::new(1802);
728        let decomposed = poly1.decompose(base, 2);
729
730        // Verify reconstruction
731        for decomposed_poly in decomposed {
732            assert!(decomposed_poly.linf_norm() <= 901)
733        }
734    }
735}