Skip to main content

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