Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_power_of_2_mul_truncated.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8use crate::num::basic::traits::Zero;
9use crate::num::basic::unsigneds::PrimitiveUnsigned;
10use crate::polynomial::{ModPowerOf2MulTruncated, ModPowerOf2MulTruncatedAssign};
11use crate::unsigned_polynomial::UnsignedPolynomial;
12use crate::unsigned_polynomial::arithmetic::mod_power_of_2_add::assert_reduced;
13use crate::unsigned_polynomial::arithmetic::mod_power_of_2_mul::{
14    MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD, add_wrapping_assign, from_coefficients_trimmed,
15    mask_coefficients, mul_karatsuba_wrapping,
16};
17use alloc::vec;
18use core::cmp::min;
19
20// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
21// coefficients `xs` and `ys`, modulo $2^\text{W}$, by schoolbook multiplication.
22pub(crate) fn mul_truncated_classical_wrapping<T: PrimitiveUnsigned>(
23    out: &mut [T],
24    xs: &[T],
25    ys: &[T],
26) {
27    let len = out.len();
28    out.fill(T::ZERO);
29    for (i, &x) in xs.iter().take(len).enumerate() {
30        if x != T::ZERO {
31            for (o, &y) in out[i..].iter_mut().zip(ys) {
32                o.wrapping_add_assign(x.wrapping_mul(y));
33            }
34        }
35    }
36}
37
38// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
39// coefficients `xs` and `ys`, both nonempty, modulo $2^\text{W}$. With $n$ equal to `out.len()` and
40// $h = \lceil n/2 \rceil$, write x = x_0 + x^h x_1 and y = y_0 + x^h y_1; since $2h \geq n$, xy mod
41// x^n is x_0 y_0 + x^h (x_1 y_0 + x_0 y_1) mod x^n. The first product is a full product of
42// half-length factors, computed by Karatsuba multiplication, and the other two are truncated
43// products of half the length, computed recursively.
44pub(crate) fn mul_truncated_karatsuba_wrapping<T: PrimitiveUnsigned>(
45    out: &mut [T],
46    xs: &[T],
47    ys: &[T],
48) {
49    let len = out.len();
50    let xs = &xs[..min(xs.len(), len)];
51    let ys = &ys[..min(ys.len(), len)];
52    let (xs, ys) = if xs.len() >= ys.len() {
53        (xs, ys)
54    } else {
55        (ys, xs)
56    };
57    let full_len = xs.len() + ys.len() - 1;
58    if full_len <= len {
59        mul_karatsuba_wrapping(&mut out[..full_len], xs, ys);
60        out[full_len..].fill(T::ZERO);
61        return;
62    }
63    if ys.len() < MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD {
64        mul_truncated_classical_wrapping(out, xs, ys);
65        return;
66    }
67    let h = len.div_ceil(2);
68    let x0 = &xs[..min(h, xs.len())];
69    let y0 = &ys[..min(h, ys.len())];
70    // The product of x_0 and y_0 has at most 2h - 1 <= `len` coefficients, so this is a full
71    // product.
72    mul_truncated_karatsuba_wrapping(out, x0, y0);
73    let mut cross = vec![T::ZERO; len - h];
74    if xs.len() > h {
75        mul_truncated_karatsuba_wrapping(&mut cross, &xs[h..], y0);
76        add_wrapping_assign(&mut out[h..], &cross);
77    }
78    if ys.len() > h {
79        mul_truncated_karatsuba_wrapping(&mut cross, x0, &ys[h..]);
80        add_wrapping_assign(&mut out[h..], &cross);
81    }
82}
83
84fn assert_lengths<T>(out: &[T], xs: &[T], ys: &[T]) {
85    assert!(!out.is_empty());
86    assert!(!xs.is_empty());
87    assert!(!ys.is_empty());
88}
89
90// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
91// coefficients `xs` and `ys`, all three nonempty and the inputs reduced modulo $2^k$, where $k$ is
92// `pow`, by schoolbook multiplication. `pow` must be no greater than `T::WIDTH`.
93crate_test_fn! {
94#[allow(dead_code)]
95mod_power_of_2_mul_truncated_to_out_classical<T: PrimitiveUnsigned>(
96    out: &mut [T],
97    xs: &[T],
98    ys: &[T],
99    pow: u64,
100) {
101    assert_lengths(out, xs, ys);
102    assert!(pow <= T::WIDTH);
103    mul_truncated_classical_wrapping(out, xs, ys);
104    mask_coefficients(out, pow);
105}}
106
107// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
108// coefficients `xs` and `ys`, all three nonempty and the inputs reduced modulo $2^k$, where $k$ is
109// `pow`, by Karatsuba multiplication. `pow` must be no greater than `T::WIDTH`.
110crate_test_fn! {
111#[allow(dead_code)]
112mod_power_of_2_mul_truncated_to_out_karatsuba<T: PrimitiveUnsigned>(
113    out: &mut [T],
114    xs: &[T],
115    ys: &[T],
116    pow: u64,
117) {
118    assert_lengths(out, xs, ys);
119    assert!(pow <= T::WIDTH);
120    mul_truncated_karatsuba_wrapping(out, xs, ys);
121    mask_coefficients(out, pow);
122}}
123
124/// Sets `out` to the first `out.len()` coefficients of the product of the polynomials with
125/// coefficients `xs` and `ys`, all three nonempty and the inputs reduced modulo $2^k$, where $k$ is
126/// `pow`. `pow` must be no greater than `T::WIDTH`.
127///
128/// This is not part of the public API; it is public so that `malachite-nz` can multiply
129/// `NaturalPolynomial`s with word-sized coefficients modulo $2^k$.
130#[doc(hidden)]
131pub fn mod_power_of_2_mul_truncated_to_out<T: PrimitiveUnsigned>(
132    out: &mut [T],
133    xs: &[T],
134    ys: &[T],
135    pow: u64,
136) {
137    assert_lengths(out, xs, ys);
138    assert!(pow <= T::WIDTH);
139    mul_truncated_karatsuba_wrapping(out, xs, ys);
140    mask_coefficients(out, pow);
141}
142
143// The number of coefficients of a truncated product worth computing: `len`, but no more than the
144// whole product of factors of lengths `len1` and `len2`, which must be positive.
145pub(crate) fn truncated_len(len1: usize, len2: usize, len: u64) -> usize {
146    min(usize::try_from(len).unwrap_or(usize::MAX), len1 + len2 - 1)
147}
148
149// The product of the polynomials with coefficients `xs` and `ys`, both reduced modulo $2^k$, where
150// $k$ is `pow`, truncated to `len` coefficients and reduced modulo $2^k$.
151pub(crate) fn mod_power_of_2_mul_truncated_helper<T: PrimitiveUnsigned>(
152    xs: &[T],
153    ys: &[T],
154    len: u64,
155    pow: u64,
156) -> UnsignedPolynomial<T> {
157    if len == 0 || xs.is_empty() || ys.is_empty() {
158        return UnsignedPolynomial::ZERO;
159    }
160    let mut out = vec![T::ZERO; truncated_len(xs.len(), ys.len(), len)];
161    mod_power_of_2_mul_truncated_to_out(&mut out, xs, ys, pow);
162    from_coefficients_trimmed(out)
163}
164
165impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncated<Self> for UnsignedPolynomial<T> {
166    type Output = Self;
167
168    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, keeping only the coefficients of $x^i$
169    /// for $i$ less than `len`, taking both by value. The coefficients of both must already be
170    /// reduced modulo $2^k$.
171    ///
172    /// $$
173    /// f(p, q, n, k) = (pq \bmod x^n) \bmod 2^k.
174    /// $$
175    ///
176    /// The polynomials need not already be truncated: this is the product of their images modulo
177    /// $x^n$, so only their first `len` coefficients are read.
178    ///
179    /// # Worst-case complexity
180    /// $T(n) = O(n^{\log_2 3})$
181    ///
182    /// $M(n) = O(n)$
183    ///
184    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
185    ///
186    /// # Panics
187    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
188    /// greater than or equal to $2^k$.
189    ///
190    /// # Examples
191    /// ```
192    /// use core::str::FromStr;
193    /// use malachite_base::polynomial::ModPowerOf2MulTruncated;
194    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
195    ///
196    /// // The product is 2*x^3+11*x^2+19*x+10; its low two coefficients, modulo 16.
197    /// assert_eq!(
198    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
199    ///         .unwrap()
200    ///         .mod_power_of_2_mul_truncated(
201    ///             UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
202    ///             2,
203    ///             4
204    ///         )
205    ///         .to_string(),
206    ///     "3*x+10"
207    /// );
208    /// // The linear coefficient of the product, 16, vanishes modulo 16.
209    /// assert_eq!(
210    ///     UnsignedPolynomial::<u8>::from_str("x+15")
211    ///         .unwrap()
212    ///         .mod_power_of_2_mul_truncated(
213    ///             UnsignedPolynomial::<u8>::from_str("x+1").unwrap(),
214    ///             2,
215    ///             4
216    ///         )
217    ///         .to_string(),
218    ///     "15"
219    /// );
220    /// ```
221    ///
222    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
223    /// modulus $2^k$.
224    fn mod_power_of_2_mul_truncated(self, other: Self, len: u64, pow: u64) -> Self {
225        assert_reduced(&self, &other, pow);
226        mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow)
227    }
228}
229
230impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncated<&Self> for UnsignedPolynomial<T> {
231    type Output = Self;
232
233    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, keeping only the coefficients of $x^i$
234    /// for $i$ less than `len`, taking the first by value and the second by reference. The
235    /// coefficients of both must already be reduced modulo $2^k$.
236    ///
237    /// $$
238    /// f(p, q, n, k) = (pq \bmod x^n) \bmod 2^k.
239    /// $$
240    ///
241    /// The polynomials need not already be truncated: this is the product of their images modulo
242    /// $x^n$, so only their first `len` coefficients are read.
243    ///
244    /// # Worst-case complexity
245    /// $T(n) = O(n^{\log_2 3})$
246    ///
247    /// $M(n) = O(n)$
248    ///
249    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
250    ///
251    /// # Panics
252    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
253    /// greater than or equal to $2^k$.
254    ///
255    /// # Examples
256    /// ```
257    /// use core::str::FromStr;
258    /// use malachite_base::polynomial::ModPowerOf2MulTruncated;
259    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
260    ///
261    /// // The product is 2*x^3+11*x^2+19*x+10; its low two coefficients, modulo 16.
262    /// assert_eq!(
263    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
264    ///         .unwrap()
265    ///         .mod_power_of_2_mul_truncated(
266    ///             &UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
267    ///             2,
268    ///             4
269    ///         )
270    ///         .to_string(),
271    ///     "3*x+10"
272    /// );
273    /// // The linear coefficient of the product, 16, vanishes modulo 16.
274    /// assert_eq!(
275    ///     UnsignedPolynomial::<u8>::from_str("x+15")
276    ///         .unwrap()
277    ///         .mod_power_of_2_mul_truncated(
278    ///             &UnsignedPolynomial::<u8>::from_str("x+1").unwrap(),
279    ///             2,
280    ///             4
281    ///         )
282    ///         .to_string(),
283    ///     "15"
284    /// );
285    /// ```
286    ///
287    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
288    /// modulus $2^k$.
289    fn mod_power_of_2_mul_truncated(self, other: &Self, len: u64, pow: u64) -> Self {
290        assert_reduced(&self, other, pow);
291        mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow)
292    }
293}
294
295impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncated<UnsignedPolynomial<T>>
296    for &UnsignedPolynomial<T>
297{
298    type Output = UnsignedPolynomial<T>;
299
300    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, keeping only the coefficients of $x^i$
301    /// for $i$ less than `len`, taking the first by reference and the second by value. The
302    /// coefficients of both must already be reduced modulo $2^k$.
303    ///
304    /// $$
305    /// f(p, q, n, k) = (pq \bmod x^n) \bmod 2^k.
306    /// $$
307    ///
308    /// The polynomials need not already be truncated: this is the product of their images modulo
309    /// $x^n$, so only their first `len` coefficients are read.
310    ///
311    /// # Worst-case complexity
312    /// $T(n) = O(n^{\log_2 3})$
313    ///
314    /// $M(n) = O(n)$
315    ///
316    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
317    ///
318    /// # Panics
319    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
320    /// greater than or equal to $2^k$.
321    ///
322    /// # Examples
323    /// ```
324    /// use core::str::FromStr;
325    /// use malachite_base::polynomial::ModPowerOf2MulTruncated;
326    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
327    ///
328    /// // The product is 2*x^3+11*x^2+19*x+10; its low two coefficients, modulo 16.
329    /// assert_eq!(
330    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
331    ///         .mod_power_of_2_mul_truncated(
332    ///             UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
333    ///             2,
334    ///             4
335    ///         )
336    ///         .to_string(),
337    ///     "3*x+10"
338    /// );
339    /// // The linear coefficient of the product, 16, vanishes modulo 16.
340    /// assert_eq!(
341    ///     (&UnsignedPolynomial::<u8>::from_str("x+15").unwrap())
342    ///         .mod_power_of_2_mul_truncated(
343    ///             UnsignedPolynomial::<u8>::from_str("x+1").unwrap(),
344    ///             2,
345    ///             4
346    ///         )
347    ///         .to_string(),
348    ///     "15"
349    /// );
350    /// ```
351    ///
352    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
353    /// modulus $2^k$.
354    fn mod_power_of_2_mul_truncated(
355        self,
356        other: UnsignedPolynomial<T>,
357        len: u64,
358        pow: u64,
359    ) -> UnsignedPolynomial<T> {
360        assert_reduced(self, &other, pow);
361        mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow)
362    }
363}
364
365impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncated<&UnsignedPolynomial<T>>
366    for &UnsignedPolynomial<T>
367{
368    type Output = UnsignedPolynomial<T>;
369
370    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, keeping only the coefficients of $x^i$
371    /// for $i$ less than `len`, taking both by reference. The coefficients of both must already be
372    /// reduced modulo $2^k$.
373    ///
374    /// $$
375    /// f(p, q, n, k) = (pq \bmod x^n) \bmod 2^k.
376    /// $$
377    ///
378    /// The polynomials need not already be truncated: this is the product of their images modulo
379    /// $x^n$, so only their first `len` coefficients are read.
380    ///
381    /// # Worst-case complexity
382    /// $T(n) = O(n^{\log_2 3})$
383    ///
384    /// $M(n) = O(n)$
385    ///
386    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
387    ///
388    /// # Panics
389    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
390    /// greater than or equal to $2^k$.
391    ///
392    /// # Examples
393    /// ```
394    /// use core::str::FromStr;
395    /// use malachite_base::polynomial::ModPowerOf2MulTruncated;
396    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
397    ///
398    /// // The product is 2*x^3+11*x^2+19*x+10; its low two coefficients, modulo 16.
399    /// assert_eq!(
400    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
401    ///         .mod_power_of_2_mul_truncated(
402    ///             &UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
403    ///             2,
404    ///             4
405    ///         )
406    ///         .to_string(),
407    ///     "3*x+10"
408    /// );
409    /// // The linear coefficient of the product, 16, vanishes modulo 16.
410    /// assert_eq!(
411    ///     (&UnsignedPolynomial::<u8>::from_str("x+15").unwrap())
412    ///         .mod_power_of_2_mul_truncated(
413    ///             &UnsignedPolynomial::<u8>::from_str("x+1").unwrap(),
414    ///             2,
415    ///             4
416    ///         )
417    ///         .to_string(),
418    ///     "15"
419    /// );
420    /// ```
421    ///
422    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
423    /// modulus $2^k$.
424    fn mod_power_of_2_mul_truncated(
425        self,
426        other: &UnsignedPolynomial<T>,
427        len: u64,
428        pow: u64,
429    ) -> UnsignedPolynomial<T> {
430        assert_reduced(self, other, pow);
431        mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow)
432    }
433}
434
435impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncatedAssign<Self> for UnsignedPolynomial<T> {
436    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $2^k$ in
437    /// place, keeping only the coefficients of $x^i$ for $i$ less than `len`, taking the right-hand
438    /// side by value. The coefficients of both must already be reduced modulo $2^k$.
439    ///
440    /// $$
441    /// p \gets (pq \bmod x^n) \bmod 2^k.
442    /// $$
443    ///
444    /// # Worst-case complexity
445    /// $T(n) = O(n^{\log_2 3})$
446    ///
447    /// $M(n) = O(n)$
448    ///
449    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
450    ///
451    /// # Panics
452    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
453    /// greater than or equal to $2^k$.
454    ///
455    /// # Examples
456    /// ```
457    /// use core::str::FromStr;
458    /// use malachite_base::polynomial::ModPowerOf2MulTruncatedAssign;
459    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
460    ///
461    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
462    /// p.mod_power_of_2_mul_truncated_assign(
463    ///     UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
464    ///     2,
465    ///     4,
466    /// );
467    /// assert_eq!(p.to_string(), "3*x+10");
468    /// ```
469    ///
470    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
471    /// modulus $2^k$.
472    fn mod_power_of_2_mul_truncated_assign(&mut self, other: Self, len: u64, pow: u64) {
473        assert_reduced(self, &other, pow);
474        *self =
475            mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow);
476    }
477}
478
479impl<T: PrimitiveUnsigned> ModPowerOf2MulTruncatedAssign<&Self> for UnsignedPolynomial<T> {
480    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $2^k$ in
481    /// place, keeping only the coefficients of $x^i$ for $i$ less than `len`, taking the right-hand
482    /// side by reference. The coefficients of both must already be reduced modulo $2^k$.
483    ///
484    /// $$
485    /// p \gets (pq \bmod x^n) \bmod 2^k.
486    /// $$
487    ///
488    /// # Worst-case complexity
489    /// $T(n) = O(n^{\log_2 3})$
490    ///
491    /// $M(n) = O(n)$
492    ///
493    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
494    ///
495    /// # Panics
496    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
497    /// greater than or equal to $2^k$.
498    ///
499    /// # Examples
500    /// ```
501    /// use core::str::FromStr;
502    /// use malachite_base::polynomial::ModPowerOf2MulTruncatedAssign;
503    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
504    ///
505    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
506    /// p.mod_power_of_2_mul_truncated_assign(
507    ///     &UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(),
508    ///     2,
509    ///     4,
510    /// );
511    /// assert_eq!(p.to_string(), "3*x+10");
512    /// ```
513    ///
514    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with the
515    /// modulus $2^k$.
516    fn mod_power_of_2_mul_truncated_assign(&mut self, other: &Self, len: u64, pow: u64) {
517        assert_reduced(self, other, pow);
518        *self =
519            mod_power_of_2_mul_truncated_helper(&self.coefficients, &other.coefficients, len, pow);
520    }
521}