qfall_math/integer_mod_q/mat_polynomial_ring_zq/arithmetic/
sub.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 [`Sub`] trait for [`MatPolynomialRingZq`] values.
10
11use crate::error::MathError;
12use crate::integer::MatPolyOverZ;
13use crate::integer_mod_q::MatPolynomialRingZq;
14use crate::macros::arithmetics::{
15    arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
16    arithmetic_trait_mixed_borrowed_owned,
17};
18use crate::traits::{CompareBase, MatrixDimensions};
19use core::panic;
20use std::ops::{Sub, SubAssign};
21
22impl SubAssign<&MatPolynomialRingZq> for MatPolynomialRingZq {
23    /// Computes the subtraction of `self` and `other` reusing
24    /// the memory of `self`.
25    /// [`SubAssign`] can be used on [`MatPolynomialRingZq`] in combination with
26    /// [`MatPolynomialRingZq`] and [`MatPolyOverZ`].
27    ///
28    /// Parameters:
29    /// - `other`: specifies the value to subtract from `self`
30    ///
31    /// # Examples
32    /// ```
33    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
34    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
35    /// use qfall_math::integer::MatPolyOverZ;
36    /// use std::str::FromStr;
37    ///
38    /// let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
39    /// let mut a = MatPolynomialRingZq::identity(2, 2, &modulus);
40    /// let b = MatPolynomialRingZq::new(2, 2, &modulus);
41    /// let c = MatPolyOverZ::new(2, 2);
42    ///
43    /// a -= &b;
44    /// a -= b;
45    /// a -= &c;
46    /// a -= c;
47    /// ```
48    ///
49    /// # Panics ...
50    /// - if the matrix dimensions mismatch.
51    /// - if the moduli of the matrices mismatch.
52    fn sub_assign(&mut self, other: &Self) {
53        if !self.compare_base(other) {
54            panic!("{}", self.call_compare_base_error(other).unwrap());
55        }
56
57        self.matrix -= &other.matrix;
58
59        self.reduce();
60    }
61}
62impl SubAssign<&MatPolyOverZ> for MatPolynomialRingZq {
63    /// Documentation at [`MatPolynomialRingZq::sub_assign`].
64    fn sub_assign(&mut self, other: &MatPolyOverZ) {
65        self.matrix -= other;
66
67        self.reduce();
68    }
69}
70
71arithmetic_assign_trait_borrowed_to_owned!(
72    SubAssign,
73    sub_assign,
74    MatPolynomialRingZq,
75    MatPolynomialRingZq
76);
77arithmetic_assign_trait_borrowed_to_owned!(
78    SubAssign,
79    sub_assign,
80    MatPolynomialRingZq,
81    MatPolyOverZ
82);
83
84impl Sub for &MatPolynomialRingZq {
85    type Output = MatPolynomialRingZq;
86    /// Implements the [`Sub`] trait for two [`MatPolynomialRingZq`] values.
87    /// [`Sub`] is implemented for any combination of [`MatPolynomialRingZq`]
88    /// and borrowed [`MatPolynomialRingZq`].
89    ///
90    /// Parameters:
91    /// - `other`: specifies the value to subtract from`self`
92    ///
93    /// Returns the result of the subtraction 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("[[3  0 1 1, 1  3],[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  7],[0, 1  16]]").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 dimensions of both matrices mismatch.
116    /// - if the moduli of both matrices mismatch.
117    fn sub(self, other: Self) -> Self::Output {
118        self.sub_safe(other).unwrap()
119    }
120}
121
122arithmetic_trait_borrowed_to_owned!(
123    Sub,
124    sub,
125    MatPolynomialRingZq,
126    MatPolynomialRingZq,
127    MatPolynomialRingZq
128);
129arithmetic_trait_mixed_borrowed_owned!(
130    Sub,
131    sub,
132    MatPolynomialRingZq,
133    MatPolynomialRingZq,
134    MatPolynomialRingZq
135);
136
137impl Sub<&MatPolyOverZ> for &MatPolynomialRingZq {
138    type Output = MatPolynomialRingZq;
139    /// Implements the [`Sub`] trait for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
140    /// [`Sub`] is implemented for any combination of owned and borrowed values.
141    ///
142    /// Parameters:
143    /// - `other`: specifies the value to subtract from `self`
144    ///
145    /// Returns the subtraction of `self` by `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 sub(self, other: &MatPolyOverZ) -> Self::Output {
162        self.sub_mat_poly_over_z_safe(other).unwrap()
163    }
164}
165
166arithmetic_trait_borrowed_to_owned!(
167    Sub,
168    sub,
169    MatPolynomialRingZq,
170    MatPolyOverZ,
171    MatPolynomialRingZq
172);
173arithmetic_trait_mixed_borrowed_owned!(
174    Sub,
175    sub,
176    MatPolynomialRingZq,
177    MatPolyOverZ,
178    MatPolynomialRingZq
179);
180
181impl MatPolynomialRingZq {
182    /// Implements subtraction for two [`MatPolynomialRingZq`] matrices.
183    ///
184    ///
185    /// Parameters:
186    /// - `other`: specifies the value to subtract from`self`
187    ///
188    /// Returns the result of the subtraction as a [`MatPolynomialRingZq`] or an
189    /// error if the matrix dimensions or moduli mismatch.
190    ///
191    /// # Examples
192    /// ```
193    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
194    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
195    /// use qfall_math::integer::MatPolyOverZ;
196    /// use std::str::FromStr;
197    ///
198    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
199    /// let poly_mat_1 = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
200    /// let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
201    /// let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  7],[0, 1  16]]").unwrap();
202    /// let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
203    ///
204    /// let poly_ring_mat_3 = poly_ring_mat_1.sub_safe(&poly_ring_mat_2);
205    /// ```
206    /// # Errors and Failures
207    /// - Returns a [`MathError`] of type [`MathError::MismatchingModulus`] if the moduli of
208    ///   both [`MatPolynomialRingZq`] mismatch.
209    /// - Returns a [`MathError`] of type [`MathError::MismatchingMatrixDimension`]
210    ///   if the dimensions of both [`MatPolynomialRingZq`] mismatch.
211    pub fn sub_safe(&self, other: &Self) -> Result<MatPolynomialRingZq, MathError> {
212        if !self.compare_base(other) {
213            return Err(self.call_compare_base_error(other).unwrap());
214        }
215        let matrix = self.matrix.sub_safe(&other.matrix)?;
216
217        Ok(MatPolynomialRingZq::from((&matrix, &self.modulus)))
218    }
219
220    /// Implements subtraction for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
221    ///
222    /// Parameters:
223    /// - `other`: specifies the value to subtract from `self`
224    ///
225    /// Returns the subtraction of `self` by `other` as a [`MatPolynomialRingZq`].
226    ///
227    /// # Examples
228    /// ```
229    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
230    /// use qfall_math::integer::MatPolyOverZ;
231    /// use std::str::FromStr;
232    ///
233    /// let mat_1 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
234    /// let mat_2 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
235    ///
236    /// let mat_3 = &mat_1.sub_mat_poly_over_z_safe(&mat_2).unwrap();
237    /// ```
238    ///
239    /// # Errors and Failures
240    /// - Returns a [`MathError`] of type
241    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
242    ///   and `other` do not match for multiplication.
243    pub fn sub_mat_poly_over_z_safe(&self, other: &MatPolyOverZ) -> Result<Self, MathError> {
244        let mut out =
245            MatPolynomialRingZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
246
247        out.matrix = self.matrix.sub_safe(other)?;
248        out.reduce();
249
250        Ok(out)
251    }
252}
253
254#[cfg(test)]
255mod test_sub_assign {
256    use crate::{
257        integer::MatPolyOverZ,
258        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
259    };
260    use std::str::FromStr;
261
262    /// Ensure that `sub_assign` works for small numbers.
263    #[test]
264    fn correct_small() {
265        let mut a = MatPolynomialRingZq::from_str("[[1  1, 0],[0, 1  1]] / 2  0 1 mod 7").unwrap();
266        let b = MatPolynomialRingZq::from_str("[[1  -4, 1  -5],[1  6, 2  -6 -1]] / 2  0 1 mod 7")
267            .unwrap();
268        let cmp = MatPolynomialRingZq::from_str("[[1  5, 1  5],[1  1, 0]] / 2  0 1 mod 7").unwrap();
269
270        a -= b;
271
272        assert_eq!(cmp, a);
273    }
274
275    /// Ensure that `sub_assign` works for large numbers.
276    #[test]
277    fn correct_large() {
278        let mut a = MatPolynomialRingZq::from_str(&format!(
279            "[[1  {}, 1  5],[1  {}, 1  -1]] / 2  0 1 mod {}",
280            i64::MAX,
281            i64::MIN,
282            u64::MAX
283        ))
284        .unwrap();
285        let b = MatPolynomialRingZq::from_str(&format!(
286            "[[1  -{}, 1  6],[1  -6, 1  1]] / 2  0 1 mod {}",
287            i64::MAX,
288            u64::MAX
289        ))
290        .unwrap();
291        let cmp = MatPolynomialRingZq::from_str(&format!(
292            "[[1  {}, 1  -1],[1  {}, 1  -2]] / 2  0 1 mod {}",
293            2 * (i64::MAX as u64),
294            i64::MIN + 6,
295            u64::MAX
296        ))
297        .unwrap();
298
299        a -= b;
300
301        assert_eq!(cmp, a);
302    }
303
304    /// Ensure that `sub_assign` works for different matrix dimensions.
305    #[test]
306    fn matrix_dimensions() {
307        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
308        let dimensions = [(3, 3), (5, 1), (1, 4)];
309
310        for (nr_rows, nr_cols) in dimensions {
311            let mut a = MatPolynomialRingZq::identity(nr_rows, nr_cols, &modulus);
312            let b = MatPolynomialRingZq::new(nr_rows, nr_cols, &modulus);
313
314            a -= b;
315
316            assert_eq!(MatPolynomialRingZq::identity(nr_rows, nr_cols, &modulus), a);
317        }
318    }
319
320    /// Ensure that mismatching dimensions will result in a panic.
321    #[test]
322    #[should_panic]
323    fn mismatching_dimensions() {
324        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
325        let mut a = MatPolynomialRingZq::new(2, 1, &modulus);
326        let b = MatPolynomialRingZq::new(1, 1, &modulus);
327
328        a -= b;
329    }
330
331    /// Ensures that mismatching moduli will result in a panic.
332    #[test]
333    #[should_panic]
334    fn mismatching_moduli() {
335        let modulus_0 = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
336        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 7").unwrap();
337        let mut a = MatPolynomialRingZq::new(1, 1, &modulus_0);
338        let b = MatPolynomialRingZq::new(1, 1, &modulus_1);
339
340        a -= b;
341    }
342
343    /// Ensure that `sub_assign` is available for all types.
344    #[test]
345    fn availability() {
346        let modulus = ModulusPolynomialRingZq::from_str("3  1 0 1 mod 7").unwrap();
347        let mut a = MatPolynomialRingZq::new(2, 2, &modulus);
348        let b = MatPolynomialRingZq::new(2, 2, &modulus);
349        let c = MatPolyOverZ::new(2, 2);
350
351        a -= &b;
352        a -= b;
353        a -= &c;
354        a -= c;
355    }
356}
357
358#[cfg(test)]
359mod test_sub {
360    use crate::{
361        integer::MatPolyOverZ,
362        integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
363    };
364    use std::str::FromStr;
365
366    /// Testing subtraction for two [`MatPolynomialRingZq`].
367    #[test]
368    fn sub() {
369        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
370        let poly_mat_1 = MatPolyOverZ::from_str("[[3  0 1 1, 1  42],[0, 2  1 2]]").unwrap();
371        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
372        let poly_mat_2 = MatPolyOverZ::from_str("[[2  0 1, 1  42],[2  3 4, 2  0 1]]").unwrap();
373        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
374
375        let poly_ring_mat_3 = poly_ring_mat_1 - poly_ring_mat_2;
376
377        let cmp_poly_mat = MatPolyOverZ::from_str("[[3  0 0 1, 0],[2  -3 -4, 2  1 1]]").unwrap();
378        let cmp_poly_ring_mat = MatPolynomialRingZq::from((&cmp_poly_mat, &modulus));
379        assert_eq!(cmp_poly_ring_mat, poly_ring_mat_3);
380    }
381
382    /// Testing subtraction for large numbers.
383    #[test]
384    fn sub_large_numbers() {
385        let modulus =
386            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
387        let poly_mat_1 =
388            MatPolyOverZ::from_str(&format!("[[3  0 {} 1, 1  42],[0, 2  1 2]]", i64::MAX)).unwrap();
389        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
390        let poly_mat_2 =
391            MatPolyOverZ::from_str(&format!("[[2  0 {}, 1  42],[2  3 4, 2  0 1]]", i64::MAX))
392                .unwrap();
393        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
394
395        let poly_ring_mat_3 = poly_ring_mat_1 - poly_ring_mat_2;
396
397        let cmp_poly_mat = MatPolyOverZ::from_str("[[3  0 0 1, 0],[2  -3 -4, 2  1 1]]").unwrap();
398        let cmp_poly_ring_mat = MatPolynomialRingZq::from((&cmp_poly_mat, &modulus));
399        assert_eq!(cmp_poly_ring_mat, poly_ring_mat_3);
400    }
401
402    /// Testing sub_safe.
403    #[test]
404    fn sub_safe() {
405        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
406        let poly_mat_1 = MatPolyOverZ::from_str("[[3  0 1 1, 1  42],[0, 2  1 2]]").unwrap();
407        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
408        let poly_mat_2 = MatPolyOverZ::from_str("[[2  0 1, 1  42],[2  3 4, 2  0 1]]").unwrap();
409        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
410
411        let poly_ring_mat_3 = poly_ring_mat_1.sub_safe(&poly_ring_mat_2).unwrap();
412
413        let cmp_poly_mat = MatPolyOverZ::from_str("[[3  0 0 1, 0],[2  -3 -4, 2  1 1]]").unwrap();
414        let cmp_poly_ring_mat = MatPolynomialRingZq::from((&cmp_poly_mat, &modulus));
415        assert_eq!(cmp_poly_ring_mat, poly_ring_mat_3);
416    }
417
418    /// Testing sub_safe throws an error if the dimensions mismatch.
419    #[test]
420    fn sub_safe_error_dim() {
421        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
422        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
423        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
424        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42, 0],[0, 1  17, 1  1]]").unwrap();
425        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
426        let poly_mat_3 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42, 0]]").unwrap();
427        let poly_ring_mat_3 = MatPolynomialRingZq::from((&poly_mat_3, &modulus));
428
429        assert!(poly_ring_mat_1.sub_safe(&poly_ring_mat_2).is_err());
430        assert!(poly_ring_mat_3.sub_safe(&poly_ring_mat_2).is_err());
431    }
432
433    /// Testing sub_safe throws an error if the moduli mismatch.
434    #[test]
435    fn sub_safe_error_modulus() {
436        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
437        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 1 1 mod 17").unwrap();
438        let modulus_3 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 18").unwrap();
439        let poly_mat = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
440        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat, &modulus_1));
441        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat, &modulus_2));
442        let poly_ring_mat_3 = MatPolynomialRingZq::from((&poly_mat, &modulus_3));
443
444        assert!(poly_ring_mat_1.sub_safe(&poly_ring_mat_2).is_err());
445        assert!(poly_ring_mat_3.sub_safe(&poly_ring_mat_2).is_err());
446    }
447
448    /// Tests the doc test (availability).
449    #[test]
450    fn doc_test() {
451        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
452        let poly_mat_1 = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
453        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
454        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  7],[0, 1  16]]").unwrap();
455        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
456
457        let poly_ring_mat_3: MatPolynomialRingZq = &poly_ring_mat_1 - &poly_ring_mat_2;
458        let poly_ring_mat_4: MatPolynomialRingZq = poly_ring_mat_1 - poly_ring_mat_2;
459        let poly_ring_mat_5: MatPolynomialRingZq = &poly_ring_mat_3 - poly_ring_mat_4;
460        let _poly_ring_mat_6: MatPolynomialRingZq = poly_ring_mat_3 - &poly_ring_mat_5;
461    }
462}
463
464#[cfg(test)]
465mod test_mul_mat_poly_over_z {
466    use super::MatPolynomialRingZq;
467    use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
468    use std::str::FromStr;
469
470    const LARGE_PRIME: u64 = u64::MAX - 58;
471
472    /// Checks whether subtraction is available for other types.
473    #[test]
474    fn availability() {
475        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
476        let poly_mat = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
477        let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
478
479        let _ = &poly_ring_mat - &poly_mat;
480        let _ = &poly_ring_mat - poly_mat.clone();
481        let _ = poly_ring_mat.clone() - &poly_mat;
482        let _ = poly_ring_mat - poly_mat;
483    }
484
485    /// Checks if subtraction works fine for squared matrices.
486    #[test]
487    fn square_correctness() {
488        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
489        let poly_mat_1 = MatPolyOverZ::from_str("[[2  1 1, 1  42],[0, 2  1 2]]").unwrap();
490        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
491        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
492
493        let poly_ring_mat_3 = &poly_ring_mat_1 - &poly_mat_2;
494
495        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  -2 1 -1, 0],[0, 2  -16 2]]").unwrap();
496        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
497
498        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
499    }
500
501    /// Checks if subtraction works fine for large entries.
502    #[test]
503    fn large_entries() {
504        let modulus =
505            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
506        let poly_mat_1 = MatPolyOverZ::from_str(&format!("[[2  3 {}],[1  1]]", u64::MAX)).unwrap();
507        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
508        let poly_mat_2 = MatPolyOverZ::from_str(&format!("[[2  1 {}],[0]]", u64::MAX)).unwrap();
509
510        let poly_ring_mat_3 = &poly_ring_mat_1 - &poly_mat_2;
511
512        let poly_mat_cmp = MatPolyOverZ::from_str("[[2  2 0],[1  1]]").unwrap();
513        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
514
515        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
516    }
517
518    /// Checks if subtraction with incompatible matrix dimensions
519    /// throws an error as expected.
520    #[test]
521    fn errors() {
522        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
523        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1],[2  1 2]]").unwrap();
524        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
525        let poly_mat_2 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
526
527        assert!((poly_ring_mat_1.sub_mat_poly_over_z_safe(&poly_mat_2)).is_err());
528    }
529
530    /// Checks if subtraction panics if dimensions mismatch.
531    #[test]
532    #[should_panic]
533    fn mul_panic() {
534        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
535        let poly_mat_1 = MatPolyOverZ::from_str("[[1  3],[2  1 2]]").unwrap();
536        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
537        let poly_mat_2 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
538
539        let _ = &poly_ring_mat_1 - &poly_mat_2;
540    }
541}