qfall_math/integer/poly_over_z/arithmetic/
mul.rs

1// Copyright © 2023 Phil Milewski
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 [`PolyOverZ`] values.
10
11use super::super::PolyOverZ;
12use crate::macros::arithmetics::{
13    arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
14    arithmetic_trait_mixed_borrowed_owned,
15};
16use flint_sys::fmpz_poly::fmpz_poly_mul;
17use std::ops::{Mul, MulAssign};
18
19impl MulAssign<&PolyOverZ> for PolyOverZ {
20    /// Computes the multiplication of `self` and `other` reusing
21    /// the memory of `self`.
22    ///
23    /// Parameters:
24    /// - `other`: specifies the polynomial to multiply to `self`
25    ///
26    /// Returns the product of both polynomials as a [`PolyOverZ`].
27    ///
28    /// # Examples
29    /// ```
30    /// use qfall_math::integer::PolyOverZ;
31    /// use std::str::FromStr;
32    ///
33    /// let mut a = PolyOverZ::from_str("3  1 2 -3").unwrap();
34    /// let b = PolyOverZ::from_str("5  1 2 -3 0 8").unwrap();
35    ///
36    /// a *= &b;
37    /// a *= b;
38    /// ```
39    fn mul_assign(&mut self, other: &Self) {
40        unsafe { fmpz_poly_mul(&mut self.poly, &self.poly, &other.poly) };
41    }
42}
43
44arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, PolyOverZ, PolyOverZ);
45
46impl Mul for &PolyOverZ {
47    type Output = PolyOverZ;
48    /// Implements the [`Mul`] trait for two [`PolyOverZ`] values.
49    /// [`Mul`] is implemented for any combination of [`PolyOverZ`] and borrowed [`PolyOverZ`].
50    ///
51    /// Parameters:
52    /// - `other`: specifies the value to multiply with `self`
53    ///
54    /// Returns the product of both polynomials as a [`PolyOverZ`].
55    ///
56    /// # Examples
57    /// ```
58    /// use qfall_math::integer::PolyOverZ;
59    /// use std::str::FromStr;
60    ///
61    /// let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
62    /// let b: PolyOverZ = PolyOverZ::from_str("5  1 2 -3 0 8").unwrap();
63    ///
64    /// let c: PolyOverZ = &a * &b;
65    /// let d: PolyOverZ = a * b;
66    /// let e: PolyOverZ = &c * d;
67    /// let f: PolyOverZ = c * &e;
68    /// ```
69    fn mul(self, other: Self) -> Self::Output {
70        let mut out = PolyOverZ::default();
71        unsafe {
72            fmpz_poly_mul(&mut out.poly, &self.poly, &other.poly);
73        }
74        out
75    }
76}
77
78arithmetic_trait_borrowed_to_owned!(Mul, mul, PolyOverZ, PolyOverZ, PolyOverZ);
79arithmetic_trait_mixed_borrowed_owned!(Mul, mul, PolyOverZ, PolyOverZ, PolyOverZ);
80
81#[cfg(test)]
82mod test_mul_assign {
83    use super::PolyOverZ;
84    use std::str::FromStr;
85
86    /// Ensure that `mul_assign` works for small numbers.
87    #[test]
88    fn correct_small() {
89        let mut a = PolyOverZ::from_str("3  -1 2 -3").unwrap();
90        let b = PolyOverZ::from_str("2  5 2").unwrap();
91        let cmp = PolyOverZ::from_str("4  -5 8 -11 -6").unwrap();
92
93        a *= b;
94
95        assert_eq!(cmp, a);
96    }
97
98    /// Ensure that `mul_assign` works for large numbers.
99    #[test]
100    fn correct_large() {
101        let mut a = PolyOverZ::from_str(&format!("1  {}", i32::MIN)).unwrap();
102        let b = PolyOverZ::from_str(&format!("2  {} {}", i32::MAX, i32::MIN)).unwrap();
103        let cmp = PolyOverZ::from_str(&format!(
104            "2  {} {}",
105            i32::MIN as i64 * i32::MAX as i64,
106            i32::MIN as i64 * i32::MIN as i64
107        ))
108        .unwrap();
109
110        a *= b;
111
112        assert_eq!(cmp, a);
113    }
114
115    /// Ensure that `mul_assign` is available for all types.
116    #[test]
117    fn availability() {
118        let mut a = PolyOverZ::from_str("3  1 2 -3").unwrap();
119        let b = PolyOverZ::from_str("3  -1 -2 3").unwrap();
120
121        a *= &b;
122        a *= b;
123    }
124}
125
126#[cfg(test)]
127mod test_mul {
128    use super::PolyOverZ;
129    use std::str::FromStr;
130
131    /// Testing multiplication for two [`PolyOverZ`]
132    #[test]
133    fn mul() {
134        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
135        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
136        let c: PolyOverZ = a * b;
137        assert_eq!(c, PolyOverZ::from_str("7  1 4 6 5 -11 1 -6").unwrap());
138    }
139
140    /// Testing multiplication for two borrowed [`PolyOverZ`]
141    #[test]
142    fn mul_borrow() {
143        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
144        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
145        let c: PolyOverZ = &a * &b;
146        assert_eq!(c, PolyOverZ::from_str("7  1 4 6 5 -11 1 -6").unwrap());
147    }
148
149    /// Testing multiplication for borrowed [`PolyOverZ`] and [`PolyOverZ`]
150    #[test]
151    fn mul_first_borrowed() {
152        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
153        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
154        let c: PolyOverZ = &a * b;
155        assert_eq!(c, PolyOverZ::from_str("7  1 4 6 5 -11 1 -6").unwrap());
156    }
157
158    /// Testing multiplication for [`PolyOverZ`] and borrowed [`PolyOverZ`]
159    #[test]
160    fn mul_second_borrowed() {
161        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
162        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
163        let c: PolyOverZ = a * &b;
164        assert_eq!(c, PolyOverZ::from_str("7  1 4 6 5 -11 1 -6").unwrap());
165    }
166
167    /// Testing multiplication with a constant [`PolyOverZ`]
168    #[test]
169    fn mul_constant() {
170        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
171        let b: PolyOverZ = PolyOverZ::from(4);
172        let c: PolyOverZ = a * b;
173        assert_eq!(c, PolyOverZ::from_str("3  4 8 -12").unwrap());
174    }
175
176    /// Testing multiplication with `0`
177    #[test]
178    fn mul_zero() {
179        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
180        let b: PolyOverZ = PolyOverZ::default();
181        let c: PolyOverZ = a * b;
182        assert_eq!(c, PolyOverZ::default());
183    }
184
185    /// Testing multiplication for large [`PolyOverZ`]
186    #[test]
187    fn mul_large_numbers() {
188        let a: PolyOverZ = PolyOverZ::from_str(&format!("2  {} {}", u16::MAX, i32::MIN)).unwrap();
189        let b: PolyOverZ = PolyOverZ::from_str(&format!("2  {} {}", u32::MAX, i32::MAX)).unwrap();
190        let c: PolyOverZ = a * b;
191        assert_eq!(
192            c,
193            PolyOverZ::from_str(&format!(
194                "3  {} {} {}",
195                i64::from(u16::MAX) * i64::from(u32::MAX),
196                i64::from(u16::MAX) * i64::from(i32::MAX)
197                    + i64::from(u32::MAX) * i64::from(i32::MIN),
198                i64::from(i32::MAX) * i64::from(i32::MIN)
199            ))
200            .unwrap()
201        );
202    }
203}