qfall_math/integer_mod_q/mat_polynomial_ring_zq/arithmetic/
mul.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 the [`Mul`] trait for [`MatPolynomialRingZq`] values.
10
11use super::super::MatPolynomialRingZq;
12use crate::error::MathError;
13use crate::integer::MatPolyOverZ;
14use crate::macros::arithmetics::{
15    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
16};
17use crate::traits::{CompareBase, MatrixDimensions};
18use std::ops::Mul;
19
20impl Mul for &MatPolynomialRingZq {
21    type Output = MatPolynomialRingZq;
22
23    /// Implements the [`Mul`] trait for two [`MatPolynomialRingZq`] values.
24    /// [`Mul`] is implemented for any combination of owned and borrowed [`MatPolynomialRingZq`].
25    ///
26    /// Parameters:
27    /// - `other`: specifies the value to multiply with `self`
28    ///
29    /// Returns the product of `self` and `other` as a [`MatPolynomialRingZq`].
30    ///
31    /// # Examples
32    /// ```
33    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
34    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
35    /// use qfall_math::integer::MatPolyOverZ;
36    /// use std::str::FromStr;
37    ///
38    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
39    /// let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
40    /// let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
41    /// let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
42    /// let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
43    ///
44    /// let poly_ring_mat_3: MatPolynomialRingZq = &poly_ring_mat_1 * &poly_ring_mat_2;
45    /// let poly_ring_mat_4: MatPolynomialRingZq = poly_ring_mat_1 * poly_ring_mat_2;
46    /// let poly_ring_mat_5: MatPolynomialRingZq = &poly_ring_mat_3 * poly_ring_mat_4;
47    /// let poly_ring_mat_6: MatPolynomialRingZq = poly_ring_mat_3 * &poly_ring_mat_5;
48    /// ```
49    ///
50    /// # Panics ...
51    /// - if the dimensions of `self` and `other` do not match for multiplication.
52    /// - if the moduli mismatch.
53    fn mul(self, other: Self) -> Self::Output {
54        self.mul_safe(other).unwrap()
55    }
56}
57
58arithmetic_trait_borrowed_to_owned!(
59    Mul,
60    mul,
61    MatPolynomialRingZq,
62    MatPolynomialRingZq,
63    MatPolynomialRingZq
64);
65arithmetic_trait_mixed_borrowed_owned!(
66    Mul,
67    mul,
68    MatPolynomialRingZq,
69    MatPolynomialRingZq,
70    MatPolynomialRingZq
71);
72
73impl Mul<&MatPolyOverZ> for &MatPolynomialRingZq {
74    type Output = MatPolynomialRingZq;
75    /// Implements the [`Mul`] trait for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
76    /// [`Mul`] is implemented for any combination of owned and borrowed values.
77    ///
78    /// Parameters:
79    /// - `other`: specifies the value to multiply with `self`
80    ///
81    /// Returns the product of `self` and `other` as a [`MatPolynomialRingZq`].
82    ///
83    /// # Examples
84    /// ```
85    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
86    /// use qfall_math::integer::MatPolyOverZ;
87    /// use std::str::FromStr;
88    ///
89    /// let mat_1 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
90    /// let mat_2 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
91    ///
92    /// let mat_3 = &mat_1 * &mat_2;
93    /// ```
94    ///
95    /// # Panics ...
96    /// - if the dimensions of `self` and `other` do not match for multiplication.
97    fn mul(self, other: &MatPolyOverZ) -> Self::Output {
98        self.mul_mat_poly_over_z_safe(other).unwrap()
99    }
100}
101
102arithmetic_trait_borrowed_to_owned!(
103    Mul,
104    mul,
105    MatPolynomialRingZq,
106    MatPolyOverZ,
107    MatPolynomialRingZq
108);
109arithmetic_trait_mixed_borrowed_owned!(
110    Mul,
111    mul,
112    MatPolynomialRingZq,
113    MatPolyOverZ,
114    MatPolynomialRingZq
115);
116
117impl MatPolynomialRingZq {
118    /// Implements multiplication for two [`MatPolynomialRingZq`] values.
119    ///
120    /// Parameters:
121    /// - `other`: specifies the value to multiply with `self`
122    ///
123    /// Returns the product of `self` and `other` as a [`MatPolynomialRingZq`]
124    /// or an error if the dimensions of `self` and `other` do not match for multiplication
125    /// or the moduli mismatch.
126    ///
127    /// # Examples
128    /// ```
129    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
130    /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq;
131    /// use qfall_math::integer::MatPolyOverZ;
132    /// use std::str::FromStr;
133    ///
134    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
135    /// let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
136    /// let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
137    /// let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
138    /// let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
139    ///
140    /// let poly_ring_mat_3: MatPolynomialRingZq = poly_ring_mat_1.mul_safe(&poly_ring_mat_2).unwrap();
141    /// ```
142    ///
143    /// # Errors and Failures
144    /// - Returns a [`MathError`] of type
145    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
146    ///   and `other` do not match for multiplication.
147    /// - Returns a [`MathError`] of type
148    ///   [`MathError::MismatchingModulus`] if the moduli mismatch.
149    pub fn mul_safe(&self, other: &Self) -> Result<Self, MathError> {
150        if !self.compare_base(other) {
151            return Err(self.call_compare_base_error(other).unwrap());
152        }
153
154        // We create a new `MatPolyOverZ` struct inside `mul_safe`.
155        // Thus, we can spare the cloning operation in `MatPolynomialRingZq::from`
156        let mut new = MatPolynomialRingZq {
157            matrix: self.matrix.mul_safe(&other.matrix)?,
158            modulus: self.modulus.clone(),
159        };
160
161        new.reduce();
162
163        Ok(new)
164    }
165
166    /// Implements multiplication for a [`MatPolynomialRingZq`] matrix with a [`MatPolyOverZ`] matrix.
167    ///
168    /// Parameters:
169    /// - `other`: specifies the value to multiply with `self`
170    ///
171    /// Returns the product of `self` and `other` as a [`MatPolynomialRingZq`].
172    ///
173    /// # Examples
174    /// ```
175    /// use qfall_math::integer_mod_q::MatPolynomialRingZq;
176    /// use qfall_math::integer::MatPolyOverZ;
177    /// use std::str::FromStr;
178    ///
179    /// let mat_1 = MatPolynomialRingZq::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]] / 3  1 2 3 mod 17").unwrap();
180    /// let mat_2 = MatPolyOverZ::from_str("[[2  1 42, 1  17],[1  8, 2  5 6]]").unwrap();
181    ///
182    /// let mat_3 = &mat_1.mul_mat_poly_over_z_safe(&mat_2).unwrap();
183    /// ```
184    ///
185    /// # Errors and Failures
186    /// - Returns a [`MathError`] of type
187    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
188    ///   and `other` do not match for multiplication.
189    pub fn mul_mat_poly_over_z_safe(&self, other: &MatPolyOverZ) -> Result<Self, MathError> {
190        let mut out =
191            MatPolynomialRingZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod());
192
193        out.matrix = self.matrix.mul_safe(other)?;
194        out.reduce();
195
196        Ok(out)
197    }
198}
199
200#[cfg(test)]
201mod test_mul {
202    use super::MatPolynomialRingZq;
203    use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
204    use std::str::FromStr;
205
206    const LARGE_PRIME: u64 = u64::MAX - 58;
207
208    /// Checks if matrix multiplication works fine for squared matrices.
209    #[test]
210    fn square_correctness() {
211        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
212        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
213        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
214        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
215        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
216
217        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_ring_mat_2;
218
219        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  11 16 1, 3  1 0 8],[0, 0]]").unwrap();
220        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
221
222        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
223    }
224
225    /// Checks if matrix multiplication works fine for matrices of different dimensions.
226    #[test]
227    fn different_dimensions_correctness() {
228        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
229        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
230        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
231        let poly_mat_2 = MatPolyOverZ::from_str("[[1  42],[1  17]]").unwrap();
232        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
233
234        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_ring_mat_2;
235
236        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 8],[0]]").unwrap();
237        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
238
239        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
240    }
241
242    /// Checks if matrix multiplication works fine for large entries.
243    #[test]
244    fn large_entries() {
245        let modulus =
246            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
247        let poly_mat_1 =
248            MatPolyOverZ::from_str(&format!("[[2  3 {}, 1  15],[1  1, 0]]", u64::MAX)).unwrap();
249        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
250        let poly_mat_2 = MatPolyOverZ::from_str(&format!("[[2  1 {}],[0]]", u64::MAX)).unwrap();
251        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus));
252
253        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_ring_mat_2;
254
255        let poly_mat_cmp = MatPolyOverZ::from_str(&format!(
256            "[[3  3 {} {}],[2  1 {}]]",
257            u128::from(u64::MAX) * 4,
258            u128::from(u64::MAX) * u128::from(u64::MAX),
259            u64::MAX
260        ))
261        .unwrap();
262        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
263
264        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
265    }
266
267    /// Checks if matrix multiplication with incompatible matrix dimensions
268    /// or mismatch moduli throws an error as expected.
269    #[test]
270    fn errors() {
271        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
272        let modulus_2 = ModulusPolynomialRingZq::from_str("4  1 0 0 2 mod 17").unwrap();
273
274        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
275        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
276        let poly_mat_2 =
277            MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17],[1  24, 0]]").unwrap();
278        let poly_ring_mat_2 = MatPolynomialRingZq::from((&poly_mat_2, &modulus_1));
279        let poly_mat_3 = MatPolyOverZ::from_str("[[3  11 16 1, 3  1 0 8],[0, 0]]").unwrap();
280        let poly_ring_mat_3 = MatPolynomialRingZq::from((&poly_mat_3, &modulus_2));
281
282        assert!((poly_ring_mat_1.mul_safe(&poly_ring_mat_2)).is_err());
283        assert!((poly_ring_mat_1.mul_safe(&poly_ring_mat_3)).is_err());
284    }
285}
286
287#[cfg(test)]
288mod test_mul_mat_poly_over_z {
289    use super::MatPolynomialRingZq;
290    use crate::{integer::MatPolyOverZ, integer_mod_q::ModulusPolynomialRingZq};
291    use std::str::FromStr;
292
293    const LARGE_PRIME: u64 = u64::MAX - 58;
294
295    /// Checks whether multiplication is available for other types.
296    #[test]
297    fn availability() {
298        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
299        let poly_mat = MatPolyOverZ::from_str("[[3  0 1 1, 1  3],[0, 2  1 2]]").unwrap();
300        let poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
301
302        let _ = &poly_ring_mat * &poly_mat;
303        let _ = &poly_ring_mat * poly_mat.clone();
304        let _ = poly_ring_mat.clone() * &poly_mat;
305        let _ = poly_ring_mat * poly_mat;
306    }
307
308    /// Checks if matrix multiplication works fine for squared matrices.
309    #[test]
310    fn square_correctness() {
311        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
312        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
313        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
314        let poly_mat_2 = MatPolyOverZ::from_str("[[3  3 0 1, 1  42],[0, 1  17]]").unwrap();
315
316        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_mat_2;
317
318        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  11 16 1, 3  1 0 8],[0, 0]]").unwrap();
319        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
320
321        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
322    }
323
324    /// Checks if matrix multiplication works fine for matrices of different dimensions.
325    #[test]
326    fn different_dimensions_correctness() {
327        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
328        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1, 1  42],[0, 2  1 2]]").unwrap();
329        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
330        let poly_mat_2 = MatPolyOverZ::from_str("[[1  42],[1  17]]").unwrap();
331
332        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_mat_2;
333
334        let poly_mat_cmp = MatPolyOverZ::from_str("[[3  1 0 8],[0]]").unwrap();
335        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
336
337        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
338    }
339
340    /// Checks if matrix multiplication works fine for large entries.
341    #[test]
342    fn large_entries() {
343        let modulus =
344            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {LARGE_PRIME}")).unwrap();
345        let poly_mat_1 =
346            MatPolyOverZ::from_str(&format!("[[2  3 {}, 1  15],[1  1, 0]]", u64::MAX)).unwrap();
347        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus));
348        let poly_mat_2 = MatPolyOverZ::from_str(&format!("[[2  1 {}],[0]]", u64::MAX)).unwrap();
349
350        let poly_ring_mat_3 = &poly_ring_mat_1 * &poly_mat_2;
351
352        let poly_mat_cmp = MatPolyOverZ::from_str(&format!(
353            "[[3  3 {} {}],[2  1 {}]]",
354            u128::from(u64::MAX) * 4,
355            u128::from(u64::MAX) * u128::from(u64::MAX),
356            u64::MAX
357        ))
358        .unwrap();
359        let poly_ring_mat_cmp = MatPolynomialRingZq::from((&poly_mat_cmp, &modulus));
360
361        assert_eq!(poly_ring_mat_cmp, poly_ring_mat_3);
362    }
363
364    /// Checks if matrix multiplication with incompatible matrix dimensions
365    /// throws an error as expected.
366    #[test]
367    fn errors() {
368        let modulus_1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
369        let poly_mat_1 = MatPolyOverZ::from_str("[[4  -1 0 1 1],[2  1 2]]").unwrap();
370        let poly_ring_mat_1 = MatPolynomialRingZq::from((&poly_mat_1, &modulus_1));
371
372        assert!((poly_ring_mat_1.mul_mat_poly_over_z_safe(&poly_mat_1)).is_err());
373    }
374
375    /// Checks if multiplication panics if dimensions mismatch.
376    #[test]
377    #[should_panic]
378    fn mul_panic() {
379        let modulus1 = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
380        let poly_mat1 = MatPolyOverZ::from_str("[[1  3],[2  1 2]]").unwrap();
381        let poly_ring_mat1 = MatPolynomialRingZq::from((&poly_mat1, &modulus1));
382
383        let _ = &poly_ring_mat1 * &poly_mat1;
384    }
385}