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