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