Skip to main content

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