Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_power_of_2_mul.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::arithmetic::traits::{ModPowerOf2Mul, ModPowerOf2MulAssign};
9use crate::num::basic::traits::Zero;
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::unsigned_polynomial::UnsignedPolynomial;
12use crate::unsigned_polynomial::arithmetic::mod_power_of_2_add::assert_reduced;
13use alloc::vec;
14use alloc::vec::Vec;
15
16// Multiplication of polynomials whose coefficients are words reduced modulo $2^k$, for $k$ no
17// greater than the word width W. Arithmetic modulo $2^\text{W}$ is wrapping arithmetic, and $2^k$
18// divides $2^\text{W}$, so the kernels compute with wrapping arithmetic throughout and reduce each
19// coefficient of the result once, at the end.
20
21// The length of the shorter factor at which Karatsuba multiplication overtakes classical
22// multiplication. Measured on an Apple M-series machine, 2026-10, for 64-bit words: Karatsuba
23// breaks even at 32 and wins from 40.
24pub(crate) const MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD: usize = 32;
25
26// The length at which Karatsuba squaring overtakes classical squaring, which computes only half the
27// products and so stays ahead longer. Measured as above: Karatsuba loses at 64 and wins from 80.
28pub(crate) const MOD_POWER_OF_2_SQUARE_KARATSUBA_THRESHOLD: usize = 64;
29
30// Reduces each element of `xs` modulo $2^k$, where $k$ is `pow`.
31pub(crate) fn mask_coefficients<T: PrimitiveUnsigned>(xs: &mut [T], pow: u64) {
32    if pow < T::WIDTH {
33        let mask = T::low_mask(pow);
34        for x in xs {
35            *x &= mask;
36        }
37    }
38}
39
40// Adds each element of `ys` to the element of `xs` at the same index, wrapping.
41pub(crate) fn add_wrapping_assign<T: PrimitiveUnsigned>(xs: &mut [T], ys: &[T]) {
42    for (x, &y) in xs.iter_mut().zip(ys) {
43        x.wrapping_add_assign(y);
44    }
45}
46
47// Subtracts each element of `ys` from the element of `xs` at the same index, wrapping.
48pub(crate) fn sub_wrapping_assign<T: PrimitiveUnsigned>(xs: &mut [T], ys: &[T]) {
49    for (x, &y) in xs.iter_mut().zip(ys) {
50        x.wrapping_sub_assign(y);
51    }
52}
53
54// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, modulo
55// $2^\text{W}$, by schoolbook multiplication. `out` must have length `xs.len() + ys.len() - 1`.
56pub(crate) fn mul_classical_wrapping<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T], ys: &[T]) {
57    out.fill(T::ZERO);
58    for (i, &x) in xs.iter().enumerate() {
59        if x != T::ZERO {
60            for (o, &y) in out[i..].iter_mut().zip(ys) {
61                o.wrapping_add_assign(x.wrapping_mul(y));
62            }
63        }
64    }
65}
66
67// The scratch length needed by `mul_karatsuba_balanced_wrapping` for factors of length `n`: at each
68// level of the recursion, two sums and their product, each about half as long.
69pub(crate) const fn karatsuba_wrapping_scratch_len(mut n: usize) -> usize {
70    let mut len = 0;
71    while n >= MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD {
72        let c = n - (n >> 1);
73        len += (c << 2) - 1;
74        n = c;
75    }
76    len
77}
78
79// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, which have the same
80// nonzero length $n$, modulo $2^\text{W}$, by Karatsuba multiplication, falling back to schoolbook
81// multiplication below the threshold. `out` must have length $2n - 1$, and `scratch` at least
82// `karatsuba_wrapping_scratch_len(n)`.
83fn mul_karatsuba_balanced_wrapping<T: PrimitiveUnsigned>(
84    out: &mut [T],
85    xs: &[T],
86    ys: &[T],
87    scratch: &mut [T],
88) {
89    let n = xs.len();
90    if n < MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD {
91        mul_classical_wrapping(out, xs, ys);
92        return;
93    }
94    // Write x = x_0 + x^h x_1 and y = y_0 + x^h y_1. Then xy = x_0 y_0 + x^h ((x_0 + x_1)(y_0 +
95    // y_1) - x_0 y_0 - x_1 y_1) + x^{2h} x_1 y_1.
96    let h = n >> 1;
97    let c = n - h;
98    let two_h = h << 1;
99    let (x0, x1) = xs.split_at(h);
100    let (y0, y1) = ys.split_at(h);
101    split_into_chunks_mut!(scratch, c, [x_sum, y_sum], scratch);
102    let (middle, scratch) = scratch.split_at_mut((c << 1) - 1);
103    let (low, high) = out.split_at_mut(two_h);
104    mul_karatsuba_balanced_wrapping(&mut low[..two_h - 1], x0, y0, scratch);
105    low[two_h - 1] = T::ZERO;
106    mul_karatsuba_balanced_wrapping(high, x1, y1, scratch);
107    x_sum.copy_from_slice(x1);
108    add_wrapping_assign(x_sum, x0);
109    y_sum.copy_from_slice(y1);
110    add_wrapping_assign(y_sum, y0);
111    mul_karatsuba_balanced_wrapping(middle, x_sum, y_sum, scratch);
112    sub_wrapping_assign(middle, &out[..two_h - 1]);
113    sub_wrapping_assign(middle, &out[two_h..]);
114    add_wrapping_assign(&mut out[h..], middle);
115}
116
117// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty,
118// modulo $2^\text{W}$, by Karatsuba multiplication, falling back to schoolbook multiplication for
119// short factors. `out` must have length `xs.len() + ys.len() - 1`. When the factors' lengths
120// differ, the longer is cut into pieces as long as the shorter, and the products of the pieces are
121// added together.
122pub(crate) fn mul_karatsuba_wrapping<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T], ys: &[T]) {
123    let (xs, ys) = if xs.len() >= ys.len() {
124        (xs, ys)
125    } else {
126        (ys, xs)
127    };
128    let n = xs.len();
129    let m = ys.len();
130    if m < MOD_POWER_OF_2_MUL_KARATSUBA_THRESHOLD {
131        mul_classical_wrapping(out, xs, ys);
132        return;
133    }
134    let mut scratch = vec![T::ZERO; karatsuba_wrapping_scratch_len(m)];
135    if n == m {
136        mul_karatsuba_balanced_wrapping(out, xs, ys, &mut scratch);
137        return;
138    }
139    out.fill(T::ZERO);
140    let mut product = vec![T::ZERO; (m << 1) - 1];
141    for (k, piece) in xs.chunks(m).enumerate() {
142        let product = &mut product[..piece.len() + m - 1];
143        if piece.len() == m {
144            mul_karatsuba_balanced_wrapping(product, piece, ys, &mut scratch);
145        } else {
146            mul_karatsuba_wrapping(product, ys, piece);
147        }
148        add_wrapping_assign(&mut out[k * m..], product);
149    }
150}
151
152fn assert_lengths<T>(out: &[T], xs: &[T], ys: &[T]) {
153    assert!(!xs.is_empty());
154    assert!(!ys.is_empty());
155    assert_eq!(out.len(), xs.len() + ys.len() - 1);
156}
157
158// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
159// reduced modulo $2^k$, where $k$ is `pow`, by schoolbook multiplication. `out` must have length
160// `xs.len() + ys.len() - 1`, and `pow` must be no greater than `T::WIDTH`.
161crate_test_fn! {
162#[allow(dead_code)]
163mod_power_of_2_mul_to_out_classical<T: PrimitiveUnsigned>(
164    out: &mut [T],
165    xs: &[T],
166    ys: &[T],
167    pow: u64,
168) {
169    assert_lengths(out, xs, ys);
170    assert!(pow <= T::WIDTH);
171    mul_classical_wrapping(out, xs, ys);
172    mask_coefficients(out, pow);
173}}
174
175// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
176// reduced modulo $2^k$, where $k$ is `pow`, by Karatsuba multiplication. `out` must have length
177// `xs.len() + ys.len() - 1`, and `pow` must be no greater than `T::WIDTH`.
178crate_test_fn! {
179#[allow(dead_code)]
180mod_power_of_2_mul_to_out_karatsuba<T: PrimitiveUnsigned>(
181    out: &mut [T],
182    xs: &[T],
183    ys: &[T],
184    pow: u64,
185) {
186    assert_lengths(out, xs, ys);
187    assert!(pow <= T::WIDTH);
188    mul_karatsuba_wrapping(out, xs, ys);
189    mask_coefficients(out, pow);
190}}
191
192/// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
193/// reduced modulo $2^k$, where $k$ is `pow`. `out` must have length `xs.len() + ys.len() - 1`, and
194/// `pow` must be no greater than `T::WIDTH`.
195///
196/// This is not part of the public API; it is public so that `malachite-nz` can multiply
197/// `NaturalPolynomial`s with word-sized coefficients modulo $2^k$.
198#[doc(hidden)]
199pub fn mod_power_of_2_mul_to_out<T: PrimitiveUnsigned>(
200    out: &mut [T],
201    xs: &[T],
202    ys: &[T],
203    pow: u64,
204) {
205    assert_lengths(out, xs, ys);
206    assert!(pow <= T::WIDTH);
207    mul_karatsuba_wrapping(out, xs, ys);
208    mask_coefficients(out, pow);
209}
210
211// The polynomial whose coefficients are `xs`, trimmed.
212pub(crate) fn from_coefficients_trimmed<T: PrimitiveUnsigned>(xs: Vec<T>) -> UnsignedPolynomial<T> {
213    let mut p = UnsignedPolynomial { coefficients: xs };
214    p.trim();
215    p
216}
217
218// The product of the polynomials with coefficients `xs` and `ys`, both reduced modulo $2^k$, where
219// $k$ is `pow`, modulo $2^k$.
220pub(crate) fn mod_power_of_2_mul_helper<T: PrimitiveUnsigned>(
221    xs: &[T],
222    ys: &[T],
223    pow: u64,
224) -> UnsignedPolynomial<T> {
225    if xs.is_empty() || ys.is_empty() {
226        return UnsignedPolynomial::ZERO;
227    }
228    let mut out = vec![T::ZERO; xs.len() + ys.len() - 1];
229    mod_power_of_2_mul_to_out(&mut out, xs, ys, pow);
230    from_coefficients_trimmed(out)
231}
232
233impl<T: PrimitiveUnsigned> ModPowerOf2Mul<Self> for UnsignedPolynomial<T> {
234    type Output = Self;
235
236    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, taking both by value. The coefficients
237    /// of both must already be reduced modulo $2^k$.
238    ///
239    /// $$
240    /// f(p, q, k) = pq \bmod 2^k.
241    /// $$
242    ///
243    /// The leading coefficient of the product can vanish modulo $2^k$, and then the degree of the
244    /// product is lower than the sum of the degrees.
245    ///
246    /// # Worst-case complexity
247    /// $T(n) = O(n^{\log_2 3})$
248    ///
249    /// $M(n) = O(n)$
250    ///
251    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
252    ///
253    /// # Panics
254    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
255    /// greater than or equal to $2^k$.
256    ///
257    /// # Examples
258    /// ```
259    /// use core::str::FromStr;
260    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Mul;
261    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
262    ///
263    /// // The coefficients wrap around modulo 16.
264    /// assert_eq!(
265    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
266    ///         .unwrap()
267    ///         .mod_power_of_2_mul(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4)
268    ///         .to_string(),
269    ///     "2*x^3+11*x^2+3*x+10"
270    /// );
271    /// // The leading coefficient vanishes modulo 16, so the degree drops.
272    /// assert_eq!(
273    ///     UnsignedPolynomial::<u8>::from_str("8*x+1")
274    ///         .unwrap()
275    ///         .mod_power_of_2_mul(UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap(), 4)
276    ///         .to_string(),
277    ///     "10*x+1"
278    /// );
279    /// ```
280    ///
281    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
282    /// $2^k$.
283    fn mod_power_of_2_mul(self, other: Self, pow: u64) -> Self {
284        assert_reduced(&self, &other, pow);
285        mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow)
286    }
287}
288
289impl<T: PrimitiveUnsigned> ModPowerOf2Mul<&Self> for UnsignedPolynomial<T> {
290    type Output = Self;
291
292    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, taking the first by value and the
293    /// second by reference. The coefficients of both must already be reduced modulo $2^k$.
294    ///
295    /// $$
296    /// f(p, q, k) = pq \bmod 2^k.
297    /// $$
298    ///
299    /// The leading coefficient of the product can vanish modulo $2^k$, and then the degree of the
300    /// product is lower than the sum of the degrees.
301    ///
302    /// # Worst-case complexity
303    /// $T(n) = O(n^{\log_2 3})$
304    ///
305    /// $M(n) = O(n)$
306    ///
307    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
308    ///
309    /// # Panics
310    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
311    /// greater than or equal to $2^k$.
312    ///
313    /// # Examples
314    /// ```
315    /// use core::str::FromStr;
316    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Mul;
317    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
318    ///
319    /// // The coefficients wrap around modulo 16.
320    /// assert_eq!(
321    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
322    ///         .unwrap()
323    ///         .mod_power_of_2_mul(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4)
324    ///         .to_string(),
325    ///     "2*x^3+11*x^2+3*x+10"
326    /// );
327    /// // The leading coefficient vanishes modulo 16, so the degree drops.
328    /// assert_eq!(
329    ///     UnsignedPolynomial::<u8>::from_str("8*x+1")
330    ///         .unwrap()
331    ///         .mod_power_of_2_mul(&UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap(), 4)
332    ///         .to_string(),
333    ///     "10*x+1"
334    /// );
335    /// ```
336    ///
337    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
338    /// $2^k$.
339    fn mod_power_of_2_mul(self, other: &Self, pow: u64) -> Self {
340        assert_reduced(&self, other, pow);
341        mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow)
342    }
343}
344
345impl<T: PrimitiveUnsigned> ModPowerOf2Mul<UnsignedPolynomial<T>> for &UnsignedPolynomial<T> {
346    type Output = UnsignedPolynomial<T>;
347
348    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, taking the first by reference and the
349    /// second by value. The coefficients of both must already be reduced modulo $2^k$.
350    ///
351    /// $$
352    /// f(p, q, k) = pq \bmod 2^k.
353    /// $$
354    ///
355    /// The leading coefficient of the product can vanish modulo $2^k$, and then the degree of the
356    /// product is lower than the sum of the degrees.
357    ///
358    /// # Worst-case complexity
359    /// $T(n) = O(n^{\log_2 3})$
360    ///
361    /// $M(n) = O(n)$
362    ///
363    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
364    ///
365    /// # Panics
366    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
367    /// greater than or equal to $2^k$.
368    ///
369    /// # Examples
370    /// ```
371    /// use core::str::FromStr;
372    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Mul;
373    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
374    ///
375    /// // The coefficients wrap around modulo 16.
376    /// assert_eq!(
377    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
378    ///         .mod_power_of_2_mul(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4)
379    ///         .to_string(),
380    ///     "2*x^3+11*x^2+3*x+10"
381    /// );
382    /// // The leading coefficient vanishes modulo 16, so the degree drops.
383    /// assert_eq!(
384    ///     (&UnsignedPolynomial::<u8>::from_str("8*x+1").unwrap())
385    ///         .mod_power_of_2_mul(UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap(), 4)
386    ///         .to_string(),
387    ///     "10*x+1"
388    /// );
389    /// ```
390    ///
391    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
392    /// $2^k$.
393    fn mod_power_of_2_mul(self, other: UnsignedPolynomial<T>, pow: u64) -> UnsignedPolynomial<T> {
394        assert_reduced(self, &other, pow);
395        mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow)
396    }
397}
398
399impl<T: PrimitiveUnsigned> ModPowerOf2Mul<&UnsignedPolynomial<T>> for &UnsignedPolynomial<T> {
400    type Output = UnsignedPolynomial<T>;
401
402    /// Multiplies two [`UnsignedPolynomial`]s modulo $2^k$, taking both by reference. The
403    /// coefficients of both must already be reduced modulo $2^k$.
404    ///
405    /// $$
406    /// f(p, q, k) = pq \bmod 2^k.
407    /// $$
408    ///
409    /// The leading coefficient of the product can vanish modulo $2^k$, and then the degree of the
410    /// product is lower than the sum of the degrees.
411    ///
412    /// # Worst-case complexity
413    /// $T(n) = O(n^{\log_2 3})$
414    ///
415    /// $M(n) = O(n)$
416    ///
417    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
418    ///
419    /// # Panics
420    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
421    /// greater than or equal to $2^k$.
422    ///
423    /// # Examples
424    /// ```
425    /// use core::str::FromStr;
426    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Mul;
427    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
428    ///
429    /// // The coefficients wrap around modulo 16.
430    /// assert_eq!(
431    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
432    ///         .mod_power_of_2_mul(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4)
433    ///         .to_string(),
434    ///     "2*x^3+11*x^2+3*x+10"
435    /// );
436    /// // The leading coefficient vanishes modulo 16, so the degree drops.
437    /// assert_eq!(
438    ///     (&UnsignedPolynomial::<u8>::from_str("8*x+1").unwrap())
439    ///         .mod_power_of_2_mul(&UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap(), 4)
440    ///         .to_string(),
441    ///     "10*x+1"
442    /// );
443    /// ```
444    ///
445    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
446    /// $2^k$.
447    fn mod_power_of_2_mul(self, other: &UnsignedPolynomial<T>, pow: u64) -> UnsignedPolynomial<T> {
448        assert_reduced(self, other, pow);
449        mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow)
450    }
451}
452
453impl<T: PrimitiveUnsigned> ModPowerOf2MulAssign<Self> for UnsignedPolynomial<T> {
454    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $2^k$ in
455    /// place, taking the right-hand side by value. The coefficients of both must already be reduced
456    /// modulo $2^k$.
457    ///
458    /// $$
459    /// p \gets pq \bmod 2^k.
460    /// $$
461    ///
462    /// # Worst-case complexity
463    /// $T(n) = O(n^{\log_2 3})$
464    ///
465    /// $M(n) = O(n)$
466    ///
467    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
468    ///
469    /// # Panics
470    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
471    /// greater than or equal to $2^k$.
472    ///
473    /// # Examples
474    /// ```
475    /// use core::str::FromStr;
476    /// use malachite_base::num::arithmetic::traits::ModPowerOf2MulAssign;
477    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
478    ///
479    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
480    /// p.mod_power_of_2_mul_assign(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4);
481    /// assert_eq!(p.to_string(), "2*x^3+11*x^2+3*x+10");
482    /// ```
483    ///
484    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
485    /// $2^k$.
486    fn mod_power_of_2_mul_assign(&mut self, other: Self, pow: u64) {
487        assert_reduced(self, &other, pow);
488        *self = mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow);
489    }
490}
491
492impl<T: PrimitiveUnsigned> ModPowerOf2MulAssign<&Self> for UnsignedPolynomial<T> {
493    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $2^k$ in
494    /// place, taking the right-hand side by reference. The coefficients of both must already be
495    /// reduced modulo $2^k$.
496    ///
497    /// $$
498    /// p \gets pq \bmod 2^k.
499    /// $$
500    ///
501    /// # Worst-case complexity
502    /// $T(n) = O(n^{\log_2 3})$
503    ///
504    /// $M(n) = O(n)$
505    ///
506    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
507    ///
508    /// # Panics
509    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
510    /// greater than or equal to $2^k$.
511    ///
512    /// # Examples
513    /// ```
514    /// use core::str::FromStr;
515    /// use malachite_base::num::arithmetic::traits::ModPowerOf2MulAssign;
516    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
517    ///
518    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
519    /// p.mod_power_of_2_mul_assign(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 4);
520    /// assert_eq!(p.to_string(), "2*x^3+11*x^2+3*x+10");
521    /// ```
522    ///
523    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0, with the modulus
524    /// $2^k$.
525    fn mod_power_of_2_mul_assign(&mut self, other: &Self, pow: u64) {
526        assert_reduced(self, other, pow);
527        *self = mod_power_of_2_mul_helper(&self.coefficients, &other.coefficients, pow);
528    }
529}