qfall_math/integer_mod_q/mat_polynomial_ring_zq/arithmetic/
add.rs

1// Copyright © 2023 Marcel Luca Schmidt
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! Implementation of the [`Add`] trait for [`MatPolynomialRingZq`] values.
10
11use super::super::MatPolynomialRingZq;
12use crate::{
13    error::MathError,
14    integer::MatPolyOverZ,
15    macros::arithmetics::{
16        arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
17        arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
18    },
19    traits::{CompareBase, MatrixDimensions},
20};
21use std::ops::{Add, AddAssign};
22
23impl AddAssign<&MatPolynomialRingZq> for MatPolynomialRingZq {
24    /// Computes the addition of `self` and `other` reusing
25    /// the memory of `self`.
26    /// [`AddAssign`] can be used on [`MatPolynomialRingZq`] in combination with
27    /// [`MatPolynomialRingZq`] and [`MatPolyOverZ`].
28    ///
29    /// Parameters:
30    /// - `other`: specifies the value to add to `self`
31    ///
32    /// # Examples
33    /// ```
34    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
35    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
36    /// use qfall_math::integer::MatPolyOverZ;
37    /// use std::str::FromStr;
38    ///
39    /// let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
40    /// let mut a = MatPolynomialRingZq::identity(2, 2, &modulus);
41    /// let b = MatPolynomialRingZq::new(2, 2, &modulus);
42    /// let c = MatPolyOverZ::new(2, 2);
43    ///
44    /// a += &b;
45    /// a += b;
46    /// a += &c;
47    /// a += c;
48    /// ```
49    ///
50    /// # Panics ...
51    /// - if the matrix dimensions mismatch.
52    /// - if the moduli of the matrices mismatch.
53    fn add_assign(&mut self, other: &Self) {
54        if !self.compare_base(other) {
55            panic!("{}", self.call_compare_base_error(other).unwrap());
56        }
57
58        self.matrix += &other.matrix;
59
60        self.reduce();
61    }
62}
63impl AddAssign<&MatPolyOverZ> for MatPolynomialRingZq {
64    /// Documentation at [`MatPolynomialRingZq::add_assign`].
65    fn add_assign(&mut self, other: &MatPolyOverZ) {
66        self.matrix += other;
67
68        self.reduce();
69    }
70}
71
72arithmetic_assign_trait_borrowed_to_owned!(
73    AddAssign,
74    add_assign,
75    MatPolynomialRingZq,
76    MatPolynomialRingZq
77);
78arithmetic_assign_trait_borrowed_to_owned!(
79    AddAssign,
80    add_assign,
81    MatPolynomialRingZq,
82    MatPolyOverZ
83);
84
85impl Add for &MatPolynomialRingZq {
86    type Output = MatPolynomialRingZq;
87    /// Implements the [`Add`] trait for two [`MatPolynomialRingZq`] values.
88    /// [`Add`] is implemented for any combination of [`MatPolynomialRingZq`] and borrowed [`MatPolynomialRingZq`].
89    ///
90    /// Parameters:
91    /// - `other`: specifies the polynomial to add to `self`
92    ///
93    /// Returns the sum of both polynomials as a [`MatPolynomialRingZq`].
94    ///
95    /// # Examples
96    /// ```
97    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
98    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
99    /// use qfall_math::integer::MatPolyOverZ;
100    /// use std::str::FromStr;
101    ///
102    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
103    /// let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
104    /// let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
105    /// let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
106    /// let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
107    ///
108    /// let poly_ring_mat_3: MatPolynomialRingZq = &poly_ring_mat_1 + &poly_ring_mat_2;
109    /// let poly_ring_mat_4: MatPolynomialRingZq = poly_ring_mat_1 + poly_ring_mat_2;
110    /// let poly_ring_mat_5: MatPolynomialRingZq = &poly_ring_mat_3 + poly_ring_mat_4;
111    /// let poly_ring_mat_6: MatPolynomialRingZq = poly_ring_mat_3 + &poly_ring_mat_5;
112    /// ```
113    ///
114    /// # Panics ...
115    /// - if the moduli of both [`MatPolynomialRingZq`] mismatch.
116    /// - if the dimensions of both [`MatPolynomialRingZq`] mismatch.
117    fn add(self, other: Self) -> Self::Output {
118        self.add_safe(other).unwrap()
119    }
120}
121
122arithmetic_trait_borrowed_to_owned!(
123    Add,
124    add,
125    MatPolynomialRingZq,
126    MatPolynomialRingZq,
127    MatPolynomialRingZq
128);
129arithmetic_trait_mixed_borrowed_owned!(
130    Add,
131    add,
132    MatPolynomialRingZq,
133    MatPolynomialRingZq,
134    MatPolynomialRingZq
135);
136
137impl Add<&MatPolyOverZ> for &MatPolynomialRingZq {
138    type Output = MatPolynomialRingZq;
139    /// Implements the [`Add`] trait for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
140    /// [`Add`] is implemented for any combination of owned and borrowed values.
141    ///
142    /// Parameters:
143    /// - `other`: specifies the value to add with `self`
144    ///
145    /// Returns the addition of `self` and `other` as a [`MatPolynomialRingZq`].
146    ///
147    /// # Examples
148    /// ```
149    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
150    /// use qfall_math::integer::MatPolyOverZ;
151    /// use std::str::FromStr;
152    ///
153    /// let mat_1 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
154    /// let mat_2 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
155    ///
156    /// let mat_3 = &mat_1 + &mat_2;
157    /// ```
158    ///
159    /// # Panics ...
160    /// - if the dimensions of `self` and `other` do not match for multiplication.
161    fn add(self, other: &MatPolyOverZ) -> Self::Output {
162        self.add_mat_poly_over_z_safe(other).unwrap()
163    }
164}
165
166arithmetic_trait_reverse!(
167    Add,
168    add,
169    MatPolyOverZ,
170    MatPolynomialRingZq,
171    MatPolynomialRingZq
172);
173
174arithmetic_trait_borrowed_to_owned!(
175    Add,
176    add,
177    MatPolynomialRingZq,
178    MatPolyOverZ,
179    MatPolynomialRingZq
180);
181arithmetic_trait_borrowed_to_owned!(
182    Add,
183    add,
184    MatPolyOverZ,
185    MatPolynomialRingZq,
186    MatPolynomialRingZq
187);
188arithmetic_trait_mixed_borrowed_owned!(
189    Add,
190    add,
191    MatPolynomialRingZq,
192    MatPolyOverZ,
193    MatPolynomialRingZq
194);
195arithmetic_trait_mixed_borrowed_owned!(
196    Add,
197    add,
198    MatPolyOverZ,
199    MatPolynomialRingZq,
200    MatPolynomialRingZq
201);
202
203impl MatPolynomialRingZq {
204    /// Implements addition for two [`MatPolynomialRingZq`] values.
205    ///
206    /// Parameters:
207    /// - `other`: specifies the polynomial to add to `self`
208    ///
209    /// Returns the sum of both polynomials as a [`MatPolynomialRingZq`] or an error if the moduli
210    /// mismatch, or the dimensions of the matrices mismatch.
211    ///
212    /// # Examples
213    /// ```
214    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
215    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
216    /// use qfall_math::integer::MatPolyOverZ;
217    /// use std::str::FromStr;
218    ///
219    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
220    /// let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
221    /// let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
222    /// let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
223    /// let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
224    ///
225    /// let poly_ring_mat_3: MatPolynomialRingZq = poly_ring_mat_1.add_safe(&poly_ring_mat_2).unwrap();
226    /// ```
227    ///
228    /// # Errors and Failures
229    /// - Returns a [`MathError`] of type [`MathError::MismatchingModulus`] if the moduli of
230    ///   both [`MatPolynomialRingZq`] mismatch.
231    /// - Returns a [`MathError`] of type [`MathError::MismatchingMatrixDimension`]
232    ///   if the dimensions of both [`MatPolynomialRingZq`] mismatch.
233    pub fn add_safe(&self, other: &Self) -> Result<MatPolynomialRingZq, MathError> {
234        if !self.compare_base(other) {
235            return Err(self.call_compare_base_error(other).unwrap());
236        }
237        let matrix = self.matrix.add_safe(&other.matrix)?;
238
239        Ok(MatPolynomialRingZq::from((&matrix, &self.modulus)))
240    }
241
242    /// Implements addition for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
243    ///
244    /// Parameters:
245    /// - `other`: specifies the value to add with `self`
246    ///
247    /// Returns the addition of `self` and `other` as a [`MatPolynomialRingZq`].
248    ///
249    /// # Examples
250    /// ```
251    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
252    /// use qfall_math::integer::MatPolyOverZ;
253    /// use std::str::FromStr;
254    ///
255    /// let mat_1 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
256    /// let mat_2 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
257    ///
258    /// let mat_3 = &mat_1.add_mat_poly_over_z_safe(&mat_2).unwrap();
259    /// ```
260    ///
261    /// # Errors and Failures
262    /// - Returns a [`MathError`] of type
263    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
264    ///   and `other` do not match for multiplication.
265    pub fn add_mat_poly_over_z_safe(&self, other: &MatPolyOverZ) -> Result<Self, MathError> {
266        let mut out =
267            MatPolynomialRingZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
268
269        out.matrix = self.matrix.add_safe(other)?;
270        out.reduce();
271
272        Ok(out)
273    }
274}
275
276#[cfg(test)]
277mod test_add_assign {
278    use crate::{
279        integer::MatPolyOverZ,
280        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
281    };
282    use std::str::FromStr;
283
284    /// Ensure that `add_assign` works for small numbers.
285    #[test]
286    fn correct_small() {
287        let mut a = MatPolynomialRingZq::from_str("[[1  1, 0],[0, 1  1]] / 2  0 1 mod 7").unwrap();
288        let b =
289            MatPolynomialRingZq::from_str("[[1  4, 1  5],[1  -6, 2  6 1]] / 2  0 1 mod 7").unwrap();
290        let cmp = MatPolynomialRingZq::from_str("[[1  5, 1  5],[1  1, 0]] / 2  0 1 mod 7").unwrap();
291
292        a += b;
293
294        assert_eq!(cmp, a);
295    }
296
297    /// Ensure that `add_assign` works for large numbers.
298    #[test]
299    fn correct_large() {
300        let mut a = MatPolynomialRingZq::from_str(&format!(
301            "[[1  {}, 1  5],[1  {}, 1  -1]] / 2  0 1 mod {}",
302            i64::MAX,
303            i64::MIN,
304            u64::MAX
305        ))
306        .unwrap();
307        let b = MatPolynomialRingZq::from_str(&format!(
308            "[[1  {}, 1  -6],[1  6, 1  -1]] / 2  0 1 mod {}",
309            i64::MAX,
310            u64::MAX
311        ))
312        .unwrap();
313        let cmp = MatPolynomialRingZq::from_str(&format!(
314            "[[1  {}, 1  -1],[1  {}, 1  -2]] / 2  0 1 mod {}",
315            2 * (i64::MAX as u64),
316            i64::MIN + 6,
317            u64::MAX
318        ))
319        .unwrap();
320
321        a += b;
322
323        assert_eq!(cmp, a);
324    }
325
326    /// Ensure that `add_assign` works for different matrix dimensions.
327    #[test]
328    fn matrix_dimensions() {
329        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
330        let dimensions = [(3, 3), (5, 1), (1, 4)];
331
332        for (nr_rows, nr_cols) in dimensions {
333            let mut a = MatPolynomialRingZq::new(nr_rows, nr_cols, &modulus);
334            let b = MatPolynomialRingZq::identity(nr_rows, nr_cols, &modulus);
335
336            a += b;
337
338            assert_eq!(MatPolynomialRingZq::identity(nr_rows, nr_cols, &modulus), a);
339        }
340    }
341
342    /// Ensure that mismatching dimensions will result in a panic.
343    #[test]
344    #[should_panic]
345    fn mismatching_dimensions() {
346        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
347        let mut a = MatPolynomialRingZq::new(2, 1, &modulus);
348        let b = MatPolynomialRingZq::new(1, 1, &modulus);
349
350        a += b;
351    }
352
353    /// Ensures that mismatching moduli will result in a panic.
354    #[test]
355    #[should_panic]
356    fn mismatching_moduli() {
357        let modulus_0 = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
358        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 7").unwrap();
359        let mut a = MatPolynomialRingZq::new(1, 1, &modulus_0);
360        let b = MatPolynomialRingZq::new(1, 1, &modulus_1);
361
362        a += b;
363    }
364
365    /// Ensure that `add_assign` is available for all types.
366    #[test]
367    fn availability() {
368        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
369        let mut a = MatPolynomialRingZq::new(2, 2, &modulus);
370        let b = MatPolynomialRingZq::new(2, 2, &modulus);
371        let c = MatPolyOverZ::new(2, 2);
372
373        a += &b;
374        a += b;
375        a += &c;
376        a += c;
377    }
378}
379
380#[cfg(test)]
381mod test_add {
382    use crate::integer::MatPolyOverZ;
383    use crate::integer_mod_q::MatPolynomialRingZq;
384    use crate::integer_mod_q::ModulusPolynomialRingZq;
385    use std::str::FromStr;
386
387    const LARGE_PRIME: u64 = 18446744073709551557;
388
389    /// Testing addition for two [`MatPolynomialRingZq`]
390    #[test]
391    fn add() {
392        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
393        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
394        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
395        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
396        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
397        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 2, 1  16],[0, 2  1 2]]").unwrap();
398        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
399
400        let poly_ring_mat_3 = poly_ring_mat_1 + poly_ring_mat_2;
401
402        assert_eq!(poly_ring_mat_3, poly_ring_mat_cmp);
403    }
404
405    /// Testing addition for two borrowed [`MatPolynomialRingZq`]
406    #[test]
407    fn add_borrow() {
408        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
409        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
410        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
411        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
412        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
413        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 2, 1  16],[0, 2  1 2]]").unwrap();
414        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
415
416        let poly_ring_mat_3 = &poly_ring_mat_1 + &poly_ring_mat_2;
417
418        assert_eq!(poly_ring_mat_3, poly_ring_mat_cmp);
419    }
420
421    /// Testing addition for borrowed [`MatPolynomialRingZq`] and [`MatPolynomialRingZq`]
422    #[test]
423    fn add_first_borrowed() {
424        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
425        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
426        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
427        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
428        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
429        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 2, 1  16],[0, 2  1 2]]").unwrap();
430        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
431
432        let poly_ring_mat_3 = &poly_ring_mat_1 + poly_ring_mat_2;
433
434        assert_eq!(poly_ring_mat_3, poly_ring_mat_cmp);
435    }
436
437    /// Testing addition for [`MatPolynomialRingZq`] and borrowed [`MatPolynomialRingZq`]
438    #[test]
439    fn add_second_borrowed() {
440        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
441        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
442        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
443        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
444        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
445        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 2, 1  16],[0, 2  1 2]]").unwrap();
446        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
447
448        let poly_ring_mat_3 = poly_ring_mat_1 + &poly_ring_mat_2;
449
450        assert_eq!(poly_ring_mat_3, poly_ring_mat_cmp);
451    }
452
453    /// Testing addition for [`MatPolynomialRingZq`] reduces `0` coefficients
454    #[test]
455    fn add_reduce() {
456        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
457        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1]]").unwrap();
458        let a = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
459        let poly_mat_2 = MatPolyOverZ::from_str("[[4  2 0 3 -1]]").unwrap();
460        let b = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
461        let c = a + &b;
462        assert_eq!(
463            c,
464            MatPolynomialRingZq::from((&MatPolyOverZ::from_str("[[3  1 0 4]]").unwrap(), &modulus))
465        );
466    }
467
468    /// Testing addition for large [`MatPolynomialRingZq`]
469    #[test]
470    fn add_large_numbers() {
471        let modulus = ModulusPolynomialRingZq::from_str(&format!(
472            "5  1 1 0 0 {} mod {LARGE_PRIME}",
473            i64::MAX
474        ))
475        .unwrap();
476        let poly_mat_1 = MatPolyOverZ::from_str(&format!(
477            "[[4  1 {} 1 1, 1  42],[0, 2  {} 2]]",
478            i64::MAX,
479            i64::MIN
480        ))
481        .unwrap();
482        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
483        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
484        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
485        let poly_mat_cmp = MatPolyOverZ::from_str(&format!(
486            "[[4  4 {} 2 1, 1  84],[0, 2  {} 2]]",
487            i64::MAX,
488            i64::MIN + 17
489        ))
490        .unwrap();
491        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
492
493        let poly_ring_mat_3 = poly_ring_mat_1 + poly_ring_mat_2;
494
495        assert_eq!(poly_ring_mat_3, poly_ring_mat_cmp);
496    }
497
498    /// Testing addition for [`MatPolynomialRingZq`] with different moduli does not work
499    #[test]
500    #[should_panic]
501    fn add_mismatching_modulus_modulus() {
502        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 11").unwrap();
503        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
504        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
505        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
506        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
507        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
508
509        let _ = poly_ring_mat_1 + poly_ring_mat_2;
510    }
511
512    /// Testing addition for [`MatPolynomialRingZq`] with different moduli does not work
513    #[test]
514    #[should_panic]
515    fn add_mismatching_modulus_polynomial() {
516        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
517        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
518        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
519        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
520        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
521        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
522
523        let _ = poly_ring_mat_1 + poly_ring_mat_2;
524    }
525
526    /// Testing addition for [`MatPolynomialRingZq`] with different dimensions does not work
527    #[test]
528    #[should_panic]
529    fn add_mismatching_dim() {
530        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
531        let poly_mat_1 = MatPolyOverZ::from_str("[[1  42],[2  1 2]]").unwrap();
532        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
533        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
534        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42]]").unwrap();
535        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
536
537        let _ = poly_ring_mat_1 + poly_ring_mat_2;
538    }
539
540    /// Testing whether add_safe throws an error for mismatching moduli
541    #[test]
542    fn add_safe_is_err_moduli() {
543        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
544        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
545        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
546        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
547        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
548        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
549
550        assert!(&poly_ring_mat_1.add_safe(&poly_ring_mat_2).is_err());
551    }
552
553    /// Testing whether add_safe throws an error for different dimensions
554    #[test]
555    fn add_safe_is_err_dim() {
556        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
557        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1],[2  1 2]]").unwrap();
558        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
559        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
560        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
561        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_2));
562
563        assert!(&poly_ring_mat_1.add_safe(&poly_ring_mat_2).is_err());
564    }
565}
566
567#[cfg(test)]
568mod test_mul_mat_poly_over_z {
569    use super::MatPolynomialRingZq;
570    use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
571    use std::str::FromStr;
572
573    const LARGE_PRIME: u64 = u64::MAX - 58;
574
575    /// Checks whether addition is available for other types.
576    #[test]
577    fn availability() {
578        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
579        let poly_mat = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
580        let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
581
582        let _ = &poly_mat + &poly_ring_mat;
583        let _ = &poly_mat + poly_ring_mat.clone();
584        let _ = poly_mat.clone() + &poly_ring_mat;
585        let _ = poly_mat.clone() + poly_ring_mat.clone();
586
587        let _ = &poly_ring_mat + &poly_mat;
588        let _ = &poly_ring_mat + poly_mat.clone();
589        let _ = poly_ring_mat.clone() + &poly_mat;
590        let _ = poly_ring_mat + poly_mat;
591    }
592
593    /// Checks if addition works fine for squared matrices.
594    #[test]
595    fn square_correctness() {
596        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
597        let poly_mat_1 = MatPolyOverZ::from_str("[[2  1 1, 1  42],[0, 2  1 2]]").unwrap();
598        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
599        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
600
601        let poly_ring_mat_3 = &poly_ring_mat_1 + &poly_mat_2;
602
603        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  4 1 1, 1  84],[0, 2  18 2]]").unwrap();
604        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
605
606        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
607    }
608
609    /// Checks if addition works fine for large entries.
610    #[test]
611    fn large_entries() {
612        let modulus =
613            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
614        let poly_mat_1 = MatPolyOverZ::from_str(&format!("[[2  3 {}],[1  1]]", u64::MAX)).unwrap();
615        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
616        let poly_mat_2 = MatPolyOverZ::from_str(&format!("[[2  1 {}],[0]]", u64::MAX)).unwrap();
617
618        let poly_ring_mat_3 = &poly_ring_mat_1 + &poly_mat_2;
619
620        let poly_mat_cmp =
621            MatPolyOverZ::from_str(&format!("[[2  4 {}],[1  1]]", u128::from(u64::MAX) * 2))
622                .unwrap();
623        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
624
625        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
626    }
627
628    /// Checks if addition with incompatible matrix dimensions
629    /// throws an error as expected.
630    #[test]
631    fn errors() {
632        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
633        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1],[2  1 2]]").unwrap();
634        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
635        let poly_mat_2 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
636
637        assert!((poly_ring_mat_1.add_mat_poly_over_z_safe(&poly_mat_2)).is_err());
638    }
639
640    /// Checks if addition panics if dimensions mismatch.
641    #[test]
642    #[should_panic]
643    fn mul_panic() {
644        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
645        let poly_mat_1 = MatPolyOverZ::from_str("[[1  3],[2  1 2]]").unwrap();
646        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
647        let poly_mat_2 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
648
649        let _ = &poly_ring_mat_1 + &poly_mat_2;
650    }
651}