qfall_math/integer_mod_q/mat_zq/arithmetic/
sub.rs

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