Skip to main content

malachite_nz/integer_polynomial/arithmetic/mul/
mod.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2008, 2009 William Hart
6//
7//      Copyright © 2014 Fredrik Johansson
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::integer_polynomial::IntegerPolynomial;
16use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
17use crate::integer_polynomial::arithmetic::mul::classical::mul_to_out_classical;
18use crate::integer_polynomial::arithmetic::mul::karatsuba::mul_to_out_karatsuba;
19use crate::integer_polynomial::arithmetic::mul::kronecker::mul_to_out_kronecker;
20use crate::integer_polynomial::arithmetic::mul::schonhage_strassen::*;
21use crate::integer_polynomial::arithmetic::mul::tiny::{mul_to_out_tiny_1, mul_to_out_tiny_2};
22use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
23use crate::integer_polynomial::arithmetic::square::square_to_out;
24use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
25use crate::integer_polynomial::arithmetic::vec::{
26    TinyKernel, classical_preferred, fft_preferred, karatsuba_preferred,
27    schonhage_strassen_preferred, tiny_kernel,
28};
29use alloc::vec;
30use alloc::vec::Vec;
31use core::mem::take;
32use core::ops::{Mul, MulAssign};
33use core::ptr;
34use malachite_base::num::conversion::traits::ExactFrom;
35
36pub mod classical;
37pub mod karatsuba;
38pub mod kronecker;
39pub mod schonhage_strassen;
40pub mod tiny;
41
42// Sets `out` to the coefficients of the product of the polynomials with coefficients `xs` and `ys`,
43// where `xs.len() >= ys.len() >= 1`. `out` must have length `xs.len() + ys.len() - 1`.
44//
45// # Worst-case complexity
46// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
47//
48// $M(n, m) = O(n(m + \log n) \log (nm))$
49//
50// where $T$ is time, $M$ is additional memory, $n$ is `xs.len()`, and $m$ is the largest number of
51// significant bits of any element of `xs` or `ys`.
52//
53// This is equivalent to `_fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0, except that it
54// chooses Schönhage–Strassen in a measured window (see `schonhage_strassen_preferred`) rather
55// than FLINT's.
56crate_test_fn! {mul_greater_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], ys: &[C]) {
57    let len1 = xs.len();
58    let len2 = ys.len();
59    if len2 == 1 {
60        C::vec_mul_scalar_to_out(out, xs, &ys[0]);
61        return;
62    }
63    if ptr::eq(xs, ys) {
64        square_to_out(out, xs);
65        return;
66    }
67    let bits1 = vec_max_bits(xs).0;
68    let bits2 = vec_max_bits(ys).0;
69    let len1 = u64::exact_from(len1);
70    let len2 = u64::exact_from(len2);
71    if fft_preferred(len2, bits1, bits2, 80, 100)
72        && mul_middle_to_out_fft(out, xs, ys, 0, xs.len() + ys.len() - 1)
73    {
74        return;
75    }
76    let half_bits = (bits1 + bits2) >> 1;
77    match tiny_kernel(bits1, bits2, len2, len2 < 40 + half_bits || len1 < 70 + half_bits) {
78        Some(TinyKernel::OneWord) => mul_to_out_tiny_1(out, xs, ys),
79        Some(TinyKernel::TwoWord) => mul_to_out_tiny_2(out, xs, ys),
80        None if classical_preferred(len2, bits1, bits2) => {
81            mul_to_out_classical(out, xs, ys);
82        }
83        None if karatsuba_preferred(len2, bits1, bits2) => {
84            mul_to_out_karatsuba(out, xs, ys);
85        }
86        None if schonhage_strassen_preferred(len1, len2, bits1, bits2, 4097) => {
87            mul_to_out_schonhage_strassen(out, xs, ys);
88        }
89        None => mul_to_out_kronecker(out, xs, ys),
90    }
91}}
92
93// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
94pub(crate) fn mul_ref_ref<C: PolynomialCoefficient>(xs: &[C], ys: &[C]) -> Vec<C> {
95    if xs.is_empty() || ys.is_empty() {
96        return Vec::new();
97    }
98    let mut out = vec![C::ZERO; xs.len() + ys.len() - 1];
99    if xs.len() >= ys.len() {
100        mul_greater_to_out(&mut out, xs, ys);
101    } else {
102        mul_greater_to_out(&mut out, ys, xs);
103    }
104    out
105}
106
107// Multiplies the polynomial with coefficients `xs` by the one with coefficients `ys`. When `ys` is
108// a constant, the product is a scalar multiple of `xs`, computed in place in its `Vec`.
109pub(crate) fn mul_val_ref<C: PolynomialCoefficient>(mut xs: Vec<C>, ys: &[C]) -> Vec<C> {
110    if let [c] = ys {
111        C::vec_mul_scalar_assign(&mut xs, c);
112        xs
113    } else {
114        mul_ref_ref(&xs, ys)
115    }
116}
117
118// Multiplies the polynomial with coefficients `xs` by the one with coefficients `ys`. When either
119// is a constant, the product is computed in place in the other's `Vec`.
120pub(crate) fn mul_val_val<C: PolynomialCoefficient>(xs: Vec<C>, mut ys: Vec<C>) -> Vec<C> {
121    if let [c] = xs.as_slice() {
122        C::vec_mul_scalar_assign(&mut ys, c);
123        ys
124    } else {
125        mul_val_ref(xs, &ys)
126    }
127}
128
129impl Mul<Self> for IntegerPolynomial {
130    type Output = Self;
131
132    /// Multiplies two [`IntegerPolynomial`]s, taking both by value.
133    ///
134    /// $$
135    /// f(p, q) = pq.
136    /// $$
137    ///
138    /// The integers have no zero divisors, so the degree of a product of nonzero polynomials is the
139    /// sum of their degrees.
140    ///
141    /// # Worst-case complexity
142    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
143    ///
144    /// $M(n, m) = O(n(m + \log n) \log (nm))$
145    ///
146    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
147    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
148    ///
149    /// # Examples
150    /// ```
151    /// use core::str::FromStr;
152    /// use malachite_base::num::basic::traits::Zero;
153    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
154    ///
155    /// assert_eq!(
156    ///     (IntegerPolynomial::from_str("x^2-3*x+2").unwrap()
157    ///         * IntegerPolynomial::from_str("2*x+5").unwrap())
158    ///     .to_string(),
159    ///     "2*x^3-x^2-11*x+10"
160    /// );
161    /// assert_eq!(
162    ///     (IntegerPolynomial::from_str("x+1").unwrap()
163    ///         * IntegerPolynomial::from_str("x-1").unwrap())
164    ///     .to_string(),
165    ///     "x^2-1"
166    /// );
167    /// assert_eq!(
168    ///     (IntegerPolynomial::from_str("x+1").unwrap() * IntegerPolynomial::ZERO).to_string(),
169    ///     "0"
170    /// );
171    /// ```
172    ///
173    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
174    #[inline]
175    fn mul(self, other: Self) -> Self {
176        Self {
177            coefficients: mul_val_val(self.coefficients, other.coefficients),
178        }
179    }
180}
181
182impl Mul<&Self> for IntegerPolynomial {
183    type Output = Self;
184
185    /// Multiplies two [`IntegerPolynomial`]s, taking the first by value and the second by
186    /// reference.
187    ///
188    /// $$
189    /// f(p, q) = pq.
190    /// $$
191    ///
192    /// The integers have no zero divisors, so the degree of a product of nonzero polynomials is the
193    /// sum of their degrees.
194    ///
195    /// # Worst-case complexity
196    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
197    ///
198    /// $M(n, m) = O(n(m + \log n) \log (nm))$
199    ///
200    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
201    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
202    ///
203    /// # Examples
204    /// ```
205    /// use core::str::FromStr;
206    /// use malachite_base::num::basic::traits::Zero;
207    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
208    ///
209    /// assert_eq!(
210    ///     (IntegerPolynomial::from_str("x^2-3*x+2").unwrap()
211    ///         * &IntegerPolynomial::from_str("2*x+5").unwrap())
212    ///         .to_string(),
213    ///     "2*x^3-x^2-11*x+10"
214    /// );
215    /// assert_eq!(
216    ///     (IntegerPolynomial::from_str("x+1").unwrap()
217    ///         * &IntegerPolynomial::from_str("x-1").unwrap())
218    ///         .to_string(),
219    ///     "x^2-1"
220    /// );
221    /// assert_eq!(
222    ///     (IntegerPolynomial::from_str("x+1").unwrap() * &IntegerPolynomial::ZERO).to_string(),
223    ///     "0"
224    /// );
225    /// ```
226    ///
227    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
228    #[inline]
229    fn mul(self, other: &Self) -> Self {
230        Self {
231            coefficients: mul_val_ref(self.coefficients, &other.coefficients),
232        }
233    }
234}
235
236impl Mul<IntegerPolynomial> for &IntegerPolynomial {
237    type Output = IntegerPolynomial;
238
239    /// Multiplies two [`IntegerPolynomial`]s, taking the first by reference and the second by
240    /// value.
241    ///
242    /// $$
243    /// f(p, q) = pq.
244    /// $$
245    ///
246    /// The integers have no zero divisors, so the degree of a product of nonzero polynomials is the
247    /// sum of their degrees.
248    ///
249    /// # Worst-case complexity
250    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
251    ///
252    /// $M(n, m) = O(n(m + \log n) \log (nm))$
253    ///
254    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
255    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
256    ///
257    /// # Examples
258    /// ```
259    /// use core::str::FromStr;
260    /// use malachite_base::num::basic::traits::Zero;
261    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
262    ///
263    /// assert_eq!(
264    ///     (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap()
265    ///         * IntegerPolynomial::from_str("2*x+5").unwrap())
266    ///     .to_string(),
267    ///     "2*x^3-x^2-11*x+10"
268    /// );
269    /// assert_eq!(
270    ///     (&IntegerPolynomial::from_str("x+1").unwrap()
271    ///         * IntegerPolynomial::from_str("x-1").unwrap())
272    ///     .to_string(),
273    ///     "x^2-1"
274    /// );
275    /// assert_eq!(
276    ///     (&IntegerPolynomial::from_str("x+1").unwrap() * IntegerPolynomial::ZERO).to_string(),
277    ///     "0"
278    /// );
279    /// ```
280    ///
281    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
282    #[inline]
283    fn mul(self, other: IntegerPolynomial) -> IntegerPolynomial {
284        IntegerPolynomial {
285            coefficients: mul_val_ref(other.coefficients, &self.coefficients),
286        }
287    }
288}
289
290impl Mul<&IntegerPolynomial> for &IntegerPolynomial {
291    type Output = IntegerPolynomial;
292
293    /// Multiplies two [`IntegerPolynomial`]s, taking both by reference.
294    ///
295    /// $$
296    /// f(p, q) = pq.
297    /// $$
298    ///
299    /// The integers have no zero divisors, so the degree of a product of nonzero polynomials is the
300    /// sum of their degrees.
301    ///
302    /// # Worst-case complexity
303    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
304    ///
305    /// $M(n, m) = O(n(m + \log n) \log (nm))$
306    ///
307    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
308    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
309    ///
310    /// # Examples
311    /// ```
312    /// use core::str::FromStr;
313    /// use malachite_base::num::basic::traits::Zero;
314    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
315    ///
316    /// assert_eq!(
317    ///     (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap()
318    ///         * &IntegerPolynomial::from_str("2*x+5").unwrap())
319    ///         .to_string(),
320    ///     "2*x^3-x^2-11*x+10"
321    /// );
322    /// assert_eq!(
323    ///     (&IntegerPolynomial::from_str("x+1").unwrap()
324    ///         * &IntegerPolynomial::from_str("x-1").unwrap())
325    ///         .to_string(),
326    ///     "x^2-1"
327    /// );
328    /// assert_eq!(
329    ///     (&IntegerPolynomial::from_str("x+1").unwrap() * &IntegerPolynomial::ZERO).to_string(),
330    ///     "0"
331    /// );
332    /// ```
333    ///
334    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
335    fn mul(self, other: &IntegerPolynomial) -> IntegerPolynomial {
336        IntegerPolynomial {
337            coefficients: mul_ref_ref(&self.coefficients, &other.coefficients),
338        }
339    }
340}
341
342impl MulAssign<Self> for IntegerPolynomial {
343    /// Multiplies an [`IntegerPolynomial`] by another [`IntegerPolynomial`] in place, taking the
344    /// right-hand side by value.
345    ///
346    /// $$
347    /// p \gets pq.
348    /// $$
349    ///
350    /// # Worst-case complexity
351    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
352    ///
353    /// $M(n, m) = O(n(m + \log n) \log (nm))$
354    ///
355    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
356    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
357    ///
358    /// # Examples
359    /// ```
360    /// use core::str::FromStr;
361    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
362    ///
363    /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
364    /// p *= IntegerPolynomial::from_str("2*x+5").unwrap();
365    /// assert_eq!(p.to_string(), "2*x^3-x^2-11*x+10");
366    /// ```
367    ///
368    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
369    #[inline]
370    fn mul_assign(&mut self, other: Self) {
371        self.coefficients = mul_val_val(take(&mut self.coefficients), other.coefficients);
372    }
373}
374
375impl MulAssign<&Self> for IntegerPolynomial {
376    /// Multiplies an [`IntegerPolynomial`] by another [`IntegerPolynomial`] in place, taking the
377    /// right-hand side by reference.
378    ///
379    /// $$
380    /// p \gets pq.
381    /// $$
382    ///
383    /// # Worst-case complexity
384    /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
385    ///
386    /// $M(n, m) = O(n(m + \log n) \log (nm))$
387    ///
388    /// where $T$ is time, $M$ is additional memory, $n$ is the length of the longer polynomial, and
389    /// $m$ is the largest number of significant bits of any coefficient of either polynomial.
390    ///
391    /// # Examples
392    /// ```
393    /// use core::str::FromStr;
394    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
395    ///
396    /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
397    /// p *= &IntegerPolynomial::from_str("2*x+5").unwrap();
398    /// assert_eq!(p.to_string(), "2*x^3-x^2-11*x+10");
399    /// ```
400    ///
401    /// This is equivalent to `fmpz_poly_mul` from `fmpz_poly/mul.c`, FLINT 3.6.0.
402    #[inline]
403    fn mul_assign(&mut self, other: &Self) {
404        self.coefficients = mul_val_ref(take(&mut self.coefficients), &other.coefficients);
405    }
406}