qfall_math/integer_mod_q/polynomial_ring_zq/arithmetic/
mul_scalar.rs

1// Copyright © 2025 Marcel Luca Schmidt, Marvin Beckmann
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 [`PolynomialRingZq`].
10
11use super::super::PolynomialRingZq;
12use crate::error::MathError;
13use crate::integer::Z;
14use crate::integer_mod_q::Zq;
15use crate::macros::arithmetics::{
16    arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
17    arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
18};
19use crate::macros::for_others::implement_for_others;
20use crate::traits::CompareBase;
21use std::ops::{Mul, MulAssign};
22
23impl Mul<&Z> for &PolynomialRingZq {
24    type Output = PolynomialRingZq;
25    /// Implements the [`Mul`] trait for a [`PolynomialRingZq`] with a [`Z`] integer.
26    /// [`Mul`] is implemented for any combination of owned and borrowed values.
27    /// [`Mul`] is also implemented for [`Z`] using [`PolynomialRingZq`].
28    ///
29    /// Parameters:
30    /// - `scalar`: specifies the scalar by which the polynomial is multiplied
31    ///
32    /// Returns the product of `self` and `scalar` as a [`PolynomialRingZq`].
33    ///
34    /// # Examples
35    /// ```
36    /// use qfall_math::integer_mod_q::PolynomialRingZq;
37    /// use qfall_math::integer::Z;
38    /// use std::str::FromStr;
39    ///
40    /// let poly_1 = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
41    /// let integer = Z::from(3);
42    ///
43    /// let poly_2 = &poly_1 * &integer;
44    /// ```
45    fn mul(self, scalar: &Z) -> Self::Output {
46        let mut out = PolynomialRingZq::from(&self.modulus);
47
48        out.poly = &self.poly * scalar;
49
50        out.reduce();
51        out
52    }
53}
54
55arithmetic_trait_reverse!(Mul, mul, Z, PolynomialRingZq, PolynomialRingZq);
56
57arithmetic_trait_borrowed_to_owned!(Mul, mul, PolynomialRingZq, Z, PolynomialRingZq);
58arithmetic_trait_borrowed_to_owned!(Mul, mul, Z, PolynomialRingZq, PolynomialRingZq);
59arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolynomialRingZq, Z, PolynomialRingZq);
60arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Z, PolynomialRingZq, PolynomialRingZq);
61
62implement_for_others!(Z, PolynomialRingZq, PolynomialRingZq, Mul Scalar for i8 i16 i32 i64 u8 u16 u32 u64);
63
64impl Mul<&Zq> for &PolynomialRingZq {
65    type Output = PolynomialRingZq;
66    /// Implements the [`Mul`] trait for a [`PolynomialRingZq`] with a [`Zq`].
67    /// [`Mul`] is implemented for any combination of owned and borrowed values.
68    /// [`Mul`] is also implemented for [`Zq`] using [`PolynomialRingZq`].
69    ///
70    /// Parameters:
71    /// - `scalar`: specifies the scalar by which the matrix is multiplied
72    ///
73    /// Returns the product of `self` and `scalar` as a [`PolynomialRingZq`].
74    ///
75    /// # Examples
76    /// ```
77    /// use qfall_math::integer_mod_q::{PolynomialRingZq, Zq};
78    /// use std::str::FromStr;
79    ///
80    /// let poly_1 = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
81    /// let integer = Zq::from((3,17));
82    ///
83    /// let poly_2 = &poly_1 * &integer;
84    /// ```
85    ///
86    /// # Panics ...
87    /// - if the moduli mismatch.
88    fn mul(self, scalar: &Zq) -> PolynomialRingZq {
89        self.mul_scalar_zq_safe(scalar).unwrap()
90    }
91}
92
93arithmetic_trait_reverse!(Mul, mul, Zq, PolynomialRingZq, PolynomialRingZq);
94
95arithmetic_trait_borrowed_to_owned!(Mul, mul, PolynomialRingZq, Zq, PolynomialRingZq);
96arithmetic_trait_borrowed_to_owned!(Mul, mul, Zq, PolynomialRingZq, PolynomialRingZq);
97arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolynomialRingZq, Zq, PolynomialRingZq);
98arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Zq, PolynomialRingZq, PolynomialRingZq);
99
100impl MulAssign<&Zq> for PolynomialRingZq {
101    /// Computes the scalar multiplication of `self` and `other` reusing
102    /// the memory of `self`.
103    ///
104    /// Parameters:
105    /// - `other`: specifies the value to multiply to `self`
106    ///
107    /// Returns the scalar of the matrix as a [`PolynomialRingZq`].
108    ///
109    /// # Examples
110    /// ```
111    /// use qfall_math::integer_mod_q::{ModulusPolynomialRingZq,PolynomialRingZq,Zq};
112    /// use qfall_math::integer::{MatZ,PolyOverZ,Z};
113    /// use std::str::FromStr;
114    ///
115    /// let modulus = ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX - 1)).unwrap();
116    /// let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
117    /// let mut polynomial_ring_zq = PolynomialRingZq::from((&poly_z, &modulus));
118    /// let zq = Zq::from((17, u64::MAX -1 ));
119    /// let z = Z::from(5);
120    ///
121    /// polynomial_ring_zq *= &zq;
122    /// polynomial_ring_zq *= zq;
123    /// polynomial_ring_zq *= &z;
124    /// polynomial_ring_zq *= z;
125    /// polynomial_ring_zq *= 2;
126    /// polynomial_ring_zq *= -2;
127    /// ```
128    ///
129    /// # Panics ...
130    /// - if the moduli are different.
131    fn mul_assign(&mut self, rhs: &Zq) {
132        if !self.compare_base(rhs) {
133            panic!("{}", self.call_compare_base_error(rhs).unwrap())
134        }
135        self.mul_assign(&rhs.value);
136    }
137}
138
139arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, PolynomialRingZq, Zq);
140
141impl PolynomialRingZq {
142    /// Implements multiplication for a [`PolynomialRingZq`] with a [`Zq`].
143    ///
144    /// Parameters:
145    /// - `scalar`: Specifies the scalar by which the polynomial is multiplied.
146    ///
147    /// Returns the product of `self` and `scalar` as a [`PolynomialRingZq`]
148    /// or an error if the moduli mismatch.
149    ///
150    /// # Examples
151    /// ```
152    /// use qfall_math::integer_mod_q::{PolynomialRingZq, Zq};
153    /// use std::str::FromStr;
154    ///
155    /// let poly_1 = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
156    /// let integer = Zq::from((3,17));
157    ///
158    /// let poly_2 = poly_1.mul_scalar_zq_safe(&integer).unwrap();
159    /// ```
160    ///
161    /// # Errors and Failures
162    /// - Returns a [`MathError`] of type
163    ///   [`MathError::MismatchingModulus`] if the moduli mismatch.
164    pub fn mul_scalar_zq_safe(&self, scalar: &Zq) -> Result<Self, MathError> {
165        if !self.compare_base(scalar) {
166            return Err(self.call_compare_base_error(scalar).unwrap());
167        }
168
169        let mut out = PolynomialRingZq::from(&self.modulus);
170
171        out.poly = &self.poly * &scalar.value;
172
173        out.reduce();
174        Ok(out)
175    }
176}
177
178#[cfg(test)]
179mod test_mul_z {
180    use super::PolynomialRingZq;
181    use crate::integer::Z;
182    use std::str::FromStr;
183
184    /// Checks if scalar multiplication works fine for both borrowed
185    #[test]
186    fn borrowed_correctness() {
187        let poly_1 = PolynomialRingZq::from_str(&format!(
188            "3  1 2 {} / 4  1 2 3 4 mod {}",
189            i64::MAX,
190            u64::MAX
191        ))
192        .unwrap();
193        let poly_2 = poly_1.clone();
194        let poly_3 = PolynomialRingZq::from_str(&format!(
195            "3  2 4 {} / 4  1 2 3 4 mod {}",
196            (i64::MAX as u64) * 2,
197            u64::MAX
198        ))
199        .unwrap();
200        let integer = Z::from(2);
201
202        let poly_1 = &poly_1 * &integer;
203        let poly_2 = &integer * &poly_2;
204
205        assert_eq!(poly_3, poly_1);
206        assert_eq!(poly_3, poly_2);
207    }
208
209    /// Checks if scalar multiplication works fine for different types
210    #[test]
211    fn availability() {
212        let poly = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
213        let z = Z::from(2);
214
215        _ = poly.clone() * z.clone();
216        _ = poly.clone() * 2i8;
217        _ = poly.clone() * 2u8;
218        _ = poly.clone() * 2i16;
219        _ = poly.clone() * 2u16;
220        _ = poly.clone() * 2i32;
221        _ = poly.clone() * 2u32;
222        _ = poly.clone() * 2i64;
223        _ = poly.clone() * 2u64;
224
225        _ = z.clone() * poly.clone();
226        _ = 2i8 * poly.clone();
227        _ = 2u64 * poly.clone();
228
229        _ = &poly * &z;
230        _ = &z * &poly;
231        _ = &poly * z.clone();
232        _ = z.clone() * &poly;
233        _ = poly.clone() * &z;
234        _ = &z * poly.clone();
235        _ = &poly * 2i8;
236        _ = 2i8 * &poly;
237    }
238}
239
240#[cfg(test)]
241mod test_mul_zq {
242    use super::PolynomialRingZq;
243    use crate::integer_mod_q::Zq;
244    use std::str::FromStr;
245
246    /// Checks if scalar multiplication works fine for both borrowed
247    #[test]
248    fn borrowed_correctness() {
249        let poly_1 = PolynomialRingZq::from_str(&format!(
250            "3  1 2 {} / 4  1 2 3 4 mod {}",
251            i64::MAX,
252            u64::MAX
253        ))
254        .unwrap();
255        let poly_2 = poly_1.clone();
256        let poly_3 = PolynomialRingZq::from_str(&format!(
257            "3  2 4 {} / 4  1 2 3 4 mod {}",
258            (i64::MAX as u64) * 2,
259            u64::MAX
260        ))
261        .unwrap();
262        let integer = Zq::from((2, u64::MAX));
263
264        let poly_1 = &poly_1 * &integer;
265        let poly_2 = &integer * &poly_2;
266
267        assert_eq!(poly_3, poly_1);
268        assert_eq!(poly_3, poly_2);
269    }
270
271    /// Checks if scalar multiplication works fine for different types
272    #[test]
273    fn availability() {
274        let poly = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
275        let z = Zq::from((2, 17));
276
277        _ = poly.clone() * z.clone();
278        _ = z.clone() * poly.clone();
279        _ = &poly * &z;
280        _ = &z * &poly;
281        _ = &poly * z.clone();
282        _ = z.clone() * &poly;
283        _ = &z * poly.clone();
284        _ = poly.clone() * &z;
285    }
286
287    /// Checks if scalar multiplication panics if the moduli mismatch
288    #[test]
289    #[should_panic]
290    fn different_moduli_panic() {
291        let poly = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
292        let z = Zq::from((2, 16));
293
294        _ = &poly * &z;
295    }
296
297    /// Checks if scalar multiplication panics if the moduli mismatch
298    #[test]
299    fn different_moduli_error() {
300        let poly = PolynomialRingZq::from_str("3  1 2 3 / 4  1 2 3 4 mod 17").unwrap();
301        let z = Zq::from((2, 16));
302
303        assert!(poly.mul_scalar_zq_safe(&z).is_err());
304    }
305}
306
307#[cfg(test)]
308mod test_mul_assign {
309    use crate::{
310        integer::{PolyOverZ, Z},
311        integer_mod_q::{ModulusPolynomialRingZq, PolyOverZq, PolynomialRingZq, Zq},
312    };
313    use std::str::FromStr;
314
315    /// Ensure that `mul_assign` produces same output as normal multiply.
316    #[test]
317    fn consistency() {
318        let modulus =
319            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
320        let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
321        let mut polynomial_ring_zq = PolynomialRingZq::from((&poly_z, &modulus));
322
323        let cmp = &polynomial_ring_zq * i64::MAX;
324        polynomial_ring_zq *= i64::MAX;
325
326        assert_eq!(cmp, polynomial_ring_zq)
327    }
328
329    /// Ensure that `mul_assign` is available for all types.
330    #[test]
331    fn availability() {
332        let modulus =
333            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
334        let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
335        let mut polynomial_ring_zq = PolynomialRingZq::from((&poly_z, &modulus));
336
337        let z = Z::from(2);
338        let zq = Zq::from((2, u64::MAX));
339
340        polynomial_ring_zq *= &z;
341        polynomial_ring_zq *= z;
342        polynomial_ring_zq *= &zq;
343        polynomial_ring_zq *= zq;
344        polynomial_ring_zq *= 1_u8;
345        polynomial_ring_zq *= 1_u16;
346        polynomial_ring_zq *= 1_u32;
347        polynomial_ring_zq *= 1_u64;
348        polynomial_ring_zq *= 1_i8;
349        polynomial_ring_zq *= 1_i16;
350        polynomial_ring_zq *= 1_i32;
351        polynomial_ring_zq *= 1_i64;
352    }
353
354    /// Ensure that `mul_assign` panics if the moduli mismatch.
355    #[test]
356    #[should_panic]
357    fn mismatching_modulus_zq() {
358        let modulus =
359            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
360        let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
361        let mut polynomial_ring_zq = PolynomialRingZq::from((&poly_z, &modulus));
362
363        let zq = Zq::from((2, u64::MAX - 1));
364
365        polynomial_ring_zq *= &zq;
366    }
367
368    /// Ensure that `mul_assign` panics if the moduli mismatch.
369    #[test]
370    #[should_panic]
371    fn mismatching_modulus_poly_zq() {
372        let modulus =
373            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX)).unwrap();
374        let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
375        let mut polynomial_ring_zq = PolynomialRingZq::from((&poly_z, &modulus));
376
377        let poly_z = PolyOverZ::from_str("2  3 1").unwrap();
378        let poly_zq = PolyOverZq::from((&poly_z, u64::MAX - 1));
379
380        polynomial_ring_zq *= &poly_zq;
381    }
382}