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