labrador/ring/
rq.rs

1// This file is part of the polynomial ring operations module.
2//
3//
4// Currently implemented functions include:
5// - Polynomial addition:          +
6// - Polynomial multiplication:    *
7// - inner_product/ Dot product:   inner_product()
8// - Polynomial subtraction:       -
9// - Polynomial negation:          neg()
10// - Scalar multiplication:        scalar_mul()
11// - Polynomial evaluation:        eval()
12// - Zero check:                   is_zero()
13// - Polynomial equality check:    is_equal()
14// - Get the Coefficients:         get_coefficients()
15// - Random small norm vector:     random_small_vector()
16// - Squared norm of coefficients: compute_norm_squared()
17//
18// Further operations and optimizations will be added in future versions.
19
20// We use the Zq ring
21use crate::ring::zq::Zq;
22use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
23use rand::distr::{Distribution, Uniform};
24use rand::{CryptoRng, Rng};
25use rustfft::num_complex::Complex;
26use rustfft::FftPlanner;
27use std::iter::Sum;
28
29use super::rq_vector::RqVector;
30
31/// This module provides implementations for various operations
32/// in the polynomial ring R = Z_q\[X\] / (X^d + 1).
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Rq {
35    coeffs: [Zq; Self::DEGREE],
36}
37
38impl Rq {
39    pub const DEGREE: usize = 64;
40    /// Constructor for the polynomial ring
41    pub const fn new(coeffs: [Zq; Self::DEGREE]) -> Self {
42        Rq { coeffs }
43    }
44
45    /// Generate zero polynomial
46    pub const fn zero() -> Self {
47        Self {
48            coeffs: [Zq::ZERO; Self::DEGREE],
49        }
50    }
51
52    /// Get the coefficients as a vector
53    pub fn get_coefficients(&self) -> &[Zq; Self::DEGREE] {
54        &self.coeffs
55    }
56
57    /// Polynomial addition
58    fn addition(&self, other: &Self) -> Self {
59        let mut result = [Zq::ZERO; Self::DEGREE];
60        for (r, (a, b)) in result
61            .iter_mut()
62            .zip(self.coeffs.iter().zip(other.coeffs.iter()))
63        {
64            *r = *a + *b;
65        }
66        Rq::new(result)
67    }
68
69    /// Polynomial subtraction
70    fn subtraction(&self, other: &Self) -> Self {
71        let mut result = [Zq::ZERO; Self::DEGREE];
72        for (r, (a, b)) in result
73            .iter_mut()
74            .zip(self.coeffs.iter().zip(other.coeffs.iter()))
75        {
76            *r = *a - *b;
77        }
78        Rq::new(result)
79    }
80
81    /// Polynomial multiplication modulo x^D + 1
82    pub fn multiplication(&self, other: &Self) -> Self {
83        let mut result = [Zq::ZERO; Self::DEGREE];
84        let mut out_of_field = [Zq::ZERO; Self::DEGREE];
85        for (i, &self_coeff) in self.coeffs.iter().enumerate() {
86            for (j, &other_coeff) in other.coeffs.iter().enumerate() {
87                if i + j < Self::DEGREE {
88                    result[i + j] += self_coeff * other_coeff;
89                } else {
90                    out_of_field[(i + j) % Self::DEGREE] += self_coeff * other_coeff;
91                }
92            }
93        }
94        // Process excess terms with sign adjustment
95        for i in (0..Self::DEGREE).rev() {
96            let m = i / Self::DEGREE;
97            let r = i % Self::DEGREE;
98            let sign = if (m + 1) % 2 == 0 { 1 } else { -1 };
99            if sign == 1 {
100                result[r] += out_of_field[i];
101            } else {
102                result[r] -= out_of_field[i];
103            }
104        }
105        Rq::new(result)
106    }
107
108    /// Dot product between coefficients
109    pub fn inner_product(&self, other: &Self) -> Zq {
110        self.coeffs
111            .iter()
112            .zip(other.coeffs.iter())
113            .map(|(&a, &b)| a * b)
114            .fold(Zq::ZERO, |acc, x| acc + x)
115    }
116
117    /// Scalar multiplication
118    pub fn scalar_mul(&self, s: Zq) -> Self {
119        let mut result = [Zq::ZERO; Self::DEGREE];
120        for (i, &coeff) in self.coeffs.iter().enumerate() {
121            result[i] = s * (coeff);
122        }
123        Rq::new(result)
124    }
125
126    /// Evaluate the polynomial at a specific point
127    pub fn eval(&self, x: Zq) -> Zq {
128        let mut result = Zq::ZERO;
129        for coeff in self.coeffs.iter().rev() {
130            result = result * x + *coeff;
131        }
132
133        result
134    }
135
136    /// Check if Polynomial == 0
137    pub fn is_zero(&self) -> bool {
138        self.coeffs.iter().all(|&coeff| coeff == Zq::ZERO)
139    }
140
141    /// Check if two polynomials are equal
142    pub fn is_equal(&self, other: &Self) -> bool {
143        self.coeffs == other.coeffs
144    }
145
146    /// Generate random polynomial with a provided cryptographically secure RNG
147    pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
148        let uniform = Uniform::new_inclusive(Zq::ZERO, Zq::MAX).unwrap();
149        let mut coeffs = [Zq::ZERO; Self::DEGREE];
150        coeffs.iter_mut().for_each(|c| *c = uniform.sample(rng));
151        Self { coeffs }
152    }
153
154    /// Generate random small polynomial with secure RNG implementation
155    pub fn random_ternary<R: Rng + CryptoRng>(rng: &mut R) -> Self {
156        let mut coeffs = [Zq::ZERO; Self::DEGREE];
157
158        for coeff in coeffs.iter_mut() {
159            // Explicitly sample from {-1, 0, 1} with equal probability
160            let val = match rng.random_range(0..3) {
161                0 => Zq::MAX,  // -1 mod q
162                1 => Zq::ZERO, // 0
163                2 => Zq::ONE,  // 1
164                _ => unreachable!(),
165            };
166            *coeff = val;
167        }
168
169        Rq::new(coeffs)
170    }
171
172    /// Decomposes a polynomial into base-B representation:
173    /// p = p⁽⁰⁾ + p⁽¹⁾·B + p⁽²⁾·B² + ... + p⁽ᵗ⁻¹⁾·B^(t-1)
174    /// Where each p⁽ⁱ⁾ has small coefficients, using centered representatives
175    pub fn decompose(&self, base: Zq, num_parts: usize) -> RqVector {
176        let mut parts = Vec::with_capacity(num_parts);
177        let mut current = self.clone();
178
179        for i in 0..num_parts {
180            if i == num_parts - 1 {
181                parts.push(current.clone());
182            } else {
183                // Extract low part (mod base, centered around 0)
184                let mut low_coeffs = [Zq::ZERO; Self::DEGREE];
185
186                for (j, coeff) in current.get_coefficients().iter().enumerate() {
187                    low_coeffs[j] = coeff.centered_mod(base);
188                }
189
190                let low_part = Self::new(low_coeffs);
191                parts.push(low_part.clone());
192
193                // Update current
194                current -= low_part;
195
196                // Scale by base
197                let mut scaled_coeffs = [Zq::ZERO; Self::DEGREE];
198                for (j, coeff) in current.get_coefficients().iter().enumerate() {
199                    scaled_coeffs[j] = coeff.scale_by(base);
200                }
201                current = Self::new(scaled_coeffs);
202            }
203        }
204
205        RqVector::new(parts)
206    }
207
208    /// Encode message into polynomial with small coefficients.
209    ///
210    /// # Arguments
211    /// * `message` - A slice of booleans representing a binary message
212    ///
213    /// # Returns
214    /// * `Some(Rq)` - A polynomial where each coefficient is 0 or 1 based on the message bits
215    /// * `None` - If the message length exceeds the polynomial degree D
216    ///
217    /// # Format
218    /// * Each boolean is encoded as a coefficient: false -> 0, true -> 1
219    /// * Message bits are mapped to coefficients in order (index 0 -> constant term)
220    /// * Remaining coefficients (if message is shorter than D) are set to 0
221    pub fn encode_message(message: &[bool]) -> Option<Self> {
222        if message.len() > Self::DEGREE {
223            return None;
224        }
225
226        let mut coeffs = [Zq::ZERO; Self::DEGREE];
227        for (i, &bit) in message.iter().enumerate() {
228            coeffs[i] = Zq::new(u32::from(bit));
229        }
230        Some(Rq::new(coeffs))
231    }
232
233    /// Iterator over coefficients
234    pub fn iter(&self) -> std::slice::Iter<'_, Zq> {
235        self.coeffs.iter()
236    }
237
238    /// Check if polynomial coefficients are within bounds
239    pub fn check_bounds(&self, bound: Zq) -> bool {
240        self.iter().all(|coeff| coeff <= &bound || coeff >= &-bound)
241    }
242
243    /// Compute the conjugate automorphism \sigma_{-1} of vector based on B) Constraints..., Page 21.
244    pub fn conjugate_automorphism(&self) -> Self {
245        let q_minus_1 = Zq::MAX;
246        let mut new_coeffs = [Zq::ZERO; Self::DEGREE];
247        for (i, new_coeff) in new_coeffs.iter_mut().enumerate().take(Self::DEGREE) {
248            if i < self.get_coefficients().len() {
249                if i == 0 {
250                    *new_coeff = self.get_coefficients()[i];
251                } else {
252                    *new_coeff = self.get_coefficients()[i] * q_minus_1;
253                }
254            } else {
255                *new_coeff = Zq::ZERO;
256            }
257        }
258        debug_assert_eq!(new_coeffs.len(), Self::DEGREE);
259        let mut reversed_coefficients = new_coeffs; // copy / move the array
260        reversed_coefficients[1..].reverse(); // reverse everything except index 0
261        Self::new(reversed_coefficients)
262    }
263
264    /// Compute the operator norm of a polynomial given its coefficients.
265    /// The operator norm is defined as the maximum magnitude of the DFT (eigenvalues)
266    /// of the coefficient vector.
267    ///
268    /// Note that: The operator norm only affects the coefficients of the random PolyRings generated from the challenge space.
269    /// Prover and Verifier will not do the operator norm check, because random PolyRings are determined after generation.
270    /// Both party will have access to the same PolyRings through transcript,
271    #[allow(clippy::as_conversions)]
272    pub fn operator_norm(&self) -> f64 {
273        let coeffs = self.get_coefficients();
274        let n = coeffs.len();
275        let mut planner = FftPlanner::new();
276        let fft = planner.plan_fft_forward(n);
277
278        // Convert coefficients into complex numbers (with zero imaginary parts)
279        let mut buffer: Vec<Complex<f64>> = coeffs
280            .iter()
281            .map(|&x| {
282                let half = Zq::MAX.scale_by(Zq::TWO);
283                let converted_value = if x > half {
284                    x.to_u128() as f64 - Zq::MAX.to_u128() as f64 - 1.0
285                } else {
286                    x.to_u128() as f64
287                };
288                Complex {
289                    re: converted_value,
290                    im: 0.0,
291                }
292            })
293            .collect();
294
295        // Compute the FFT (this gives the eigenvalues of the circulant matrix)
296        fft.process(&mut buffer);
297
298        // Return the maximum absolute value (norm) among the eigenvalues
299        buffer
300            .iter()
301            .map(|c| c.norm())
302            .fold(0.0, |max, x| max.max(x))
303    }
304}
305
306macro_rules! impl_arithmetic {
307    ($trait:ident, $assign_trait:ident, $method:ident, $assign_method:ident, $op_method:ident) => {
308        impl $trait for Rq {
309            type Output = Self;
310
311            fn $method(self, rhs: Self) -> Self::Output {
312                self.$op_method(&rhs)
313            }
314        }
315
316        impl $assign_trait for Rq {
317            fn $assign_method(&mut self, rhs: Self) {
318                let result = self.$op_method(&rhs);
319                self.coeffs = result.coeffs;
320            }
321        }
322    };
323}
324
325impl_arithmetic!(Add, AddAssign, add, add_assign, addition);
326impl_arithmetic!(Sub, SubAssign, sub, sub_assign, subtraction);
327impl_arithmetic!(Mul, MulAssign, mul, mul_assign, multiplication);
328
329impl From<Vec<Zq>> for Rq {
330    fn from(vec: Vec<Zq>) -> Self {
331        let mut temp = [Zq::ZERO; Self::DEGREE];
332        // Process excess terms with sign adjustment
333        for i in (0..vec.len()).rev() {
334            let m = i / Self::DEGREE;
335            let r = i % Self::DEGREE;
336            let sign = if m % 2 == 0 { 1 } else { -1 };
337            if sign == 1 {
338                temp[r] += vec[i];
339            } else {
340                temp[r] -= vec[i];
341            }
342        }
343        Rq::new(temp)
344    }
345}
346
347impl Mul<&Zq> for &Rq {
348    type Output = Rq;
349    /// Scalar multiplication of a polynomial
350    fn mul(self, other: &Zq) -> Rq {
351        let mut copied_coeffs = self.coeffs;
352        for elem in copied_coeffs.iter_mut() {
353            *elem *= *other;
354        }
355        Rq::new(copied_coeffs)
356    }
357}
358
359impl Add<&Rq> for &Rq {
360    type Output = Rq;
361    /// Add two polynomials
362    fn add(self, other: &Rq) -> Rq {
363        let mut coeffs = [Zq::ZERO; Rq::DEGREE];
364        for (idx, item) in coeffs.iter_mut().enumerate().take(Rq::DEGREE) {
365            *item = self.get_coefficients()[idx] + other.get_coefficients()[idx];
366        }
367        Rq::new(coeffs)
368    }
369}
370
371impl Sum for Zq {
372    // Accumulate using the addition operator
373    fn sum<I>(iter: I) -> Self
374    where
375        I: Iterator<Item = Zq>,
376    {
377        iter.fold(Zq::ZERO, |acc, x| acc + x)
378    }
379}
380
381impl Mul<&Rq> for &Rq {
382    type Output = Rq;
383    /// Polynomial multiplication of two polynomials
384    fn mul(self, other: &Rq) -> Rq {
385        // Initialize a vector to hold the intermediate multiplication result
386        let mut result_coefficients =
387            vec![Zq::new(0); self.get_coefficients().len() + other.get_coefficients().len() - 1];
388        for (i, &coeff1) in self.get_coefficients().iter().enumerate() {
389            for (j, &coeff2) in other.get_coefficients().iter().enumerate() {
390                result_coefficients[i + j] += coeff1 * coeff2;
391            }
392        }
393
394        // Reduce modulo X^d + 1
395        if result_coefficients.len() > Rq::DEGREE {
396            let q_minus_1 = Zq::MAX;
397            let (left, right) = result_coefficients.split_at_mut(Rq::DEGREE);
398            for (i, &overflow) in right.iter().enumerate() {
399                left[i] += overflow * q_minus_1;
400            }
401            result_coefficients.truncate(Rq::DEGREE);
402        }
403        Rq::new(result_coefficients[..Rq::DEGREE].try_into().unwrap())
404    }
405}
406
407// Implementing the Neg trait
408impl Neg for Rq {
409    type Output = Self;
410
411    /// Polynomial negation
412    fn neg(self) -> Self {
413        let mut result = [Zq::ZERO; Rq::DEGREE];
414        for (i, &coeff) in self.coeffs.iter().enumerate() {
415            result[i] = Zq::ZERO - coeff;
416        }
417        Rq::new(result)
418    }
419}
420
421impl FromIterator<Zq> for Rq {
422    fn from_iter<T: IntoIterator<Item = Zq>>(iter: T) -> Self {
423        let coeffs_vec: Vec<Zq> = iter.into_iter().collect();
424        assert_eq!(
425            coeffs_vec.len(),
426            Self::DEGREE,
427            "Iterator must contain exactly {} elements to create Rq<{}>",
428            Self::DEGREE,
429            Self::DEGREE
430        );
431
432        // Convert the vector to an array
433        let mut coeffs = [Zq::default(); Self::DEGREE];
434        for (i, coeff) in coeffs_vec.into_iter().enumerate().take(Self::DEGREE) {
435            coeffs[i] = coeff;
436        }
437
438        Rq::new(coeffs)
439    }
440}
441
442#[cfg(test)]
443mod tests {
444    use super::*;
445
446    mod helper {
447        use super::*;
448        pub fn padded(prefix: &[Zq]) -> [Zq; Rq::DEGREE] {
449            assert!(
450                prefix.len() <= Rq::DEGREE,
451                "too many coefficients for degree {}",
452                Rq::DEGREE
453            );
454
455            let mut out = [Zq::ZERO; Rq::DEGREE];
456            out[..prefix.len()].copy_from_slice(prefix);
457            out
458        }
459
460        pub fn rq_from(prefix: &[Zq]) -> Rq {
461            Rq {
462                coeffs: padded(prefix),
463            }
464        }
465    }
466
467    // Test new() and polynomial creation
468    #[test]
469    fn test_new_and_create_poly() {
470        let coeffs = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
471        let poly = helper::rq_from(&coeffs);
472        assert_eq!(poly.coeffs, helper::padded(&coeffs));
473
474        // Direct conversion
475        let coeffs2 = [Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)];
476        let poly_from_vec_direct: Rq = coeffs.to_vec().into();
477        assert_eq!(poly_from_vec_direct.coeffs, helper::padded(&coeffs2));
478    }
479
480    // Test addition of polynomials
481    #[test]
482    fn test_add() {
483        // Within bounds
484        let poly1: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
485        let poly2: Rq = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
486        let result = poly1 + poly2;
487        assert_eq!(
488            result.coeffs,
489            helper::padded(&[Zq::new(5), Zq::new(5), Zq::new(5), Zq::new(5)])
490        );
491
492        // Outside of bounds
493        let poly3: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
494        let poly4: Rq = vec![Zq::MAX, Zq::new(3), Zq::MAX, Zq::ONE].into();
495        let result2 = poly3 + poly4;
496        assert_eq!(
497            result2.coeffs,
498            helper::padded(&[Zq::ZERO, Zq::new(5), Zq::new(2), Zq::new(5)])
499        );
500        // Addition with zero polynomial
501        let poly5: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
502        let poly6: Rq = vec![Zq::ZERO].into();
503        let result3 = poly5 + poly6;
504        assert_eq!(
505            result3.coeffs,
506            helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
507        );
508        // Addition with high coefficients
509        let poly7: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::MAX].into();
510        let poly8: Rq = vec![Zq::MAX, Zq::MAX, Zq::MAX, Zq::MAX].into();
511        let result3 = poly7 + poly8;
512        assert_eq!(
513            result3.coeffs,
514            helper::padded(&[
515                Zq::ZERO,
516                Zq::ONE,
517                Zq::new(2),
518                Zq::new(u32::MAX.wrapping_add(u32::MAX))
519            ])
520        );
521    }
522
523    // Test multiplication of polynomials
524    #[test]
525    fn test_mul() {
526        // Multiplication with wrapping
527        let poly1: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
528        let poly2: Rq = vec![Zq::ONE, Zq::ONE].into();
529        let result = poly1 * poly2;
530        assert_eq!(
531            result.coeffs,
532            helper::padded(&[Zq::new(1), Zq::new(2), Zq::new(3), Zq::new(2)])
533        );
534
535        // Multiplication with zero polynomial
536        let poly3: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
537        let poly4: Rq = vec![Zq::ZERO].into();
538        let result2 = poly3 * poly4;
539        assert_eq!(
540            result2.coeffs,
541            helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO])
542        );
543
544        // Needs to be revised later
545        // // Multiplication with wrapping higher order
546        // let poly5: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(2)].into();
547        // let poly6: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(7), Zq::new(5)].into();
548        // let result3 = poly5 * poly6;
549        // assert_eq!(
550        //     result3.coeffs,
551        //     helper::padded(&[Zq::new(u32::MAX - 12), Zq::new(u32::MAX - 16), Zq::ZERO])
552        // );
553    }
554
555    // Test subtraction of polynomials
556    #[test]
557    fn test_sub() {
558        // within bounds
559        let poly1: Rq = vec![Zq::new(5), Zq::new(10), Zq::new(15), Zq::new(20)].into();
560        let poly2: Rq = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
561        let result = poly1 - poly2;
562        assert_eq!(
563            result.coeffs,
564            helper::padded(&[Zq::new(3), Zq::new(6), Zq::new(9), Zq::new(12)])
565        );
566
567        // Outside of bounds
568        let poly3: Rq = vec![Zq::ONE, Zq::ONE, Zq::new(3), Zq::new(2)].into();
569        let poly4: Rq = vec![Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)].into();
570        let result2 = poly3 - poly4;
571        assert_eq!(
572            result2.coeffs,
573            helper::padded(&[
574                Zq::MAX,
575                Zq::new(u32::MAX - 2),
576                Zq::new(u32::MAX - 2),
577                Zq::new(u32::MAX - 5)
578            ])
579        );
580        // Subtraction with zero polynomial
581        let poly5: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
582        let poly6: Rq = vec![Zq::ZERO].into();
583        let result3 = poly6.clone() - poly5.clone();
584        let result4 = poly5 - poly6;
585        assert_eq!(
586            result3.coeffs,
587            helper::padded(&[
588                Zq::MAX,
589                Zq::new(u32::MAX - 1),
590                Zq::new(u32::MAX - 2),
591                Zq::new(u32::MAX - 3)
592            ])
593        );
594        assert_eq!(
595            result4.coeffs,
596            helper::padded(&[Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)])
597        );
598    }
599
600    // Test negation of polynomial
601    #[test]
602    fn test_neg() {
603        let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
604        let result = -poly;
605        assert_eq!(
606            result.coeffs,
607            helper::padded(&[
608                Zq::MAX,
609                Zq::new(u32::MAX - 1),
610                Zq::new(u32::MAX - 2),
611                Zq::new(u32::MAX - 3)
612            ])
613        );
614    }
615
616    // Test scalar multiplication
617    #[test]
618    fn test_scalar_mul() {
619        let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
620        let result = poly.scalar_mul(Zq::new(2));
621        assert_eq!(
622            result.coeffs,
623            helper::padded(&[Zq::new(2), Zq::new(4), Zq::new(6), Zq::new(8)])
624        );
625    }
626
627    // Test polynomial evaluation
628    #[test]
629    fn test_eval() {
630        let poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
631        let result = poly.eval(Zq::new(2));
632        assert_eq!(result, Zq::new(49));
633    }
634
635    // Test equality check
636    #[test]
637    fn test_is_equal() {
638        let poly1: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
639        let poly2: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
640        let poly3: Rq = vec![Zq::new(4), Zq::new(3), Zq::new(2), Zq::ONE].into();
641        assert!(poly1.is_equal(&poly2));
642        assert!(!poly1.is_equal(&poly3));
643    }
644
645    // Test zero polynomial check
646    #[test]
647    fn test_is_zero_poly() {
648        let zero_poly: Rq = vec![Zq::ZERO; 4].into();
649        let non_zero_poly: Rq = vec![Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
650        assert!(zero_poly.is_zero());
651        assert!(!non_zero_poly.is_zero());
652    }
653
654    #[test]
655    fn test_encode_message() {
656        // Test successful encoding
657        let message = vec![true, false, true, false];
658        let encoded = Rq::encode_message(&message).unwrap();
659        assert_eq!(
660            encoded.coeffs,
661            helper::padded(&[Zq::ONE, Zq::ZERO, Zq::ONE, Zq::ZERO])
662        );
663
664        // Test message shorter than degree
665        let short_message = vec![true, false];
666        let encoded_short = Rq::encode_message(&short_message).unwrap();
667        assert_eq!(
668            encoded_short.coeffs,
669            helper::padded(&[Zq::ONE, Zq::ZERO, Zq::ZERO, Zq::ZERO])
670        );
671
672        // Test message too long
673        let long_message = vec![true; Rq::DEGREE + 2];
674        assert!(Rq::encode_message(&long_message).is_none());
675
676        // Test empty message
677        let empty_message: Vec<bool> = vec![];
678        let encoded_empty = Rq::encode_message(&empty_message).unwrap();
679        assert!(encoded_empty.is_zero());
680    }
681
682    // Test coefficient extraction
683    #[test]
684    fn test_get_coefficient() {
685        let poly: Rq = vec![Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX].into();
686        let vec = helper::padded(&[Zq::ONE, Zq::ZERO, Zq::new(5), Zq::MAX]).to_vec();
687        assert!(poly.get_coefficients().to_vec() == vec);
688
689        let poly_zero: Rq = vec![Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO].into();
690        let vec_zero = helper::padded(&[Zq::ZERO, Zq::ZERO, Zq::ZERO, Zq::ZERO]).to_vec();
691        assert!(poly_zero.get_coefficients().to_vec() == vec_zero);
692    }
693
694    #[test]
695    fn test_base2_decomposition() {
696        // Test case 1: Base 2 decomposition
697        let poly: Rq = vec![Zq::new(5), Zq::new(3), Zq::new(7), Zq::new(1)].into();
698        let parts = poly.decompose(Zq::TWO, 2);
699
700        // Part 0: remainders mod 2 (no centering needed for base 2)
701        assert_eq!(
702            parts[0].coeffs,
703            helper::padded(&[
704                Zq::ONE, // 5 mod 2 = 1
705                Zq::ONE, // 3 mod 2 = 1
706                Zq::ONE, // 7 mod 2 = 1
707                Zq::ONE, // 1 mod 2 = 1
708            ])
709        );
710
711        // Part 1: quotients after division by 2
712        assert_eq!(
713            parts[1].coeffs,
714            helper::padded(&[
715                Zq::new(2), // 5 div 2 = 2
716                Zq::ONE,    // 3 div 2 = 1
717                Zq::new(3), // 7 div 2 = 3
718                Zq::ZERO,   // 1 div 2 = 0
719            ])
720        );
721
722        // Verify Base 2 reconstruction coefficient by coefficient
723        for i in 0..4 {
724            let expected = poly.coeffs[i];
725            let actual = parts[0].coeffs[i] + parts[1].coeffs[i] * Zq::TWO;
726            assert_eq!(actual, expected, "Base 2: Coefficient {} mismatch", i);
727        }
728    }
729
730    #[test]
731    fn test_base3_decomposition() {
732        // Test case: Base 3 decomposition with centering
733        let specific_poly: Rq = vec![Zq::new(8), Zq::new(11), Zq::new(4), Zq::new(15)].into();
734        let parts = specific_poly.decompose(Zq::new(3), 2);
735
736        // Part 0: centered remainders mod 3
737        assert_eq!(
738            parts[0].coeffs,
739            helper::padded(&[
740                Zq::MAX,  // 8 mod 3 = 2 -> -1 (centered)
741                Zq::MAX,  // 11 mod 3 = 2 -> -1 (centered)
742                Zq::ONE,  // 4 mod 3 = 1 -> 1 (centered)
743                Zq::ZERO, // 15 mod 3 = 0 -> 0 (centered)
744            ])
745        );
746
747        // Part 1: quotients
748        assert_eq!(
749            parts[1].coeffs,
750            helper::padded(&[
751                Zq::new(3), // (8 + 1) div 3 = 3
752                Zq::new(4), // (11 + 1) div 3 = 4
753                Zq::ONE,    // 4 div 3 = 1
754                Zq::new(5), // 15 div 3 = 5
755            ])
756        );
757
758        // Verify Base 3 reconstruction coefficient by coefficient
759        for i in 0..4 {
760            let expected = specific_poly.coeffs[i];
761            let p0 = parts[0].coeffs[i];
762            let p1 = parts[1].coeffs[i];
763            let actual = p0 + p1 * Zq::new(3);
764            assert_eq!(actual, expected, "Base 3: Coefficient {} mismatch", i);
765        }
766    }
767
768    #[test]
769    fn test_decomposition_edge_cases() {
770        // Test zero polynomial
771        let zero_poly: Rq = vec![Zq::ZERO; 4].into();
772        let parts = zero_poly.decompose(Zq::TWO, 2);
773        assert!(
774            parts.iter().all(|p| p.is_zero()),
775            "Zero polynomial decomposition failed"
776        );
777
778        // Test single part decomposition
779        let simple_poly: Rq = vec![Zq::ONE, Zq::new(2), Zq::new(3), Zq::new(4)].into();
780        let parts = simple_poly.decompose(Zq::TWO, 1);
781        assert_eq!(
782            parts.get_elements().len(),
783            1,
784            "Single part decomposition length incorrect"
785        );
786        assert_eq!(
787            parts[0], simple_poly,
788            "Single part decomposition value incorrect"
789        );
790    }
791
792    #[test]
793    fn test_large_base_decomposition() {
794        // Test decomposition with larger bases (8 and 16)
795        let poly: Rq = vec![Zq::new(120), Zq::new(33), Zq::new(255), Zq::new(19)].into();
796
797        // Base 8 decomposition
798        let parts_base8 = poly.decompose(Zq::new(8), 2);
799
800        // Part 0: centered remainders mod 8
801        assert_eq!(
802            parts_base8[0].coeffs,
803            helper::padded(&[
804                Zq::ZERO,   // 120 mod 8 = 0 -> 0 (centered)
805                Zq::ONE,    // 33 mod 8 = 1 -> 1 (centered)
806                Zq::MAX,    // 255 mod 8 = 7 -> -1 (centered)
807                Zq::new(3), // 19 mod 8 = 3 -> 3 (centered)
808            ])
809        );
810
811        // Part 1: quotients
812        assert_eq!(
813            parts_base8[1].coeffs,
814            helper::padded(&[
815                Zq::new(15), // 120 div 8 = 15
816                Zq::new(4),  // 33 div 8 = 4
817                Zq::new(32), // (255 + 1) div 8 = 32
818                Zq::new(2),  // 19 div 8 = 2
819            ])
820        );
821
822        // Verify reconstruction coefficient by coefficient
823        for i in 0..4 {
824            let expected = poly.coeffs[i];
825            let p0 = parts_base8[0].coeffs[i];
826            let p1 = parts_base8[1].coeffs[i];
827            let actual = p0 + p1 * Zq::new(8);
828            assert_eq!(actual, expected, "Base 8: Coefficient {} mismatch", i);
829        }
830
831        // Base 16 decomposition
832        let parts_base16 = poly.decompose(Zq::new(16), 2);
833
834        // Verify reconstruction for base 16
835        for i in 0..4 {
836            let expected = poly.coeffs[i];
837            let p0 = parts_base16[0].coeffs[i];
838            let p1 = parts_base16[1].coeffs[i];
839            let actual = p0 + p1 * Zq::new(16);
840            assert_eq!(actual, expected, "Base 16: Coefficient {} mismatch", i);
841        }
842    }
843
844    #[test]
845    fn test_multi_part_decomposition() {
846        // Test with more than 2 parts
847        let poly: Rq = vec![Zq::new(123), Zq::new(456), Zq::new(789), Zq::new(101112)].into();
848
849        // Decompose into 3 parts with base 4
850        let parts = poly.decompose(Zq::new(4), 3);
851        assert_eq!(parts.get_elements().len(), 3, "Should have 3 parts");
852
853        // Test reconstruction with all 3 parts
854        let reconstructed =
855            parts[0].clone() + parts[1].scalar_mul(Zq::new(4)) + parts[2].scalar_mul(Zq::new(16)); // 4²
856
857        // Verify reconstruction coefficient by coefficient
858        for i in 0..4 {
859            assert_eq!(
860                reconstructed.coeffs[i], poly.coeffs[i],
861                "3-part base 4: Coefficient {} mismatch",
862                i
863            );
864        }
865    }
866
867    #[test]
868    fn test_centering_properties() {
869        // Test that centering works correctly for various values
870        // Using base 5 which has half_base = 2
871        let values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
872        let poly: Rq = values
873            .iter()
874            .map(|&v| Zq::new(v))
875            .collect::<Vec<Zq>>()
876            .into();
877
878        let parts = poly.decompose(Zq::new(5), 2);
879
880        // Expected centered values for each coefficient:
881        // 0 mod 5 = 0 -> 0
882        // 1 mod 5 = 1 -> 1
883        // 2 mod 5 = 2 -> 2 (at threshold)
884        // 3 mod 5 = 3 -> -2 (centered)
885        // 4 mod 5 = 4 -> -1 (centered)
886        // 5 mod 5 = 0 -> 0
887        // ... and so on
888        let expected_centered = [
889            Zq::ZERO,    // 0 centered
890            Zq::ONE,     // 1 centered
891            Zq::new(2),  // 2 centered (at threshold)
892            -Zq::new(2), // 3 centered to -2
893            -Zq::ONE,    // 4 centered to -1
894            Zq::ZERO,    // 5 centered
895            Zq::ONE,     // 6 centered
896            Zq::new(2),  // 7 centered
897            -Zq::new(2), // 8 centered to -2
898            -Zq::ONE,    // 9 centered to -1
899            Zq::ZERO,    // 10 centered
900        ];
901
902        for (i, &expected) in expected_centered.iter().enumerate() {
903            assert_eq!(
904                parts[0].coeffs[i], expected,
905                "Base 5 centering: Coefficient {} incorrectly centered",
906                i
907            );
908        }
909    }
910
911    #[test]
912    fn test_extreme_values() {
913        // Test with values near the extremes of the Zq range
914        let poly: Rq = vec![Zq::ZERO, Zq::MAX, Zq::MAX - Zq::ONE].into();
915
916        // Decompose with base 3
917        let parts = poly.decompose(Zq::new(3), 2);
918
919        // Verify reconstruction
920        let reconstructed = parts[0].clone() + parts[1].scalar_mul(Zq::new(3));
921
922        for i in 0..3 {
923            assert_eq!(
924                reconstructed.coeffs[i], poly.coeffs[i],
925                "Extreme values: Coefficient {} mismatch",
926                i
927            );
928        }
929
930        // Corrected test for high value divisibility
931        // u32::MAX = 4294967295, which equals 1431655765 * 3 + 0
932        // So u32::MAX mod 3 = 0, which remains 0 (no centering needed)
933        assert_eq!(parts[0].coeffs[1], Zq::ZERO); // Remainder after division by 3
934        assert_eq!(parts[1].coeffs[1], Zq::new(1431655765)); // Quotient
935
936        // Check u32::MAX - 1 as well
937        // 4294967294 mod 3 = 1, which remains 1 (no centering needed since 1 <= half_base)
938        assert_eq!(parts[0].coeffs[2], Zq::MAX); // u32::MAX - 1 is the third coefficient
939        assert_eq!(parts[1].coeffs[2], Zq::new(1431655765)); // Should be same quotient
940    }
941
942    #[test]
943    fn test_decomposition_properties() {
944        // Test the algebraic property that all coefficients in first part should be small
945        let poly: Rq = vec![
946            Zq::new(100),
947            Zq::new(200),
948            Zq::new(300),
949            Zq::new(400),
950            Zq::new(500),
951            Zq::new(600),
952            Zq::new(700),
953            Zq::new(800),
954        ]
955        .into();
956
957        for base in [2, 3, 4, 5, 8, 10, 16].iter() {
958            let parts = poly.decompose(Zq::new(*base), 2);
959            let half_base = Zq::new(*base).scale_by(Zq::TWO);
960
961            // Check that all coefficients in first part are properly "small"
962            for coeff in parts[0].coeffs.iter() {
963                // In centered representation, all coefficients should be <= half_base
964                let abs_coeff = if *coeff > Zq::new(u32::MAX / 2) {
965                    Zq::ZERO - *coeff // Handle negative values (represented as large positive ones)
966                } else {
967                    *coeff
968                };
969
970                assert!(
971                    abs_coeff <= half_base,
972                    "Base {}: First part coefficient {} exceeds half-base {}",
973                    base,
974                    coeff,
975                    half_base
976                );
977            }
978
979            // Verify reconstruction
980            let reconstructed = parts[0].clone() + parts[1].scalar_mul(Zq::new(*base));
981            assert_eq!(reconstructed, poly, "Base {}: Reconstruction failed", base);
982        }
983    }
984
985    #[test]
986    fn test_conjugate_automorphism() {
987        let poly1 = helper::rq_from(&[Zq::ONE, Zq::TWO, Zq::new(3)]);
988        let poly2 = helper::rq_from(&[Zq::new(4), Zq::new(5), Zq::new(6)]);
989        let inner_12 = poly1.inner_product(&poly2);
990        let conjugated_1 = poly1.conjugate_automorphism();
991        let inner_conjugated_12 = conjugated_1 * poly2;
992
993        assert_eq!(inner_conjugated_12.coeffs.len(), Rq::DEGREE);
994        assert_eq!(inner_conjugated_12.get_coefficients()[0], Zq::from(32));
995        assert_eq!(inner_conjugated_12.get_coefficients()[1], Zq::from(17));
996        assert_eq!(inner_conjugated_12.get_coefficients()[2], Zq::new(6));
997
998        // ct<\sigma_{-1}(poly1), poly2> ?= <poly1, poly2>
999        let ct_inner_conjugated_12 = inner_conjugated_12.get_coefficients()[0];
1000        assert_eq!(ct_inner_conjugated_12, inner_12);
1001    }
1002}