qfall_math/integer/mat_poly_over_z/arithmetic/
add.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 [`Add`] trait for [`MatPolyOverZ`] values.
10
11use super::super::MatPolyOverZ;
12use crate::error::MathError;
13use crate::macros::arithmetics::{
14    arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
15    arithmetic_trait_mixed_borrowed_owned,
16};
17use crate::traits::MatrixDimensions;
18use flint_sys::fmpz_poly_mat::fmpz_poly_mat_add;
19use std::ops::{Add, AddAssign};
20
21impl AddAssign<&MatPolyOverZ> for MatPolyOverZ {
22    /// Computes the addition of `self` and `other` reusing
23    /// the memory of `self`.
24    ///
25    /// Parameters:
26    /// - `other`: specifies the value to add to `self`
27    ///
28    /// # Examples
29    /// ```
30    /// use qfall_math::integer::MatPolyOverZ;
31    /// let mut a = MatPolyOverZ::identity(2, 2);
32    /// let b = MatPolyOverZ::new(2, 2);
33    ///
34    /// a += &b;
35    /// a += b;
36    /// ```
37    ///
38    /// # Panics ...
39    /// - if the matrix dimensions mismatch.
40    fn add_assign(&mut self, other: &Self) {
41        if self.get_num_rows() != other.get_num_rows()
42            || self.get_num_columns() != other.get_num_columns()
43        {
44            panic!(
45                "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
46                self.get_num_rows(),
47                self.get_num_columns(),
48                other.get_num_rows(),
49                other.get_num_columns()
50            );
51        }
52
53        unsafe { fmpz_poly_mat_add(&mut self.matrix, &self.matrix, &other.matrix) };
54    }
55}
56
57arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, MatPolyOverZ, MatPolyOverZ);
58
59impl Add for &MatPolyOverZ {
60    type Output = MatPolyOverZ;
61    /// Implements the [`Add`] trait for two [`MatPolyOverZ`] values.
62    /// [`Add`] is implemented for any combination of [`MatPolyOverZ`] and borrowed [`MatPolyOverZ`].
63    ///
64    /// Parameters:
65    /// - `other`: specifies the value to add to `self`
66    ///
67    /// Returns the sum of both numbers as a [`MatPolyOverZ`].
68    ///
69    /// # Examples
70    /// ```
71    /// use qfall_math::integer::MatPolyOverZ;
72    /// use std::str::FromStr;
73    ///
74    /// let a: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
75    /// let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
76    ///
77    /// let c: MatPolyOverZ = &a + &b;
78    /// let d: MatPolyOverZ = a + b;
79    /// let e: MatPolyOverZ = &c + d;
80    /// let f: MatPolyOverZ = c + &e;
81    /// ```
82    ///
83    /// # Panics ...
84    /// - if the dimensions of both matrices mismatch.
85    fn add(self, other: Self) -> Self::Output {
86        self.add_safe(other).unwrap()
87    }
88}
89
90impl MatPolyOverZ {
91    /// Implements addition for two [`MatPolyOverZ`] matrices.
92    ///
93    ///
94    /// Parameters:
95    /// - `other`: specifies the value to add to `self`
96    ///
97    /// Returns the sum of both matrices as a [`MatPolyOverZ`] or an
98    /// error if the matrix dimensions mismatch.
99    ///
100    /// # Examples
101    /// ```
102    /// use qfall_math::integer::MatPolyOverZ;
103    /// use std::str::FromStr;
104    ///
105    /// let a: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
106    /// let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
107    ///
108    /// let c: MatPolyOverZ = a.add_safe(&b).unwrap();
109    /// ```
110    ///
111    /// # Errors and Failures
112    /// - Returns a [`MathError`] of type
113    ///   [`MathError::MismatchingMatrixDimension`] if the matrix dimensions
114    ///   mismatch.
115    pub fn add_safe(&self, other: &Self) -> Result<MatPolyOverZ, MathError> {
116        if self.get_num_rows() != other.get_num_rows()
117            || self.get_num_columns() != other.get_num_columns()
118        {
119            return Err(MathError::MismatchingMatrixDimension(format!(
120                "Tried to add a '{}x{}' matrix and a '{}x{}' matrix.",
121                self.get_num_rows(),
122                self.get_num_columns(),
123                other.get_num_rows(),
124                other.get_num_columns()
125            )));
126        }
127        let mut out = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns());
128        unsafe {
129            fmpz_poly_mat_add(&mut out.matrix, &self.matrix, &other.matrix);
130        }
131        Ok(out)
132    }
133}
134
135arithmetic_trait_borrowed_to_owned!(Add, add, MatPolyOverZ, MatPolyOverZ, MatPolyOverZ);
136arithmetic_trait_mixed_borrowed_owned!(Add, add, MatPolyOverZ, MatPolyOverZ, MatPolyOverZ);
137
138#[cfg(test)]
139mod test_add_assign {
140    use crate::integer::MatPolyOverZ;
141    use std::str::FromStr;
142
143    /// Ensure that `add_assign` works for small numbers.
144    #[test]
145    fn correct_small() {
146        let mut a = MatPolyOverZ::identity(2, 2);
147        let b = MatPolyOverZ::from_str("[[1  4, 2  1 5],[1  -6, 1  -1]]").unwrap();
148        let cmp = MatPolyOverZ::from_str("[[1  5, 2  1 5],[1  -6, 0]]").unwrap();
149
150        a += b;
151
152        assert_eq!(cmp, a);
153    }
154
155    /// Ensure that `add_assign` works for large numbers.
156    #[test]
157    fn correct_large() {
158        let mut a =
159            MatPolyOverZ::from_str(&format!("[[1  {}, 2  0 2],[1  {}, 0]]", i64::MAX, i64::MIN))
160                .unwrap();
161        let b = MatPolyOverZ::from_str(&format!("[[1  {}, 1  1],[1  6, 1  3]]", i64::MAX)).unwrap();
162        let cmp = MatPolyOverZ::from_str(&format!(
163            "[[1  {}, 2  1 2],[1  {}, 1  3]]",
164            2 * (i64::MAX as u64),
165            i64::MIN + 6
166        ))
167        .unwrap();
168
169        a += b;
170
171        assert_eq!(cmp, a);
172    }
173
174    /// Ensure that `add_assign` works for different matrix dimensions.
175    #[test]
176    fn matrix_dimensions() {
177        let dimensions = [(3, 3), (5, 1), (1, 4)];
178
179        for (nr_rows, nr_cols) in dimensions {
180            let mut a = MatPolyOverZ::new(nr_rows, nr_cols);
181            let b = MatPolyOverZ::identity(nr_rows, nr_cols);
182
183            a += b;
184
185            assert_eq!(MatPolyOverZ::identity(nr_rows, nr_cols), a);
186        }
187    }
188
189    /// Ensure that `add_assign` is available for all types.
190    #[test]
191    fn availability() {
192        let mut a = MatPolyOverZ::new(2, 2);
193        let b = MatPolyOverZ::new(2, 2);
194
195        a += &b;
196        a += b;
197    }
198}
199
200#[cfg(test)]
201mod test_add {
202    use super::MatPolyOverZ;
203    use std::str::FromStr;
204
205    /// Testing addition for two [`MatPolyOverZ`]
206    #[test]
207    fn add() {
208        let a: MatPolyOverZ =
209            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
210        let b: MatPolyOverZ =
211            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
212
213        let c: MatPolyOverZ = a + b;
214        assert_eq!(
215            c,
216            MatPolyOverZ::from_str("[[1  -42, 1  42, 2  66 66],[3  18 36 46, 1  16, 1  59]]")
217                .unwrap()
218        );
219    }
220
221    /// Testing addition for two borrowed [`MatPolyOverZ`]
222    #[test]
223    fn add_borrow() {
224        let a: MatPolyOverZ =
225            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
226        let b: MatPolyOverZ =
227            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
228
229        let c: MatPolyOverZ = &a + &b;
230        assert_eq!(
231            c,
232            MatPolyOverZ::from_str("[[1  -42, 1  42, 2  66 66],[3  18 36 46, 1  16, 1  59]]")
233                .unwrap()
234        );
235    }
236
237    /// Testing addition for borrowed [`MatPolyOverZ`] and [`MatPolyOverZ`]
238    #[test]
239    fn add_first_borrowed() {
240        let a: MatPolyOverZ =
241            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
242        let b: MatPolyOverZ =
243            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
244
245        let c: MatPolyOverZ = &a + b;
246        assert_eq!(
247            c,
248            MatPolyOverZ::from_str("[[1  -42, 1  42, 2  66 66],[3  18 36 46, 1  16, 1  59]]")
249                .unwrap()
250        );
251    }
252
253    /// Testing addition for [`MatPolyOverZ`] and borrowed [`MatPolyOverZ`]
254    #[test]
255    fn add_second_borrowed() {
256        let a: MatPolyOverZ =
257            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
258        let b: MatPolyOverZ =
259            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
260
261        let c: MatPolyOverZ = a + &b;
262        assert_eq!(
263            c,
264            MatPolyOverZ::from_str("[[1  -42, 1  42, 2  66 66],[3  18 36 46, 1  16, 1  59]]")
265                .unwrap()
266        );
267    }
268
269    /// Testing addition for large numbers
270    #[test]
271    fn add_large_numbers() {
272        let a: MatPolyOverZ = MatPolyOverZ::from_str(&format!(
273            "[[1  {}, 2  1 {}],[2  -{} 7, 0]]",
274            i64::MAX,
275            i64::MIN,
276            u64::MAX
277        ))
278        .unwrap();
279        let b: MatPolyOverZ = MatPolyOverZ::from_str(&format!(
280            "[[1  {}, 2  1 {}],[2  {} 7, 0]]",
281            i64::MAX,
282            i64::MIN + 1,
283            i64::MAX
284        ))
285        .unwrap();
286        let c: MatPolyOverZ = a + &b;
287        assert_eq!(
288            c,
289            MatPolyOverZ::from_str(&format!(
290                "[[1  {}, 2  2 -{}],[2  -{} 14, 0]]",
291                u64::MAX - 1,
292                u64::MAX,
293                (u64::MAX - 1) / 2 + 1
294            ))
295            .unwrap()
296        );
297    }
298
299    /// Testing add_safe
300    #[test]
301    fn add_safe() {
302        let a: MatPolyOverZ =
303            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
304        let b: MatPolyOverZ =
305            MatPolyOverZ::from_str("[[1  -42, 0, 2  24 42],[3  1 12 4, 1  -1, 1  17]]").unwrap();
306
307        let c: MatPolyOverZ = a.add_safe(&b).unwrap();
308        assert_eq!(
309            c,
310            MatPolyOverZ::from_str("[[1  -42, 1  42, 2  66 66],[3  18 36 46, 1  16, 1  59]]")
311                .unwrap()
312        );
313    }
314
315    /// Testing add_safe throws error
316    #[test]
317    fn add_safe_is_err() {
318        let a: MatPolyOverZ =
319            MatPolyOverZ::from_str("[[0, 1  42, 2  42 24],[3  17 24 42, 1  17, 1  42]]").unwrap();
320        let b: MatPolyOverZ = MatPolyOverZ::from_str("[[1  -42, 0],[3  1 12 4, 1  17]]").unwrap();
321        let c: MatPolyOverZ = MatPolyOverZ::from_str("[[0, 1  42, 2  42 24]]").unwrap();
322        assert!(a.add_safe(&b).is_err());
323        assert!(c.add_safe(&b).is_err());
324    }
325}