malachite_nz/integer_polynomial/arithmetic/mul_truncated/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 © 2010 Sebastian Pancratz
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::{
17 PolynomialCoefficient, trim_coefficients, truncate_coefficients,
18};
19use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
20use crate::integer_polynomial::arithmetic::mul_truncated::classical::mul_truncated_to_out_classical;
21use crate::integer_polynomial::arithmetic::mul_truncated::karatsuba::mul_truncated_to_out_karatsuba;
22use crate::integer_polynomial::arithmetic::mul_truncated::kronecker::mul_truncated_to_out_kronecker;
23use crate::integer_polynomial::arithmetic::mul_truncated::schonhage_strassen::*;
24use crate::integer_polynomial::arithmetic::mul_truncated::tiny::{
25 mul_truncated_to_out_tiny_1, mul_truncated_to_out_tiny_2,
26};
27use crate::integer_polynomial::arithmetic::square_truncated::square_truncated_to_out;
28use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
29use crate::integer_polynomial::arithmetic::vec::{
30 TinyKernel, classical_preferred, fft_preferred, karatsuba_preferred,
31 schonhage_strassen_preferred, tiny_kernel,
32};
33use alloc::vec;
34use alloc::vec::Vec;
35use core::cmp::min;
36use core::mem::{swap, take};
37use core::ptr;
38use malachite_base::num::conversion::traits::ExactFrom;
39use malachite_base::polynomial::{MulTruncated, MulTruncatedAssign};
40
41pub mod classical;
42pub mod karatsuba;
43pub mod kronecker;
44pub mod schonhage_strassen;
45pub mod tiny;
46
47// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
48// coefficients `xs` and `ys`, both nonempty. `out.len()` must be positive and at most `xs.len() +
49// ys.len() - 1`.
50//
51// # Worst-case complexity
52// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
53//
54// $M(n, m) = O(n(m + \log n) \log (nm))$
55//
56// where $T$ is time, $M$ is additional memory, $n$ is `out.len()`, and $m$ is the largest number of
57// significant bits of any element of `xs` or `ys`.
58//
59// This is equivalent to `_fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0, where `n` is
60// `out.len()`, except that it chooses Schönhage–Strassen in a measured window (see
61// `schonhage_strassen_preferred`) rather than FLINT's.
62crate_test_fn! {mul_truncated_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], ys: &[C]) {
63 let n = out.len();
64 let mut xs = &xs[..min(xs.len(), n)];
65 let mut ys = &ys[..min(ys.len(), n)];
66 assert_ne!(n, 0);
67 assert_ne!(xs.len(), 0);
68 assert_ne!(ys.len(), 0);
69 assert!(n < xs.len() + ys.len());
70 if xs.len() < ys.len() {
71 swap(&mut xs, &mut ys);
72 }
73 if ys.len() == 1 {
74 C::vec_mul_scalar_to_out(out, xs, &ys[0]);
75 return;
76 }
77 if ptr::eq(xs, ys) {
78 square_truncated_to_out(out, xs);
79 return;
80 }
81 let bits1 = vec_max_bits(xs).0;
82 let bits2 = vec_max_bits(ys).0;
83 let len1 = u64::exact_from(xs.len());
84 let len2 = u64::exact_from(ys.len());
85 if fft_preferred(len2, bits1, bits2, 100, 200) && mul_middle_to_out_fft(out, xs, ys, 0, n) {
86 return;
87 }
88 let n = u64::exact_from(n);
89 let short_enough = len2 < 50 || (len2 << 2 >= 3 * n && n < 150 + bits1 + bits2);
90 match tiny_kernel(bits1, bits2, len2, short_enough) {
91 Some(TinyKernel::OneWord) => mul_truncated_to_out_tiny_1(out, xs, ys),
92 Some(TinyKernel::TwoWord) => mul_truncated_to_out_tiny_2(out, xs, ys),
93 None if classical_preferred(len2, bits1, bits2) => {
94 mul_truncated_to_out_classical(out, xs, ys);
95 }
96 None if karatsuba_preferred(len2, bits1, bits2) => {
97 mul_truncated_to_out_karatsuba(out, xs, ys);
98 }
99 None if schonhage_strassen_preferred(len1, len2, bits1, bits2, 4097) => {
100 mul_truncated_to_out_schonhage_strassen(out, xs, ys);
101 }
102 None => mul_truncated_to_out_kronecker(out, xs, ys),
103 }
104}}
105
106// The coefficients of the product of the polynomials with coefficients `xs` and `ys`, keeping only
107// the coefficients of $x^i$ for $i$ less than `len`, without zeros at the end.
108//
109// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
110pub(crate) fn mul_truncated_ref_ref<C: PolynomialCoefficient>(
111 xs: &[C],
112 ys: &[C],
113 len: u64,
114) -> Vec<C> {
115 if xs.is_empty() || ys.is_empty() || len == 0 {
116 return Vec::new();
117 }
118 let n = usize::try_from(len)
119 .unwrap_or(usize::MAX)
120 .min(xs.len() + ys.len() - 1);
121 let mut out = vec![C::ZERO; n];
122 mul_truncated_to_out(&mut out, xs, ys);
123 trim_coefficients(&mut out);
124 out
125}
126
127// Multiplies the polynomial with coefficients `xs` by the one with coefficients `ys`, keeping only
128// the coefficients of $x^i$ for $i$ less than `len`. When `ys` is a constant, the product is a
129// scalar multiple of the truncation of `xs`, computed in place in its `Vec`.
130pub(crate) fn mul_truncated_val_ref<C: PolynomialCoefficient>(
131 mut xs: Vec<C>,
132 ys: &[C],
133 len: u64,
134) -> Vec<C> {
135 if let [c] = ys {
136 truncate_coefficients(&mut xs, len);
137 C::vec_mul_scalar_assign(&mut xs, c);
138 xs
139 } else {
140 mul_truncated_ref_ref(&xs, ys, len)
141 }
142}
143
144// Multiplies the polynomial with coefficients `xs` by the one with coefficients `ys`, keeping only
145// the coefficients of $x^i$ for $i$ less than `len`. When either is a constant, the product is
146// computed in place in the other's `Vec`.
147pub(crate) fn mul_truncated_val_val<C: PolynomialCoefficient>(
148 xs: Vec<C>,
149 ys: Vec<C>,
150 len: u64,
151) -> Vec<C> {
152 if xs.len() == 1 {
153 mul_truncated_val_ref(ys, &xs, len)
154 } else {
155 mul_truncated_val_ref(xs, &ys, len)
156 }
157}
158
159impl MulTruncated<Self> for IntegerPolynomial {
160 type Output = Self;
161
162 /// Multiplies two [`IntegerPolynomial`]s, keeping only the coefficients of $x^i$ for $i$ less
163 /// than `len`, taking both by value.
164 ///
165 /// $$
166 /// f(p, q, n) = pq \bmod x^n.
167 /// $$
168 ///
169 /// The polynomials need not already be truncated: this is the product of their images modulo
170 /// $x^n$, so only the first `len` coefficients of each are read. The product is trimmed, so
171 /// when the coefficient of $x^{n-1}$ is zero, the degree is lower still.
172 ///
173 /// # Worst-case complexity
174 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
175 ///
176 /// $M(n, m) = O(n(m + \log n) \log (nm))$
177 ///
178 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
179 /// significant bits of any of the first `len` coefficients of either polynomial.
180 ///
181 /// # Examples
182 /// ```
183 /// use core::str::FromStr;
184 /// use malachite_base::polynomial::MulTruncated;
185 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
186 ///
187 /// assert_eq!(
188 /// (IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
189 /// .mul_truncated(IntegerPolynomial::from_str("2*x+5").unwrap(), 2)
190 /// .to_string(),
191 /// "-11*x+10"
192 /// );
193 /// // The linear coefficient cancels.
194 /// assert_eq!(
195 /// (IntegerPolynomial::from_str("x+1").unwrap())
196 /// .mul_truncated(IntegerPolynomial::from_str("x-1").unwrap(), 2)
197 /// .to_string(),
198 /// "-1"
199 /// );
200 /// ```
201 ///
202 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
203 #[inline]
204 fn mul_truncated(self, other: Self, len: u64) -> Self {
205 Self {
206 coefficients: mul_truncated_val_val(self.coefficients, other.coefficients, len),
207 }
208 }
209}
210
211impl MulTruncated<&Self> for IntegerPolynomial {
212 type Output = Self;
213
214 /// Multiplies two [`IntegerPolynomial`]s, keeping only the coefficients of $x^i$ for $i$ less
215 /// than `len`, taking the first by value and the second by reference.
216 ///
217 /// $$
218 /// f(p, q, n) = pq \bmod x^n.
219 /// $$
220 ///
221 /// The polynomials need not already be truncated: this is the product of their images modulo
222 /// $x^n$, so only the first `len` coefficients of each are read. The product is trimmed, so
223 /// when the coefficient of $x^{n-1}$ is zero, the degree is lower still.
224 ///
225 /// # Worst-case complexity
226 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
227 ///
228 /// $M(n, m) = O(n(m + \log n) \log (nm))$
229 ///
230 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
231 /// significant bits of any of the first `len` coefficients of either polynomial.
232 ///
233 /// # Examples
234 /// ```
235 /// use core::str::FromStr;
236 /// use malachite_base::polynomial::MulTruncated;
237 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
238 ///
239 /// assert_eq!(
240 /// (IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
241 /// .mul_truncated(&IntegerPolynomial::from_str("2*x+5").unwrap(), 2)
242 /// .to_string(),
243 /// "-11*x+10"
244 /// );
245 /// // The linear coefficient cancels.
246 /// assert_eq!(
247 /// (IntegerPolynomial::from_str("x+1").unwrap())
248 /// .mul_truncated(&IntegerPolynomial::from_str("x-1").unwrap(), 2)
249 /// .to_string(),
250 /// "-1"
251 /// );
252 /// ```
253 ///
254 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
255 #[inline]
256 fn mul_truncated(self, other: &Self, len: u64) -> Self {
257 Self {
258 coefficients: mul_truncated_val_ref(self.coefficients, &other.coefficients, len),
259 }
260 }
261}
262
263impl MulTruncated<IntegerPolynomial> for &IntegerPolynomial {
264 type Output = IntegerPolynomial;
265
266 /// Multiplies two [`IntegerPolynomial`]s, keeping only the coefficients of $x^i$ for $i$ less
267 /// than `len`, taking the first by reference and the second by value.
268 ///
269 /// $$
270 /// f(p, q, n) = pq \bmod x^n.
271 /// $$
272 ///
273 /// The polynomials need not already be truncated: this is the product of their images modulo
274 /// $x^n$, so only the first `len` coefficients of each are read. The product is trimmed, so
275 /// when the coefficient of $x^{n-1}$ is zero, the degree is lower still.
276 ///
277 /// # Worst-case complexity
278 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
279 ///
280 /// $M(n, m) = O(n(m + \log n) \log (nm))$
281 ///
282 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
283 /// significant bits of any of the first `len` coefficients of either polynomial.
284 ///
285 /// # Examples
286 /// ```
287 /// use core::str::FromStr;
288 /// use malachite_base::polynomial::MulTruncated;
289 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
290 ///
291 /// assert_eq!(
292 /// (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
293 /// .mul_truncated(IntegerPolynomial::from_str("2*x+5").unwrap(), 2)
294 /// .to_string(),
295 /// "-11*x+10"
296 /// );
297 /// // The linear coefficient cancels.
298 /// assert_eq!(
299 /// (&IntegerPolynomial::from_str("x+1").unwrap())
300 /// .mul_truncated(IntegerPolynomial::from_str("x-1").unwrap(), 2)
301 /// .to_string(),
302 /// "-1"
303 /// );
304 /// ```
305 ///
306 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
307 #[inline]
308 fn mul_truncated(self, other: IntegerPolynomial, len: u64) -> IntegerPolynomial {
309 IntegerPolynomial {
310 coefficients: mul_truncated_val_ref(other.coefficients, &self.coefficients, len),
311 }
312 }
313}
314
315impl MulTruncated<&IntegerPolynomial> for &IntegerPolynomial {
316 type Output = IntegerPolynomial;
317
318 /// Multiplies two [`IntegerPolynomial`]s, keeping only the coefficients of $x^i$ for $i$ less
319 /// than `len`, taking both by reference.
320 ///
321 /// $$
322 /// f(p, q, n) = pq \bmod x^n.
323 /// $$
324 ///
325 /// The polynomials need not already be truncated: this is the product of their images modulo
326 /// $x^n$, so only the first `len` coefficients of each are read. The product is trimmed, so
327 /// when the coefficient of $x^{n-1}$ is zero, the degree is lower still.
328 ///
329 /// # Worst-case complexity
330 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
331 ///
332 /// $M(n, m) = O(n(m + \log n) \log (nm))$
333 ///
334 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
335 /// significant bits of any of the first `len` coefficients of either polynomial.
336 ///
337 /// # Examples
338 /// ```
339 /// use core::str::FromStr;
340 /// use malachite_base::polynomial::MulTruncated;
341 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
342 ///
343 /// assert_eq!(
344 /// (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
345 /// .mul_truncated(&IntegerPolynomial::from_str("2*x+5").unwrap(), 2)
346 /// .to_string(),
347 /// "-11*x+10"
348 /// );
349 /// // The linear coefficient cancels.
350 /// assert_eq!(
351 /// (&IntegerPolynomial::from_str("x+1").unwrap())
352 /// .mul_truncated(&IntegerPolynomial::from_str("x-1").unwrap(), 2)
353 /// .to_string(),
354 /// "-1"
355 /// );
356 /// ```
357 ///
358 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
359 #[inline]
360 fn mul_truncated(self, other: &IntegerPolynomial, len: u64) -> IntegerPolynomial {
361 IntegerPolynomial {
362 coefficients: mul_truncated_ref_ref(&self.coefficients, &other.coefficients, len),
363 }
364 }
365}
366
367impl MulTruncatedAssign<Self> for IntegerPolynomial {
368 /// Multiplies an [`IntegerPolynomial`] by another [`IntegerPolynomial`] in place, keeping only
369 /// the coefficients of $x^i$ for $i$ less than `len`, taking the right-hand side by value.
370 ///
371 /// $$
372 /// p \gets pq \bmod x^n.
373 /// $$
374 ///
375 /// # Worst-case complexity
376 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
377 ///
378 /// $M(n, m) = O(n(m + \log n) \log (nm))$
379 ///
380 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
381 /// significant bits of any of the first `len` coefficients of either polynomial.
382 ///
383 /// # Examples
384 /// ```
385 /// use core::str::FromStr;
386 /// use malachite_base::polynomial::MulTruncatedAssign;
387 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
388 ///
389 /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
390 /// p.mul_truncated_assign(IntegerPolynomial::from_str("2*x+5").unwrap(), 2);
391 /// assert_eq!(p.to_string(), "-11*x+10");
392 /// ```
393 ///
394 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
395 #[inline]
396 fn mul_truncated_assign(&mut self, other: Self, len: u64) {
397 self.coefficients =
398 mul_truncated_val_val(take(&mut self.coefficients), other.coefficients, len);
399 }
400}
401
402impl MulTruncatedAssign<&Self> for IntegerPolynomial {
403 /// Multiplies an [`IntegerPolynomial`] by another [`IntegerPolynomial`] in place, keeping only
404 /// the coefficients of $x^i$ for $i$ less than `len`, taking the right-hand side by reference.
405 ///
406 /// $$
407 /// p \gets pq \bmod x^n.
408 /// $$
409 ///
410 /// # Worst-case complexity
411 /// $T(n, m) = O(n^{\log_2 3} m \log m \log\log m)$
412 ///
413 /// $M(n, m) = O(n(m + \log n) \log (nm))$
414 ///
415 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
416 /// significant bits of any of the first `len` coefficients of either polynomial.
417 ///
418 /// # Examples
419 /// ```
420 /// use core::str::FromStr;
421 /// use malachite_base::polynomial::MulTruncatedAssign;
422 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
423 ///
424 /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
425 /// p.mul_truncated_assign(&IntegerPolynomial::from_str("2*x+5").unwrap(), 2);
426 /// assert_eq!(p.to_string(), "-11*x+10");
427 /// ```
428 ///
429 /// This is equivalent to `fmpz_poly_mullow` from `fmpz_poly/mullow.c`, FLINT 3.6.0.
430 #[inline]
431 fn mul_truncated_assign(&mut self, other: &Self, len: u64) {
432 self.coefficients =
433 mul_truncated_val_ref(take(&mut self.coefficients), &other.coefficients, len);
434 }
435}