qfall_math/integer_mod_q/poly_over_zq/arithmetic/
mul.rs

1// Copyright © 2023 Phil Milewski, 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 [`PolyOverZq`] values.
10
11use super::super::PolyOverZq;
12use crate::{
13    error::MathError,
14    integer::PolyOverZ,
15    macros::arithmetics::{
16        arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
17        arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
18    },
19    traits::CompareBase,
20};
21use core::panic;
22use flint_sys::fmpz_mod_poly::fmpz_mod_poly_mul;
23use std::{
24    ops::{Mul, MulAssign},
25    str::FromStr,
26};
27
28impl MulAssign<&PolyOverZq> for PolyOverZq {
29    /// Computes the multiplication of `self` and `other` reusing
30    /// the memory of `self`.
31    /// [`MulAssign`] can be used on [`PolyOverZq`] in combination with
32    /// [`PolyOverZq`] and [`PolyOverZ`].
33    ///
34    /// Parameters:
35    /// - `other`: specifies the polynomial to multiply to `self`
36    ///
37    /// Returns the product of both polynomials modulo `q` as a [`PolyOverZq`].
38    ///
39    /// # Examples
40    /// ```
41    /// use qfall_math::{integer_mod_q::PolyOverZq, integer::PolyOverZ};
42    /// use std::str::FromStr;
43    ///
44    /// let mut a = PolyOverZq::from_str("3  1 2 3 mod 7").unwrap();
45    /// let b = PolyOverZq::from_str("5  1 2 -3 0 4 mod 7").unwrap();
46    /// let c = PolyOverZ::from_str("4  -1 2 5 3").unwrap();
47    ///
48    /// a *= &b;
49    /// a *= b;
50    /// a *= &c;
51    /// a *= c;
52    /// ```
53    ///
54    /// # Panics ...
55    /// - if the moduli of both [`PolyOverZq`] mismatch.
56    fn mul_assign(&mut self, other: &Self) {
57        if !self.compare_base(other) {
58            panic!("{}", self.call_compare_base_error(other).unwrap());
59        }
60
61        unsafe {
62            fmpz_mod_poly_mul(
63                &mut self.poly,
64                &self.poly,
65                &other.poly,
66                self.modulus.get_fmpz_mod_ctx_struct(),
67            )
68        };
69    }
70}
71impl MulAssign<&PolyOverZ> for PolyOverZq {
72    /// Documentation at [`PolyOverZq::mul_assign`].
73    fn mul_assign(&mut self, other: &PolyOverZ) {
74        let other = PolyOverZq::from((other, self.get_mod()));
75
76        self.mul_assign(&other);
77    }
78}
79
80arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, PolyOverZq, PolyOverZq);
81arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, PolyOverZq, PolyOverZ);
82
83impl Mul for &PolyOverZq {
84    type Output = PolyOverZq;
85    /// Implements the [`Mul`] trait for two [`PolyOverZq`] values.
86    /// [`Mul`] is implemented for any combination of [`PolyOverZq`] and borrowed [`PolyOverZq`].
87    ///
88    /// Parameters:
89    /// - `other`: specifies the polynomial to multiply with `self`
90    ///
91    /// Returns the product of both polynomials as a [`PolyOverZq`].
92    ///
93    /// # Examples
94    /// ```
95    /// use qfall_math::integer_mod_q::PolyOverZq;
96    /// use std::str::FromStr;
97    ///
98    /// let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
99    /// let b: PolyOverZq = PolyOverZq::from_str("3  5 1 1 mod 7").unwrap();
100    ///
101    /// let c: PolyOverZq = &a * &b;
102    /// let d: PolyOverZq = a * b;
103    /// let e: PolyOverZq = &c * d;
104    /// let f: PolyOverZq = c * &e;
105    /// ```
106    ///
107    /// # Panics ...
108    /// - if the moduli of both [`PolyOverZq`] mismatch.
109    fn mul(self, other: Self) -> Self::Output {
110        self.mul_safe(other).unwrap()
111    }
112}
113
114arithmetic_trait_borrowed_to_owned!(Mul, mul, PolyOverZq, PolyOverZq, PolyOverZq);
115arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolyOverZq, PolyOverZq, PolyOverZq);
116
117impl Mul<&PolyOverZ> for &PolyOverZq {
118    type Output = PolyOverZq;
119    /// Implements the [`Mul`] trait for [`PolyOverZq`] and [`PolyOverZ`].
120    /// [`Mul`] is implemented for any combination of owned and borrowed values.
121    ///
122    /// Parameters:
123    /// - `other`: specifies the polynomial to multiply to `self`
124    ///
125    /// Returns the product of both polynomials as a [`PolyOverZq`].
126    ///
127    /// # Examples
128    /// ```
129    /// use qfall_math::integer_mod_q::PolyOverZq;
130    /// use qfall_math::integer::PolyOverZ;
131    /// use std::str::FromStr;
132    ///
133    /// let a = PolyOverZq::from_str("4  -1 0 1 1 mod 17").unwrap();
134    /// let b = PolyOverZ::from_str("4  2 0 3 1").unwrap();
135    ///
136    /// let c: PolyOverZq = &a * &b;
137    /// ```
138    fn mul(self, other: &PolyOverZ) -> Self::Output {
139        let mut out = PolyOverZq::from(&self.modulus);
140        unsafe {
141            fmpz_mod_poly_mul(
142                &mut out.poly,
143                &self.poly,
144                &PolyOverZq::from((other, &self.modulus)).poly,
145                self.modulus.get_fmpz_mod_ctx_struct(),
146            );
147        }
148        out
149    }
150}
151
152arithmetic_trait_reverse!(Mul, mul, PolyOverZ, PolyOverZq, PolyOverZq);
153
154arithmetic_trait_borrowed_to_owned!(Mul, mul, PolyOverZq, PolyOverZ, PolyOverZq);
155arithmetic_trait_borrowed_to_owned!(Mul, mul, PolyOverZ, PolyOverZq, PolyOverZq);
156arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolyOverZq, PolyOverZ, PolyOverZq);
157arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolyOverZ, PolyOverZq, PolyOverZq);
158
159impl PolyOverZq {
160    /// Implements multiplication for two [`PolyOverZq`] values.
161    ///
162    /// Parameters:
163    /// - `other`: specifies the polynomial to multiply to `self`
164    ///
165    /// Returns the product of both polynomials as a [`PolyOverZq`] or an error if the moduli
166    /// mismatch.
167    ///
168    /// # Examples
169    /// ```
170    /// use qfall_math::integer_mod_q::PolyOverZq;
171    /// use std::str::FromStr;
172    ///
173    /// let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
174    /// let b: PolyOverZq = PolyOverZq::from_str("3  5 1 1 mod 7").unwrap();
175    ///
176    /// let c: PolyOverZq = a.mul_safe(&b).unwrap();
177    /// ```
178    /// # Errors and Failures
179    /// - Returns a [`MathError`] of type [`MathError::MismatchingModulus`] if the moduli of
180    ///   both [`PolyOverZq`] mismatch.
181    pub fn mul_safe(&self, other: &Self) -> Result<PolyOverZq, MathError> {
182        if !self.compare_base(other) {
183            return Err(self.call_compare_base_error(other).unwrap());
184        }
185        let mut out = PolyOverZq::from_str(&format!("0 mod {}", self.modulus)).unwrap();
186        unsafe {
187            fmpz_mod_poly_mul(
188                &mut out.poly,
189                &self.poly,
190                &other.poly,
191                self.modulus.get_fmpz_mod_ctx_struct(),
192            );
193        }
194        Ok(out)
195    }
196}
197
198#[cfg(test)]
199mod test_mul_assign {
200    use super::PolyOverZq;
201    use crate::integer::PolyOverZ;
202    use std::str::FromStr;
203
204    /// Ensure that `mul_assign` works for small numbers.
205    #[test]
206    fn correct_small() {
207        let mut a = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
208        let b = PolyOverZq::from_str("2  2 4 mod 7").unwrap();
209
210        a *= b;
211
212        assert_eq!(a, PolyOverZq::from_str("4  4 2 4 4 mod 7").unwrap());
213    }
214
215    /// Ensure that `mul_assign` works for large numbers.
216    #[test]
217    fn correct_large() {
218        let mut a = PolyOverZq::from_str(&format!(
219            "2  {} {} mod {}",
220            u64::MAX,
221            i64::MAX,
222            u64::MAX - 58
223        ))
224        .unwrap();
225        let b = PolyOverZq::from_str(&format!(
226            "2  {} {} mod {}",
227            i64::MAX,
228            i64::MIN,
229            u64::MAX - 58
230        ))
231        .unwrap();
232
233        a *= b;
234
235        assert_eq!(
236            a,
237            PolyOverZq::from_str(&format!(
238                "3  {} {} {} mod {}",
239                i128::from(i64::MAX) * 58,
240                i128::from(i64::MIN) * 58 + i128::from(i64::MAX) * i128::from(i64::MAX),
241                i128::from(i64::MAX) * i128::from(i64::MIN),
242                u64::MAX - 58
243            ))
244            .unwrap()
245        );
246    }
247
248    /// Ensure that `mul_assign` is available for all types.
249    #[test]
250    fn availability() {
251        let mut a = PolyOverZq::from_str("3  1 2 -3 mod 5").unwrap();
252        let b = PolyOverZq::from_str("3  -1 -2 3 mod 5").unwrap();
253        let c = PolyOverZ::from_str("2  -2 2").unwrap();
254
255        a *= &b;
256        a *= b;
257        a *= &c;
258        a *= c;
259    }
260
261    /// Ensures that mismatching moduli result in a panic.
262    #[test]
263    #[should_panic]
264    fn mismatching_moduli() {
265        let mut a: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
266        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 8").unwrap();
267
268        a *= b;
269    }
270}
271
272#[cfg(test)]
273mod test_mul {
274    use super::PolyOverZq;
275    use std::str::FromStr;
276
277    /// Testing multiplication for two [`PolyOverZq`]
278    #[test]
279    fn mul() {
280        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
281        let b: PolyOverZq = PolyOverZq::from_str("2  2 4 mod 7").unwrap();
282        let c: PolyOverZq = a * b;
283        assert_eq!(c, PolyOverZq::from_str("4  4 2 4 4 mod 7").unwrap());
284    }
285
286    /// Testing multiplication for two borrowed [`PolyOverZq`]
287    #[test]
288    fn mul_borrow() {
289        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
290        let b: PolyOverZq = PolyOverZq::from_str("2  2 4 mod 7").unwrap();
291        let c: PolyOverZq = &a * &b;
292        assert_eq!(c, PolyOverZq::from_str("4  4 2 4 4 mod 7").unwrap());
293    }
294
295    /// Testing multiplication for borrowed [`PolyOverZq`] and [`PolyOverZq`]
296    #[test]
297    fn mul_first_borrowed() {
298        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
299        let b: PolyOverZq = PolyOverZq::from_str("2  2 4 mod 7").unwrap();
300        let c: PolyOverZq = &a * b;
301        assert_eq!(c, PolyOverZq::from_str("4  4 2 4 4 mod 7").unwrap());
302    }
303
304    /// Testing multiplication for [`PolyOverZq`] and borrowed [`PolyOverZq`]
305    #[test]
306    fn mul_second_borrowed() {
307        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
308        let b: PolyOverZq = PolyOverZq::from_str("2  2 4 mod 7").unwrap();
309        let c: PolyOverZq = a * &b;
310        assert_eq!(c, PolyOverZq::from_str("4  4 2 4 4 mod 7").unwrap());
311    }
312
313    /// Testing multiplication for [`PolyOverZq`] and a constant [`PolyOverZq`]
314    #[test]
315    fn mul_constant() {
316        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
317        let b: PolyOverZq = PolyOverZq::from_str("1  2 mod 7").unwrap();
318        let c: PolyOverZq = &a * b;
319        assert_eq!(c, PolyOverZq::from_str("3  4 1 2 mod 7").unwrap());
320        assert_eq!(
321            a * PolyOverZq::from_str("0 mod 7").unwrap(),
322            PolyOverZq::from_str("0 mod 7").unwrap()
323        );
324    }
325
326    /// Testing multiplication for large [`PolyOverZq`]
327    #[test]
328    fn mul_large_numbers() {
329        let a: PolyOverZq = PolyOverZq::from_str(&format!(
330            "2  {} {} mod {}",
331            u64::MAX,
332            i64::MAX,
333            u64::MAX - 58
334        ))
335        .unwrap();
336        let b: PolyOverZq = PolyOverZq::from_str(&format!(
337            "2  {} {} mod {}",
338            i64::MAX,
339            i64::MIN,
340            u64::MAX - 58
341        ))
342        .unwrap();
343        let c: PolyOverZq = a * &b;
344        assert_eq!(
345            c,
346            PolyOverZq::from_str(&format!(
347                "3  {} {} {} mod {}",
348                i128::from(i64::MAX) * 58,
349                i128::from(i64::MIN) * 58 + i128::from(i64::MAX) * i128::from(i64::MAX),
350                i128::from(i64::MAX) * i128::from(i64::MIN),
351                u64::MAX - 58
352            ))
353            .unwrap()
354        );
355    }
356
357    /// Testing multiplication for [`PolyOverZq`] with different moduli does not work
358    #[test]
359    #[should_panic]
360    fn mul_mismatching_modulus() {
361        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 8").unwrap();
362        let b: PolyOverZq = PolyOverZq::from_str("2  -5 4 mod 7").unwrap();
363        let _c: PolyOverZq = a * b;
364    }
365
366    /// Testing whether mul_safe throws an error for mismatching moduli
367    #[test]
368    fn mul_safe_is_err() {
369        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 9").unwrap();
370        let b: PolyOverZq = PolyOverZq::from_str("2  -5 4 mod 7").unwrap();
371        assert!(&a.mul_safe(&b).is_err());
372    }
373}
374
375#[cfg(test)]
376mod test_mul_poly_over_z {
377    use super::PolyOverZq;
378    use crate::integer::PolyOverZ;
379    use std::str::FromStr;
380
381    /// Checks if polynomial multiplication works fine for both borrowed
382    #[test]
383    fn borrowed_correctness() {
384        let poly_1 = PolyOverZq::from_str(&format!("1  {} mod {}", i64::MAX, u64::MAX)).unwrap();
385        let poly_2 = PolyOverZ::from_str("2  1 2").unwrap();
386        let poly_cmp = PolyOverZq::from_str(&format!(
387            "2  {} {} mod {}",
388            i64::MAX,
389            i64::MAX as u64 * 2,
390            u64::MAX
391        ))
392        .unwrap();
393
394        let poly_1 = &poly_1 * &poly_2;
395
396        assert_eq!(poly_cmp, poly_1);
397    }
398
399    /// Checks if multiplication works fine for different types
400    #[test]
401    fn availability() {
402        let poly = PolyOverZq::from_str("3  1 2 3 mod 17").unwrap();
403        let z = PolyOverZ::from(2);
404
405        _ = poly.clone() * z.clone();
406        _ = z.clone() * poly.clone();
407        _ = &poly * &z;
408        _ = &z * &poly;
409        _ = &poly * z.clone();
410        _ = z.clone() * &poly;
411        _ = &z * poly.clone();
412        _ = poly.clone() * &z;
413    }
414}