qfall_math/integer/mat_poly_over_z/arithmetic/
sub.rs

1// Copyright © 2023 Phil Milewski, 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 [`MatPolyOverZ`] values.
10
11use super::super::MatPolyOverZ;
12use crate::error::MathError;
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::MatrixDimensions;
19use flint_sys::fmpz_poly_mat::fmpz_poly_mat_sub;
20use std::ops::{Sub, SubAssign};
21
22impl SubAssign<&MatPolyOverZ> for MatPolyOverZ {
23    /// Computes the subtraction of `self` and `other` reusing
24    /// the memory of `self`.
25    ///
26    /// Parameters:
27    /// - `other`: specifies the value to subtract from `self`
28    ///
29    /// # Examples
30    /// ```
31    /// use qfall_math::integer::MatPolyOverZ;
32    /// let mut a = MatPolyOverZ::identity(2, 2);
33    /// let b = MatPolyOverZ::new(2, 2);
34    ///
35    /// a -= &b;
36    /// a -= b;
37    /// ```
38    ///
39    /// # Panics ...
40    /// - if the matrix dimensions mismatch.
41    fn sub_assign(&mut self, other: &Self) {
42        if self.get_num_rows() != other.get_num_rows()
43            || self.get_num_columns() != other.get_num_columns()
44        {
45            panic!(
46                "Tried to subtract a '{}x{}' matrix and a '{}x{}' matrix.",
47                self.get_num_rows(),
48                self.get_num_columns(),
49                other.get_num_rows(),
50                other.get_num_columns()
51            );
52        }
53
54        unsafe { fmpz_poly_mat_sub(&mut self.matrix, &self.matrix, &other.matrix) };
55    }
56}
57
58arithmetic_assign_trait_borrowed_to_owned!(SubAssign, sub_assign, MatPolyOverZ, MatPolyOverZ);
59
60impl Sub for &MatPolyOverZ {
61    type Output = MatPolyOverZ;
62    /// Implements the [`Sub`] trait for two [`MatPolyOverZ`] values.
63    /// [`Sub`] is implemented for any combination of [`MatPolyOverZ`] and borrowed [`MatPolyOverZ`].
64    ///
65    /// Parameters:
66    /// - `other`: specifies the value to subtract from`self`
67    ///
68    /// Returns the result of the subtraction as a [`MatPolyOverZ`].
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer::MatPolyOverZ;
73    /// use std::str::FromStr;
74    ///
75    /// let a: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
76    /// let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
77    ///
78    /// let c: MatPolyOverZ = &a - &b;
79    /// let d: MatPolyOverZ = a - b;
80    /// let e: MatPolyOverZ = &c - d;
81    /// let f: MatPolyOverZ = c - &e;
82    /// ```
83    ///
84    /// # Panics ...
85    /// - if the dimensions of both matrices mismatch.
86    fn sub(self, other: Self) -> Self::Output {
87        self.sub_safe(other).unwrap()
88    }
89}
90
91arithmetic_trait_borrowed_to_owned!(Sub, sub, MatPolyOverZ, MatPolyOverZ, MatPolyOverZ);
92arithmetic_trait_mixed_borrowed_owned!(Sub, sub, MatPolyOverZ, MatPolyOverZ, MatPolyOverZ);
93
94impl Sub<&MatPolynomialRingZq> for &MatPolyOverZ {
95    type Output = MatPolynomialRingZq;
96    /// Implements the [`Sub`] trait for a [`MatPolyOverZ`] matrix with a [`MatPolynomialRingZq`] matrix.
97    /// [`Sub`] is implemented for any combination of owned and borrowed values.
98    ///
99    /// Parameters:
100    /// - `other`: specifies the value to subtract from `self`
101    ///
102    /// Returns the subtraction of `self` by `other` as a [`MatPolynomialRingZq`].
103    ///
104    /// # Examples
105    /// ```
106    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
107    /// use qfall_math::integer::MatPolyOverZ;
108    /// use std::str::FromStr;
109    ///
110    /// let mat_1 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
111    /// let mat_2 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
112    ///
113    /// let mat_3 = &mat_1 - &mat_2;
114    /// ```
115    ///
116    /// # Panics ...
117    /// - if the dimensions of `self` and `other` do not match for multiplication.
118    fn sub(self, other: &MatPolynomialRingZq) -> Self::Output {
119        self.sub_mat_poly_ring_zq_safe(other).unwrap()
120    }
121}
122
123arithmetic_trait_borrowed_to_owned!(
124    Sub,
125    sub,
126    MatPolyOverZ,
127    MatPolynomialRingZq,
128    MatPolynomialRingZq
129);
130arithmetic_trait_mixed_borrowed_owned!(
131    Sub,
132    sub,
133    MatPolyOverZ,
134    MatPolynomialRingZq,
135    MatPolynomialRingZq
136);
137
138impl MatPolyOverZ {
139    /// Implements subtraction for two [`MatPolyOverZ`] matrices.
140    ///
141    ///
142    /// Parameters:
143    /// - `other`: specifies the value to subtract from`self`
144    ///
145    /// Returns the result of the subtraction as a [`MatPolyOverZ`] or an
146    /// error if the matrix dimensions mismatch.
147    ///
148    /// # Examples
149    /// ```
150    /// use qfall_math::integer::MatPolyOverZ;
151    /// use std::str::FromStr;
152    ///
153    /// let a: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
154    /// let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
155    ///
156    /// let c: MatPolyOverZ = a.sub_safe(&b).unwrap();
157    /// ```
158    /// # Errors
159    /// - Returns a [`MathError`] of type
160    ///   [`MathError::MismatchingMatrixDimension`] if the matrix dimensions
161    ///   mismatch.
162    pub fn sub_safe(&self, other: &Self) -> Result<MatPolyOverZ, MathError> {
163        if self.get_num_rows() != other.get_num_rows()
164            || self.get_num_columns() != other.get_num_columns()
165        {
166            return Err(MathError::MismatchingMatrixDimension(format!(
167                "Tried to subtract a '{}x{}' matrix and a '{}x{}' matrix.",
168                other.get_num_rows(),
169                other.get_num_columns(),
170                self.get_num_rows(),
171                self.get_num_columns()
172            )));
173        }
174        let mut out = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns());
175        unsafe {
176            fmpz_poly_mat_sub(&mut out.matrix, &self.matrix, &other.matrix);
177        }
178        Ok(out)
179    }
180
181    /// Implements subtraction for a [`MatPolyOverZ`] matrix with a [`MatPolynomialRingZq`] matrix.
182    ///
183    /// Parameters:
184    /// - `other`: specifies the value to subtract from `self`
185    ///
186    /// Returns the subtraction of `self` by `other` as a [`MatPolynomialRingZq`].
187    ///
188    /// # Examples
189    /// ```
190    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
191    /// use qfall_math::integer::MatPolyOverZ;
192    /// use std::str::FromStr;
193    ///
194    /// let mat_1 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
195    /// let mat_2 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
196    ///
197    /// let mat_3 = &mat_1.sub_mat_poly_ring_zq_safe(&mat_2).unwrap();
198    /// ```
199    ///
200    /// # Errors and Failures
201    /// - Returns a [`MathError`] of type
202    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
203    ///   and `other` do not match for multiplication.
204    pub fn sub_mat_poly_ring_zq_safe(
205        &self,
206        other: &MatPolynomialRingZq,
207    ) -> Result<MatPolynomialRingZq, MathError> {
208        let mut out =
209            MatPolynomialRingZq::new(self.get_num_rows(), self.get_num_columns(), other.get_mod());
210
211        out.matrix = self.sub_safe(&other.matrix)?;
212        out.reduce();
213
214        Ok(out)
215    }
216}
217
218#[cfg(test)]
219mod test_sub_assign {
220    use crate::integer::MatPolyOverZ;
221    use std::str::FromStr;
222
223    /// Ensure that `sub_assign` works for small numbers.
224    #[test]
225    fn correct_small() {
226        let mut a = MatPolyOverZ::identity(2, 2);
227        let b = MatPolyOverZ::from_str("[[1  -4, 2  -1 -5],[1  6, 1  1]]").unwrap();
228        let cmp = MatPolyOverZ::from_str("[[1  5, 2  1 5],[1  -6, 0]]").unwrap();
229
230        a -= b;
231
232        assert_eq!(cmp, a);
233    }
234
235    /// Ensure that `sub_assign` works for large numbers.
236    #[test]
237    fn correct_large() {
238        let mut a =
239            MatPolyOverZ::from_str(&format!("[[1  {}, 2  0 2],[1  {}, 0]]", i64::MAX, i64::MIN))
240                .unwrap();
241        let b =
242            MatPolyOverZ::from_str(&format!("[[1  -{}, 1  -1],[1  -6, 1  -3]]", i64::MAX)).unwrap();
243        let cmp = MatPolyOverZ::from_str(&format!(
244            "[[1  {}, 2  1 2],[1  {}, 1  3]]",
245            2 * (i64::MAX as u64),
246            i64::MIN + 6
247        ))
248        .unwrap();
249
250        a -= b;
251
252        assert_eq!(cmp, a);
253    }
254
255    /// Ensure that `sub_assign` works for different matrix dimensions.
256    #[test]
257    fn matrix_dimensions() {
258        let dimensions = [(3, 3), (5, 1), (1, 4)];
259
260        for (nr_rows, nr_cols) in dimensions {
261            let mut a = MatPolyOverZ::identity(nr_rows, nr_cols);
262            let b = MatPolyOverZ::new(nr_rows, nr_cols);
263
264            a -= b;
265
266            assert_eq!(MatPolyOverZ::identity(nr_rows, nr_cols), a);
267        }
268    }
269
270    /// Ensure that `sub_assign` is available for all types.
271    #[test]
272    fn availability() {
273        let mut a = MatPolyOverZ::new(2, 2);
274        let b = MatPolyOverZ::new(2, 2);
275
276        a -= &b;
277        a -= b;
278    }
279}
280
281#[cfg(test)]
282mod test_sub {
283    use super::MatPolyOverZ;
284    use std::str::FromStr;
285
286    /// Testing subtraction for two [`MatPolyOverZ`]
287    #[test]
288    fn sub() {
289        let a: MatPolyOverZ =
290            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
291        let b: MatPolyOverZ =
292            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
293        let c: MatPolyOverZ = a - b;
294        assert_eq!(
295            c,
296            MatPolyOverZ::from_str("[[1  42, 1  42, 2  18 -18],[3  16 12 38, 1  18, 1  25]]")
297                .unwrap()
298        );
299    }
300
301    /// Testing subtraction for two borrowed [`MatPolyOverZ`]
302    #[test]
303    fn sub_borrow() {
304        let a: MatPolyOverZ =
305            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
306        let b: MatPolyOverZ =
307            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
308        let c: MatPolyOverZ = &a - &b;
309        assert_eq!(
310            c,
311            MatPolyOverZ::from_str("[[1  42, 1  42, 2  18 -18],[3  16 12 38, 1  18, 1  25]]")
312                .unwrap()
313        );
314    }
315
316    /// Testing subtraction for borrowed [`MatPolyOverZ`] and [`MatPolyOverZ`]
317    #[test]
318    fn sub_first_borrowed() {
319        let a: MatPolyOverZ =
320            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
321        let b: MatPolyOverZ =
322            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
323        let c: MatPolyOverZ = &a - b;
324        assert_eq!(
325            c,
326            MatPolyOverZ::from_str("[[1  42, 1  42, 2  18 -18],[3  16 12 38, 1  18, 1  25]]")
327                .unwrap()
328        );
329    }
330
331    /// Testing subtraction for [`MatPolyOverZ`] and borrowed [`MatPolyOverZ`]
332    #[test]
333    fn sub_second_borrowed() {
334        let a: MatPolyOverZ =
335            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
336        let b: MatPolyOverZ =
337            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
338        let c: MatPolyOverZ = a - &b;
339        assert_eq!(
340            c,
341            MatPolyOverZ::from_str("[[1  42, 1  42, 2  18 -18],[3  16 12 38, 1  18, 1  25]]")
342                .unwrap()
343        );
344    }
345
346    /// Testing subtraction for large numbers
347    #[test]
348    fn sub_large_numbers() {
349        let a: MatPolyOverZ = MatPolyOverZ::from_str(&format!(
350            "[[1  {}, 2  1 {}],[2  -{} 7, 0]]",
351            i64::MAX,
352            i64::MIN,
353            u64::MAX
354        ))
355        .unwrap();
356        let b: MatPolyOverZ = MatPolyOverZ::from_str(&format!(
357            "[[1  {}, 2  1 {}],[2  {} 7, 0]]",
358            i64::MAX,
359            i64::MAX,
360            i64::MIN
361        ))
362        .unwrap();
363        let c: MatPolyOverZ = a - &b;
364        assert_eq!(
365            c,
366            MatPolyOverZ::from_str(&format!("[[0, 2  0 -{}],[1  -{}, 0]]", u64::MAX, i64::MAX))
367                .unwrap()
368        );
369    }
370
371    /// Testing sub_safe
372    #[test]
373    fn sub_safe() {
374        let a: MatPolyOverZ =
375            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
376        let b: MatPolyOverZ =
377            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
378        let c: MatPolyOverZ = a.sub_safe(&b).unwrap();
379        assert_eq!(
380            c,
381            MatPolyOverZ::from_str("[[1  42, 1  42, 2  18 -18],[3  16 12 38, 1  18, 1  25]]")
382                .unwrap()
383        );
384    }
385
386    /// Testing sub_safe throws error
387    #[test]
388    fn sub_safe_is_err() {
389        let a: MatPolyOverZ =
390            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
391        let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0],[3  1 12 4, 1  17]]").unwrap();
392        let c: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24]]").unwrap();
393        assert!(a.sub_safe(&b).is_err());
394        assert!(c.sub_safe(&b).is_err());
395    }
396}
397
398#[cfg(test)]
399mod test_mul_mat_poly_over_z {
400    use super::MatPolynomialRingZq;
401    use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
402    use std::str::FromStr;
403
404    const LARGE_PRIME: u64 = u64::MAX - 58;
405
406    /// Checks whether subtraction is available for other types.
407    #[test]
408    fn availability() {
409        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
410        let poly_mat = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
411        let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
412
413        let _ = &poly_mat - &poly_ring_mat;
414        let _ = &poly_mat - poly_ring_mat.clone();
415        let _ = poly_mat.clone() - &poly_ring_mat;
416        let _ = poly_mat.clone() - poly_ring_mat.clone();
417    }
418
419    /// Checks if subtraction works fine for squared matrices.
420    #[test]
421    fn square_correctness() {
422        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
423        let poly_mat_1 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
424        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
425        let poly_mat_2 = MatPolyOverZ::from_str("[[2  1 1, 1  42],[0, 2  1 2]]").unwrap();
426
427        let poly_ring_mat_3 = &poly_mat_2 - &poly_ring_mat_1;
428
429        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  -2 1 -1, 0],[0, 2  -16 2]]").unwrap();
430        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
431
432        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
433    }
434
435    /// Checks if subtraction works fine for large entries.
436    #[test]
437    fn large_entries() {
438        let modulus =
439            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
440        let poly_mat_1 = MatPolyOverZ::from_str(&format!("[[2  1 {}],[0]]", u64::MAX)).unwrap();
441        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
442        let poly_mat_2 = MatPolyOverZ::from_str(&format!("[[2  3 {}],[1  1]]", u64::MAX)).unwrap();
443
444        let poly_ring_mat_3 = &poly_mat_2 - &poly_ring_mat_1;
445
446        let poly_mat_cmp = MatPolyOverZ::from_str("[[2  2 0],[1  1]]").unwrap();
447        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
448
449        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
450    }
451
452    /// Checks if subtraction with incompatible matrix dimensions
453    /// throws an error as expected.
454    #[test]
455    fn errors() {
456        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
457        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
458        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
459        let poly_mat_2 = MatPolyOverZ::from_str("[[4  -1 0 1 1],[2  1 2]]").unwrap();
460
461        assert!((poly_mat_2.sub_mat_poly_ring_zq_safe(&poly_ring_mat_1)).is_err());
462    }
463
464    /// Checks if subtraction panics if dimensions mismatch.
465    #[test]
466    #[should_panic]
467    fn mul_panic() {
468        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
469        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  1],[2  1 2, 1  1]]").unwrap();
470        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
471        let poly_mat_2 = MatPolyOverZ::from_str("[[1  3],[2  1 2]]").unwrap();
472
473        let _ = &poly_mat_2 - &poly_ring_mat_1;
474    }
475}