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