qfall_math/integer/mat_z/arithmetic/
mul_scalar.rs

1// Copyright © 2023 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 scalar multiplication for [`MatZ`] matrices.
10
11use super::super::MatZ;
12use crate::integer::Z;
13use crate::integer_mod_q::{MatZq, Zq};
14use crate::macros::arithmetics::{
15    arithmetic_assign_between_types, arithmetic_assign_trait_borrowed_to_owned,
16    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
17    arithmetic_trait_reverse,
18};
19use crate::macros::for_others::implement_for_others;
20use crate::rational::{MatQ, Q};
21use crate::traits::MatrixDimensions;
22use flint_sys::fmpz_mat::{
23    fmpz_mat_scalar_mul_fmpz, fmpz_mat_scalar_mul_si, fmpz_mat_scalar_mul_ui,
24};
25use std::ops::{Mul, MulAssign};
26
27impl Mul<&Z> for &MatZ {
28    type Output = MatZ;
29    /// Implements the [`Mul`] trait for a [`MatZ`] matrix with a [`Z`] integer.
30    /// [`Mul`] is implemented for any combination of owned and borrowed values.
31    ///
32    /// Parameters:
33    /// - `scalar`: specifies the scalar by which the matrix is multiplied
34    ///
35    /// Returns the product of `self` and `scalar` as a [`MatZ`].
36    ///
37    /// # Examples
38    /// ```
39    /// use qfall_math::integer::MatZ;
40    /// use qfall_math::integer::Z;
41    /// use std::str::FromStr;
42    ///
43    /// let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
44    /// let integer = Z::from(3);
45    ///
46    /// let mat_2 = &mat_1 * &integer;
47    /// ```
48    fn mul(self, scalar: &Z) -> Self::Output {
49        let mut out = MatZ::new(self.get_num_rows(), self.get_num_columns());
50        unsafe {
51            fmpz_mat_scalar_mul_fmpz(&mut out.matrix, &self.matrix, &scalar.value);
52        }
53        out
54    }
55}
56
57arithmetic_trait_reverse!(Mul, mul, Z, MatZ, MatZ);
58
59arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, Z, MatZ);
60arithmetic_trait_borrowed_to_owned!(Mul, mul, Z, MatZ, MatZ);
61arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, Z, MatZ);
62arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Z, MatZ, MatZ);
63
64implement_for_others!(Z, MatZ, MatZ, Mul Scalar for i8 i16 i32 i64 u8 u16 u32 u64);
65
66impl Mul<&Q> for &MatZ {
67    type Output = MatQ;
68    /// Implements the [`Mul`] trait for a [`MatZ`] matrix with a [`Q`] rational.
69    /// [`Mul`] is implemented for any combination of owned and borrowed values.
70    ///
71    /// Parameters:
72    /// - `scalar`: specifies the scalar by which the matrix is multiplied
73    ///
74    /// Returns the product of `self` and `scalar` as a [`MatQ`].
75    ///
76    /// # Examples
77    /// ```
78    /// use qfall_math::integer::MatZ;
79    /// use qfall_math::rational::Q;
80    /// use std::str::FromStr;
81    ///
82    /// let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
83    /// let rational = Q::from((1,3));
84    ///
85    /// let mat_2 = &mat_1 * &rational;
86    /// ```
87    fn mul(self, scalar: &Q) -> Self::Output {
88        let out = MatQ::from(self);
89        out * scalar
90    }
91}
92
93arithmetic_trait_reverse!(Mul, mul, Q, MatZ, MatQ);
94
95arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, Q, MatQ);
96arithmetic_trait_borrowed_to_owned!(Mul, mul, Q, MatZ, MatQ);
97arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, Q, MatQ);
98arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Q, MatZ, MatQ);
99
100implement_for_others!(Q, MatZ, MatQ, Mul Scalar for f32 f64);
101
102impl Mul<&Zq> for &MatZ {
103    type Output = MatZq;
104    /// Implements the [`Mul`] trait for a [`MatZ`] matrix with a [`Zq`] representative of a residue class.
105    /// [`Mul`] is implemented for any combination of owned and borrowed values.
106    ///
107    /// Parameters:
108    /// - `scalar`: specifies the scalar by which the matrix is multiplied
109    ///
110    /// Returns the product of `self` and `scalar` as a [`MatZq`].
111    ///
112    /// # Examples
113    /// ```
114    /// use qfall_math::integer::MatZ;
115    /// use qfall_math::integer_mod_q::Zq;
116    /// use std::str::FromStr;
117    ///
118    /// let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
119    /// let zq = Zq::from((1,3));
120    ///
121    /// let mat_2 = &mat_1 * &zq;
122    /// ```
123    fn mul(self, scalar: &Zq) -> Self::Output {
124        let out = MatZq::from((self, scalar.get_mod()));
125        out * scalar
126    }
127}
128
129arithmetic_trait_reverse!(Mul, mul, Zq, MatZ, MatZq);
130
131arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, Zq, MatZq);
132arithmetic_trait_borrowed_to_owned!(Mul, mul, Zq, MatZ, MatZq);
133arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, Zq, MatZq);
134arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Zq, MatZ, MatZq);
135
136impl MulAssign<&Z> for MatZ {
137    /// Computes the scalar multiplication of `self` and `scalar` reusing
138    /// the memory of `self`.
139    ///
140    /// Parameters:
141    /// - `scalar`: specifies the value to multiply to `self`
142    ///
143    /// Returns the scalar of the matrix as a [`MatZ`].
144    ///
145    /// # Examples
146    /// ```
147    /// use qfall_math::integer::{Z,MatZ};
148    /// use std::str::FromStr;
149    ///
150    /// let mut a = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
151    /// let b = Z::from(2);
152    ///
153    /// a *= &b;
154    /// a *= b;
155    /// a *= 2;
156    /// a *= -2;
157    /// ```
158    fn mul_assign(&mut self, scalar: &Z) {
159        unsafe { fmpz_mat_scalar_mul_fmpz(&mut self.matrix, &self.matrix, &scalar.value) };
160    }
161}
162
163impl MulAssign<i64> for MatZ {
164    /// Documentation at [`MatZ::mul_assign`].
165    fn mul_assign(&mut self, scalar: i64) {
166        unsafe { fmpz_mat_scalar_mul_si(&mut self.matrix, &self.matrix, scalar) };
167    }
168}
169
170impl MulAssign<u64> for MatZ {
171    /// Documentation at [`MatZ::mul_assign`].
172    fn mul_assign(&mut self, scalar: u64) {
173        unsafe { fmpz_mat_scalar_mul_ui(&mut self.matrix, &self.matrix, scalar) };
174    }
175}
176
177arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, MatZ, Z);
178arithmetic_assign_between_types!(MulAssign, mul_assign, MatZ, i64, i32 i16 i8);
179arithmetic_assign_between_types!(MulAssign, mul_assign, MatZ, u64, u32 u16 u8);
180
181#[cfg(test)]
182mod test_mul {
183    use super::MatZ;
184    use crate::integer::Z;
185    use std::str::FromStr;
186
187    /// Checks if scalar multiplication works fine for both borrowed
188    #[test]
189    fn borrowed_correctness() {
190        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
191        let mat_2 = mat_1.clone();
192        let mat_3 = MatZ::from_str("[[6, 3],[3, 6]]").unwrap();
193        let integer = Z::from(3);
194
195        let mat_1 = &mat_1 * &integer;
196        let mat_2 = &integer * &mat_2;
197
198        assert_eq!(mat_3, mat_1);
199        assert_eq!(mat_3, mat_2);
200    }
201
202    /// Checks if scalar multiplication works fine for both owned
203    #[test]
204    fn owned_correctness() {
205        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
206        let mat_2 = mat_1.clone();
207        let mat_3 = MatZ::from_str("[[6, 3],[3, 6]]").unwrap();
208        let integer_1 = Z::from(3);
209        let integer_2 = Z::from(3);
210
211        let mat_1 = mat_1 * integer_1;
212        let mat_2 = integer_2 * mat_2;
213
214        assert_eq!(mat_3, mat_1);
215        assert_eq!(mat_3, mat_2);
216    }
217
218    /// Checks if scalar multiplication works fine for half owned/borrowed
219    #[test]
220    fn half_correctness() {
221        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
222        let mat_2 = mat_1.clone();
223        let mat_3 = mat_1.clone();
224        let mat_4 = mat_1.clone();
225        let mat_5 = MatZ::from_str("[[6, 3],[3, 6]]").unwrap();
226        let integer_1 = Z::from(3);
227        let integer_2 = Z::from(3);
228
229        let mat_1 = mat_1 * &integer_1;
230        let mat_2 = &integer_2 * mat_2;
231        let mat_3 = &mat_3 * integer_1;
232        let mat_4 = integer_2 * &mat_4;
233
234        assert_eq!(mat_5, mat_1);
235        assert_eq!(mat_5, mat_2);
236        assert_eq!(mat_5, mat_3);
237        assert_eq!(mat_5, mat_4);
238    }
239
240    /// Checks if scalar multiplication works fine for different scalar types
241    #[test]
242    #[allow(clippy::erasing_op)]
243    fn different_types() {
244        let mat_1 = MatZ::from_str("[[1],[0],[4]]").unwrap();
245        let mat_2 = MatZ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
246        let mat_3 = MatZ::from_str("[[2],[0],[8]]").unwrap();
247        let mat_4 = MatZ::from_str("[[0],[0],[0]]").unwrap();
248        let mat_5 = MatZ::from_str("[[-1],[0],[-4]]").unwrap();
249        let mat_6 = MatZ::from_str("[[6, 15, 18],[3, 9, 3]]").unwrap();
250
251        assert_eq!(mat_3, 2 * &mat_1);
252        assert_eq!(mat_4, 0 * &mat_1);
253        assert_eq!(mat_5, -1 * mat_1);
254        assert_eq!(mat_6, mat_2 * 3);
255    }
256
257    /// Checks if scalar multiplication works fine for matrices of different dimensions
258    #[test]
259    fn different_dimensions_correctness() {
260        let mat_1 = MatZ::from_str("[[1],[0],[4]]").unwrap();
261        let mat_2 = MatZ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
262        let mat_3 = MatZ::from_str("[[3],[0],[12]]").unwrap();
263        let mat_4 = MatZ::from_str("[[6, 15, 18],[3, 9, 3]]").unwrap();
264        let integer = Z::from(3);
265
266        assert_eq!(mat_3, &integer * mat_1);
267        assert_eq!(mat_4, integer * mat_2);
268    }
269
270    /// Checks if scalar multiplication works fine for large values
271    #[test]
272    fn large_entries() {
273        let mat_1 = MatZ::from_str(&format!("[[1],[{}],[4]]", i64::MAX)).unwrap();
274        let mat_2 = MatZ::from_str("[[3]]").unwrap();
275        let mat_3 = MatZ::from_str(&format!("[[3],[{}],[12]]", 3 * i64::MAX as i128)).unwrap();
276        let mat_4 = MatZ::from_str(&format!("[[{}]]", 3 * i64::MAX as i128)).unwrap();
277        let integer_1 = Z::from(3);
278        let integer_2 = Z::from(i64::MAX);
279
280        assert_eq!(mat_3, integer_1 * mat_1);
281        assert_eq!(mat_4, integer_2 * mat_2);
282    }
283
284    /// Checks if scalar multiplication is available for any integer type
285    #[test]
286    fn availability() {
287        let mat = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
288        let integer = Z::from(3);
289
290        let _ = &mat * 1u8;
291        let _ = &mat * 1u16;
292        let _ = &mat * 1u32;
293        let _ = &mat * 1u64;
294        let _ = &mat * 1i8;
295        let _ = &mat * 1i16;
296        let _ = &mat * 1i32;
297        let _ = &mat * 1i64;
298        let _ = &mat * &integer;
299        let _ = &mat * integer;
300    }
301}
302
303#[cfg(test)]
304mod test_mul_q {
305    use super::MatZ;
306    use crate::rational::{MatQ, Q};
307    use std::str::FromStr;
308
309    /// Checks if scalar multiplication works fine for both borrowed
310    #[test]
311    fn borrowed_correctness() {
312        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
313        let mat_2 = mat_1.clone();
314        let mat_3 = MatQ::from_str("[[2/3, 1/3],[1/3, 2/3]]").unwrap();
315        let rational = Q::from((1, 3));
316
317        let mat_1 = &mat_1 * &rational;
318        let mat_2 = &rational * &mat_2;
319
320        assert_eq!(mat_3, mat_1);
321        assert_eq!(mat_3, mat_2);
322    }
323
324    /// Checks if scalar multiplication works fine for both owned
325    #[test]
326    fn owned_correctness() {
327        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
328        let mat_2 = mat_1.clone();
329        let mat_3 = MatQ::from_str("[[2/3, 1/3],[1/3, 2/3]]").unwrap();
330        let rational = Q::from((1, 3));
331
332        let mat_1 = mat_1 * rational.clone();
333        let mat_2 = rational * mat_2;
334
335        assert_eq!(mat_3, mat_1);
336        assert_eq!(mat_3, mat_2);
337    }
338
339    /// Checks if scalar multiplication works fine for half owned/borrowed
340    #[test]
341    fn half_correctness() {
342        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
343        let mat_2 = mat_1.clone();
344        let mat_3 = mat_1.clone();
345        let mat_4 = mat_1.clone();
346        let mat_5 = MatQ::from_str("[[2/3, 1/3],[1/3, 2/3]]").unwrap();
347        let rational = Q::from((1, 3));
348
349        let mat_1 = mat_1 * &rational;
350        let mat_2 = &rational * mat_2;
351        let mat_3 = &mat_3 * rational.clone();
352        let mat_4 = rational * &mat_4;
353
354        assert_eq!(mat_5, mat_1);
355        assert_eq!(mat_5, mat_2);
356        assert_eq!(mat_5, mat_3);
357        assert_eq!(mat_5, mat_4);
358    }
359
360    /// Checks if scalar multiplication works fine for matrices of different dimensions
361    #[test]
362    fn different_dimensions_correctness() {
363        let mat_1 = MatZ::from_str("[[1],[0],[4]]").unwrap();
364        let mat_2 = MatZ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
365        let mat_3 = MatQ::from_str("[[1/3],[0],[4/3]]").unwrap();
366        let mat_4 = MatQ::from_str("[[2/3, 5/3, 6/3],[1/3, 1, 1/3]]").unwrap();
367        let integer = Q::from((1, 3));
368
369        assert_eq!(mat_3, &integer * mat_1);
370        assert_eq!(mat_4, integer * mat_2);
371    }
372
373    /// Checks if scalar multiplication works fine for large values
374    #[test]
375    fn large_entries() {
376        let mat_1 = MatZ::from_str(&format!("[[1],[{}],[4]]", i64::MAX)).unwrap();
377        let mat_2 = MatZ::from_str("[[3]]").unwrap();
378        let mat_3 = MatQ::from_str(&format!("[[1/3],[{}/3],[4/3]]", i64::MAX)).unwrap();
379        let mat_4 = MatQ::from_str(&format!("[[3/{}]]", i64::MAX)).unwrap();
380        let rational_1 = Q::from((1, 3));
381        let rational_2 = Q::from((1, i64::MAX));
382
383        assert_eq!(mat_3, rational_1 * mat_1);
384        assert_eq!(mat_4, rational_2 * mat_2);
385    }
386
387    /// Checks if scalar multiplication is available for any rational type
388    #[test]
389    fn availability() {
390        let mat = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
391        let rational = Q::from(3);
392
393        let _ = &mat * 1.0f32;
394        let _ = &mat * 1.0f64;
395        let _ = &mat * &rational;
396        let _ = &mat * rational;
397    }
398}
399
400#[cfg(test)]
401mod test_mul_zq {
402    use super::MatZ;
403    use crate::integer_mod_q::{MatZq, Zq};
404    use std::str::FromStr;
405
406    /// Checks if scalar multiplication works fine for both borrowed
407    #[test]
408    fn borrowed_correctness() {
409        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
410        let mat_2 = mat_1.clone();
411        let mat_3 = MatZq::from_str("[[1, 2],[2, 1]] mod 3").unwrap();
412        let zq = Zq::from((2, 3));
413
414        let mat_1 = &mat_1 * &zq;
415        let mat_2 = &zq * &mat_2;
416
417        assert_eq!(mat_3, mat_1);
418        assert_eq!(mat_3, mat_2);
419    }
420
421    /// Checks if scalar multiplication works fine for both owned
422    #[test]
423    fn owned_correctness() {
424        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
425        let mat_2 = mat_1.clone();
426        let mat_3 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
427        let zq = Zq::from((1, 3));
428
429        let mat_1 = mat_1 * zq.clone();
430        let mat_2 = zq * mat_2;
431
432        assert_eq!(mat_3, mat_1);
433        assert_eq!(mat_3, mat_2);
434    }
435
436    /// Checks if scalar multiplication works fine for half owned/borrowed
437    #[test]
438    fn half_correctness() {
439        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
440        let mat_2 = mat_1.clone();
441        let mat_3 = mat_1.clone();
442        let mat_4 = mat_1.clone();
443        let mat_5 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
444        let zq = Zq::from((1, 3));
445
446        let mat_1 = mat_1 * &zq;
447        let mat_2 = &zq * mat_2;
448        let mat_3 = &mat_3 * zq.clone();
449        let mat_4 = zq * &mat_4;
450
451        assert_eq!(mat_5, mat_1);
452        assert_eq!(mat_5, mat_2);
453        assert_eq!(mat_5, mat_3);
454        assert_eq!(mat_5, mat_4);
455    }
456
457    /// Checks if scalar multiplication works fine for matrices of different dimensions
458    #[test]
459    fn different_dimensions_correctness() {
460        let mat_1 = MatZ::from_str("[[1],[0],[4]]").unwrap();
461        let mat_2 = MatZ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
462        let mat_3 = MatZq::from_str("[[1],[0],[1]] mod 3").unwrap();
463        let mat_4 = MatZq::from_str("[[2, 2, 0],[1, 0, 1]] mod 3").unwrap();
464        let integer = Zq::from((1, 3));
465
466        assert_eq!(mat_3, &integer * mat_1);
467        assert_eq!(mat_4, integer * mat_2);
468    }
469
470    /// Checks if scalar multiplication is available for any rational type
471    #[test]
472    fn availability() {
473        let mat = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
474        let zq = Zq::from((1, 3));
475
476        let _ = &mat * &zq;
477        let _ = &mat * zq;
478    }
479}
480
481#[cfg(test)]
482mod test_mul_assign {
483    use crate::integer::{MatZ, Z};
484    use std::str::FromStr;
485
486    /// Ensure that `mul_assign` produces same output as normal multiplication.
487    #[test]
488    fn consistency() {
489        let mut a = MatZ::from_str("[[2, 1],[-1, 0]]").unwrap();
490        let b = i32::MAX;
491        let cmp = &a * b;
492
493        a *= b;
494
495        assert_eq!(cmp, a);
496    }
497
498    /// Ensure that `mul_assign` is available for all types.
499    #[test]
500    fn availability() {
501        let mut a = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
502        let b = Z::from(2);
503
504        a *= &b;
505        a *= b;
506        a *= 1_u8;
507        a *= 1_u16;
508        a *= 1_u32;
509        a *= 1_u64;
510        a *= 1_i8;
511        a *= 1_i16;
512        a *= 1_i32;
513        a *= 1_i64;
514    }
515}