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