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