Skip to main content

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