qfall_math/integer/mat_z/arithmetic/
div.rs

1// Copyright © 2023 Marcel Luca Schmidt, Sven Moog
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 scalar multiplication for [`MatZ`] matrices.
10
11use super::super::MatZ;
12use crate::integer::Z;
13use crate::macros::arithmetics::{
14    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
15};
16use crate::rational::MatQ;
17use crate::traits::MatrixDimensions;
18use flint_sys::fmpq_mat::fmpq_mat_set_fmpz_mat_div_fmpz;
19use flint_sys::fmpz_mat::fmpz_mat_scalar_divexact_fmpz;
20use std::ops::Div;
21
22impl MatZ {
23    /// Implements division for a [`MatZ`] matrix by a [`Z`] integer.
24    ///
25    /// Parameters:
26    /// - `divisor`: specifies the divisor by which the matrix is divided
27    ///
28    /// Returns the quotient of `self` divided by `divisor` as a [`MatZ`].
29    ///
30    /// # Safety
31    /// The divisor MUST exactly divide each element in the matrix.
32    /// If this is not the case, the result can contain arbitrary values
33    /// which can depend on the location in memory.
34    ///
35    /// # Examples
36    /// ```
37    /// use qfall_math::integer::{MatZ, Z};
38    /// use std::str::FromStr;
39    ///
40    /// let mut mat = MatZ::from_str("[[3, 6],[9, 27]]").unwrap();
41    ///
42    /// let mat_z = unsafe { mat.div_exact(3) };
43    ///
44    /// assert_eq!("[[1, 2],[3, 9]]", mat_z.to_string());
45    /// ```
46    ///
47    /// # Panics ...
48    /// - if the divisor is `0`.
49    pub unsafe fn div_exact(mut self, divisor: impl Into<Z>) -> MatZ {
50        let divisor: Z = divisor.into();
51        assert!(!divisor.is_zero(), "Tried to divide {self} by zero.");
52
53        unsafe { fmpz_mat_scalar_divexact_fmpz(&mut self.matrix, &self.matrix, &divisor.value) };
54
55        self
56    }
57
58    /// Implements division for a [`MatZ`] matrix by a [`Z`] integer.
59    ///
60    /// Parameters:
61    /// - `divisor`: specifies the divisor by which the matrix is divided
62    ///
63    /// Returns the quotient of `self` divided by `divisor` as a [`MatZ`].
64    ///
65    /// # Safety
66    /// The divisor MUST exactly divide each element in the matrix.
67    /// If this is not the case, the result can contain arbitrary values
68    /// which can depend on the location in memory.
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer::{MatZ, Z};
73    /// use std::str::FromStr;
74    ///
75    /// let mat = MatZ::from_str("[[3, 6],[9, 27]]").unwrap();
76    ///
77    /// let mat_z = unsafe { mat.div_exact_ref(3) };
78    ///
79    /// assert_eq!("[[1, 2],[3, 9]]", mat_z.to_string());
80    /// ```
81    ///
82    /// # Panics ...
83    /// - if the divisor is `0`.
84    pub unsafe fn div_exact_ref(&self, divisor: impl Into<Z>) -> MatZ {
85        let divisor: Z = divisor.into();
86        assert!(!divisor.is_zero(), "Tried to divide {self} by zero.");
87
88        let mut out = MatZ::new(self.get_num_rows(), self.get_num_columns());
89        unsafe { fmpz_mat_scalar_divexact_fmpz(&mut out.matrix, &self.matrix, &divisor.value) };
90
91        out
92    }
93}
94
95impl Div<&Z> for &MatZ {
96    type Output = MatQ;
97    /// Implements the [`Div`] trait for a [`MatZ`] matrix by a [`Z`] integer.
98    /// [`Div`] is also implemented for borrowed values.
99    ///
100    /// Parameters:
101    /// - `divisor`: specifies the divisor by which the matrix is divided
102    ///
103    /// Returns the quotient of `self` divided by `divisor` as a [`MatQ`].
104    ///
105    /// # Examples
106    /// ```
107    /// use qfall_math::integer::{MatZ, Z};
108    /// use std::str::FromStr;
109    ///
110    /// let mat = MatZ::from_str("[[3, 5],[9, 22]]").unwrap();
111    /// let divisor = Z::from(3);
112    ///
113    /// let mat_q = &mat / &divisor;
114    ///
115    /// assert_eq!("[[1, 5/3],[3, 22/3]]", mat_q.to_string());
116    /// ```
117    ///
118    /// # Panics ...
119    /// - if the divisor is `0`.
120    fn div(self, divisor: &Z) -> Self::Output {
121        assert!(!divisor.is_zero(), "Tried to divide {self} by zero.");
122        let mut out = MatQ::new(self.get_num_rows(), self.get_num_columns());
123        unsafe {
124            fmpq_mat_set_fmpz_mat_div_fmpz(&mut out.matrix, &self.matrix, &divisor.value);
125        }
126        out
127    }
128}
129
130arithmetic_trait_borrowed_to_owned!(Div, div, MatZ, Z, MatQ);
131arithmetic_trait_mixed_borrowed_owned!(Div, div, MatZ, Z, MatQ);
132
133#[cfg(test)]
134mod test_div_exact {
135    use super::*;
136    use crate::integer_mod_q::Modulus;
137    use std::str::FromStr;
138
139    /// Tests that division is available for other types.
140    #[test]
141    #[allow(clippy::needless_borrows_for_generic_args)]
142    fn availability() {
143        let mat = MatZ::from_str("[[6, 3],[3, 6]]").unwrap();
144
145        let _ = unsafe { mat.div_exact_ref(3i64) };
146        let _ = unsafe { mat.div_exact_ref(3i32) };
147        let _ = unsafe { mat.div_exact_ref(3i16) };
148        let _ = unsafe { mat.div_exact_ref(3i8) };
149        let _ = unsafe { mat.div_exact_ref(3u64) };
150        let _ = unsafe { mat.div_exact_ref(3u32) };
151        let _ = unsafe { mat.div_exact_ref(3u16) };
152        let _ = unsafe { mat.div_exact_ref(3u8) };
153        let _ = unsafe { mat.div_exact_ref(Z::from(3)) };
154        let _ = unsafe { mat.div_exact_ref(Modulus::from(3)) };
155        let _ = unsafe { mat.div_exact_ref(&Z::from(3)) };
156        let _ = unsafe { mat.div_exact_ref(&Modulus::from(3)) };
157
158        let _ = unsafe { mat.clone().div_exact(3i64) };
159        let _ = unsafe { mat.clone().div_exact(3i32) };
160        let _ = unsafe { mat.clone().div_exact(3i16) };
161        let _ = unsafe { mat.clone().div_exact(3i8) };
162        let _ = unsafe { mat.clone().div_exact(3u64) };
163        let _ = unsafe { mat.clone().div_exact(3u32) };
164        let _ = unsafe { mat.clone().div_exact(3u16) };
165        let _ = unsafe { mat.clone().div_exact(3u8) };
166        let _ = unsafe { mat.clone().div_exact(Z::from(3)) };
167        let _ = unsafe { mat.clone().div_exact(Modulus::from(3)) };
168        let _ = unsafe { mat.clone().div_exact(&Z::from(3)) };
169        let _ = unsafe { mat.div_exact(&Modulus::from(3)) };
170    }
171
172    /// Checks if matrix division works correctly.
173    #[test]
174    fn division_correctness() {
175        let mat = MatZ::from_str("[[6, 3],[3, 6]]").unwrap();
176
177        let mat_divided_1 = unsafe { mat.div_exact_ref(3) };
178        let mat_divided_2 = unsafe { mat.div_exact(3) };
179
180        let mat_cmp = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
181        assert_eq!(mat_cmp, mat_divided_1);
182        assert_eq!(mat_cmp, mat_divided_2);
183    }
184
185    /// Checks if matrix division works for negative numbers.
186    #[test]
187    fn negative_correctness() {
188        let mat = MatZ::from_str("[[6, -3],[3, -6]]").unwrap();
189
190        let mat_divided_1 = unsafe { mat.div_exact_ref(3) };
191        let mat_divided_2 = unsafe { mat.div_exact_ref(-3) };
192
193        let mat_cmp_1 = MatZ::from_str("[[2, -1],[1, -2]]").unwrap();
194        let mat_cmp_2 = MatZ::from_str("[[-2, 1],[-1, 2]]").unwrap();
195        assert_eq!(mat_cmp_1, mat_divided_1);
196        assert_eq!(mat_cmp_2, mat_divided_2);
197    }
198
199    /// Checks if matrix division works fine for matrices of different dimensions.
200    #[test]
201    fn different_dimensions_correctness() {
202        let mat_1 = MatZ::from_str("[[-3],[0],[12]]").unwrap();
203        let mat_2 = MatZ::from_str("[[6, 15, 18],[3, -9, 3]]").unwrap();
204
205        let mat_cmp_1 = MatZ::from_str("[[-1],[0],[4]]").unwrap();
206        let mat_cmp_2 = MatZ::from_str("[[2, 5, 6],[1, -3, 1]]").unwrap();
207        assert_eq!(mat_cmp_1, unsafe { mat_1.div_exact_ref(3) });
208        assert_eq!(mat_cmp_2, unsafe { mat_2.div_exact_ref(3) });
209        assert_eq!(mat_cmp_1, unsafe { mat_1.div_exact(3) });
210        assert_eq!(mat_cmp_2, unsafe { mat_2.div_exact(3) });
211    }
212
213    /// Checks if matrix division works fine for large values.
214    #[test]
215    fn large_entries() {
216        let mat_1 =
217            MatZ::from_str(&format!("[[6],[{}],[{}]]", i64::MAX / 3, i64::MIN / 3)).unwrap();
218        let mat_2 = MatZ::from_str(&format!("[[{}]]", i64::MAX)).unwrap();
219        let mat_3 = MatZ::from_str(&format!("[[{}]]", i64::MIN)).unwrap();
220
221        let mat_cmp_1 =
222            MatZ::from_str(&format!("[[3],[{}],[{}]]", (i64::MAX / 6), (i64::MIN / 6))).unwrap();
223        let mat_cmp_2 = MatZ::from_str("[[1]]").unwrap();
224        assert_eq!(mat_cmp_1, unsafe { mat_1.div_exact_ref(2) });
225        assert_eq!(mat_cmp_2, unsafe { mat_2.div_exact_ref(i64::MAX) });
226        assert_eq!(mat_cmp_2, unsafe { mat_3.div_exact_ref(i64::MIN) });
227        assert_eq!(mat_cmp_1, unsafe { mat_1.div_exact(2) });
228        assert_eq!(mat_cmp_2, unsafe { mat_2.div_exact(i64::MAX) });
229        assert_eq!(mat_cmp_2, unsafe { mat_3.div_exact(i64::MIN) });
230    }
231
232    /// Checks if matrix division panics on division by 0.
233    #[test]
234    #[should_panic]
235    fn div_by_0_error() {
236        let mat = MatZ::from_str("[[6, 2],[3, 10]]").unwrap();
237
238        let _mat = unsafe { mat.div_exact(0) };
239    }
240
241    /// Checks if matrix division panics on division by 0.
242    #[test]
243    #[should_panic]
244    fn div_by_0_error_ref() {
245        let mat = MatZ::from_str("[[6, 2],[3, 10]]").unwrap();
246
247        let _mat = unsafe { mat.div_exact_ref(0) };
248    }
249}
250
251#[cfg(test)]
252mod test_div {
253    use super::*;
254    use std::str::FromStr;
255
256    /// Tests that division is available for other types.
257    #[test]
258    fn availability() {
259        let mat = MatZ::from_str("[[6, 5],[2, 6]]").unwrap();
260        let divisor = Z::from(3);
261
262        let _ = &mat / &divisor;
263        let _ = mat.clone() / &divisor;
264        let _ = &mat / divisor.clone();
265        let _ = mat / divisor;
266    }
267
268    /// Checks if matrix division works correctly.
269    #[test]
270    fn division_correctness() {
271        let mat = MatZ::from_str("[[6, 5],[2, 6]]").unwrap();
272        let divisor = Z::from(3);
273
274        let mat_q = &mat / &divisor;
275
276        let mat_cmp = MatQ::from_str("[[2, 5/3],[2/3, 2]]").unwrap();
277        assert_eq!(mat_cmp, mat_q);
278    }
279
280    /// Checks if matrix division works fine for matrices of different dimensions.
281    #[test]
282    fn different_dimensions_correctness() {
283        let mat_1 = MatZ::from_str("[[4],[0],[12]]").unwrap();
284        let mat_2 = MatZ::from_str("[[6, 15, 18],[3, 10, 3]]").unwrap();
285        let divisor = Z::from(3);
286
287        let mat_cmp_1 = MatQ::from_str("[[4/3],[0],[4]]").unwrap();
288        let mat_cmp_2 = MatQ::from_str("[[2, 5, 6],[1, 10/3, 1]]").unwrap();
289        assert_eq!(mat_cmp_1, mat_1 / &divisor);
290        assert_eq!(mat_cmp_2, mat_2 / divisor);
291    }
292
293    /// Checks if matrix division works fine for large values.
294    #[test]
295    fn large_entries() {
296        let mat_1 =
297            MatZ::from_str(&format!("[[6],[{}],[{}]]", i64::MAX / 3, i64::MIN / 3)).unwrap();
298        let mat_2 = MatZ::from_str(&format!("[[{}]]", i64::MAX)).unwrap();
299        let mat_3 = MatZ::from_str(&format!("[[{}]]", i64::MIN)).unwrap();
300        let divisor_1 = Z::from(2);
301        let divisor_2 = Z::from(i64::MAX);
302        let divisor_3 = Z::from(i64::MIN);
303
304        let mat_cmp_1 =
305            MatQ::from_str(&format!("[[3],[{}],[{}]]", (i64::MAX / 6), (i64::MIN / 6))).unwrap();
306        let mat_cmp_2 = MatQ::from_str("[[1]]").unwrap();
307        assert_eq!(mat_cmp_1, mat_1 / divisor_1);
308        assert_eq!(mat_cmp_2, mat_2 / divisor_2);
309        assert_eq!(mat_cmp_2, mat_3 / divisor_3);
310    }
311
312    /// Checks if matrix division panics on division by 0.
313    #[test]
314    #[should_panic]
315    fn div_by_0_error() {
316        let mat = MatZ::from_str("[[6, 2],[3, 10]]").unwrap();
317        let divisor = Z::ZERO;
318
319        let _mat = mat / divisor;
320    }
321
322    /// Checks if the doctest works.
323    #[test]
324    fn doctest_correct() {
325        let mat = MatZ::from_str("[[3, 5],[9, 22]]").unwrap();
326        let divisor = Z::from(3);
327
328        let mat_q = &mat / &divisor;
329
330        assert_eq!("[[1, 5/3],[3, 22/3]]", mat_q.to_string());
331    }
332}