Skip to main content

qfall_math/integer/poly_over_z/arithmetic/
mul_scalar.rs

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