Skip to main content

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}