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}