Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_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::mod_mul::{limbs_invert_limb_u64, mod_preinverted_double};
9use crate::num::arithmetic::traits::{ModMul, ModMulAssign};
10use crate::num::basic::integers::PrimitiveInt;
11use crate::num::basic::traits::Zero;
12use crate::num::basic::unsigneds::PrimitiveUnsigned;
13use crate::num::logic::traits::LeadingZeros;
14use crate::unsigned_polynomial::UnsignedPolynomial;
15use crate::unsigned_polynomial::arithmetic::mod_add::assert_reduced;
16use crate::unsigned_polynomial::arithmetic::mod_power_of_2_mul::from_coefficients_trimmed;
17use alloc::vec;
18
19// Multiplication of polynomials whose coefficients are words reduced modulo a word $m$. As in
20// FLINT's `_nmod_poly_mul_classical`, each coefficient of a product is accumulated exactly, in one,
21// two, or three words, depending on how large the sum can get, and reduced once. The reduction uses
22// multiplication modulo $m$ with precomputed data, so that no two-word division is needed.
23
24// The length of the shorter factor at which Karatsuba multiplication overtakes classical
25// multiplication. Measured on an Apple M-series machine, 2026-10, with 64-bit words: about 80 for
26// moduli of 16 to 64 bits.
27pub(crate) const MOD_MUL_KARATSUBA_THRESHOLD: usize = 80;
28
29// The length at which Karatsuba squaring overtakes classical squaring. Classical squaring computes
30// only half the products, so it stays ahead longer: measured as above, about 256.
31pub(crate) const MOD_SQUARE_KARATSUBA_THRESHOLD: usize = 256;
32
33// What summing products of coefficients and reducing the sums modulo $m$ needs: $m$, data for
34// reducing two-word values modulo $m$, and the number of words in which the sums are accumulated.
35// As in FLINT's `_nmod_vec_dot_bound_limbs`, a sum of at most `terms` products of values less than
36// $m$ is bounded by `terms` $(m - 1)^2$, and when the bound fits in one or two words, fewer words
37// are used.
38pub(crate) struct ModData<T: PrimitiveUnsigned> {
39    pub(crate) m: T,
40    // When `T` has 64 bits: the inverse of $m$, shifted left until its top bit is set, for
41    // `mod_preinverted_double`.
42    inv: u64,
43    // When `T` has more than 64 bits: $2^\text{W} \bmod m$, where W is `T::WIDTH`.
44    radix: T,
45    // 1, 2, or 3.
46    pub(crate) words: u8,
47    // When three words are used: the number of products that can be added to a reduced value
48    // without overflowing three words, with room to double the sum: $2^{\text{W} - 1}$, or
49    // `usize::MAX` if that does not fit.
50    pub(crate) max_terms: usize,
51}
52
53impl<T: PrimitiveUnsigned> ModData<T> {
54    // Sums will have at most `terms` products, which must be positive.
55    pub(crate) fn new(m: T, terms: usize) -> Self {
56        assert_ne!(m, T::ZERO);
57        let words = match T::try_from(terms) {
58            Ok(terms) => {
59                // (m - 1)^2 terms, in three words.
60                let (hi, lo) = T::x_mul_y_to_zz(m - T::ONE, m - T::ONE);
61                let carry = T::x_mul_y_to_zz(lo, terms).0;
62                let (top, mid) = T::x_mul_y_to_zz(hi, terms);
63                let (top, mid) = T::xx_add_yy_to_zz(top, mid, T::ZERO, carry);
64                if top != T::ZERO {
65                    3
66                } else if mid != T::ZERO {
67                    2
68                } else {
69                    1
70                }
71            }
72            Err(_) => 3,
73        };
74        let inv = if T::WIDTH == u64::WIDTH {
75            let m: u64 = m.wrapping_into();
76            limbs_invert_limb_u64(m << LeadingZeros::leading_zeros(m))
77        } else {
78            0
79        };
80        Self {
81            m,
82            inv,
83            // 2^W mod m = (2^W - m) mod m, computed without overflow.
84            radix: if T::WIDTH > u64::WIDTH {
85                m.wrapping_neg() % m
86            } else {
87                T::ZERO
88            },
89            words,
90            max_terms: if T::WIDTH > usize::WIDTH {
91                usize::MAX
92            } else {
93                1 << (T::WIDTH - 1)
94            },
95        }
96    }
97
98    // The value $x_1 2^\text{W} + x_0$ modulo $m$. Words of at most 32 bits are combined into a
99    // `u64` and divided once, and 64-bit words are reduced with a precomputed inverse, as by
100    // FLINT's `NMOD2_RED2`.
101    #[inline]
102    pub(crate) fn reduce_2(&self, x1: T, x0: T) -> T {
103        let m = self.m;
104        if T::WIDTH <= u32::WIDTH {
105            let x: u64 = x1.wrapping_into();
106            let x0: u64 = x0.wrapping_into();
107            let m: u64 = m.wrapping_into();
108            T::wrapping_from(((x << T::WIDTH) | x0) % m)
109        } else if T::WIDTH == u64::WIDTH {
110            T::wrapping_from(mod_preinverted_double::<u64, u128>(
111                x1.wrapping_into(),
112                x0.wrapping_into(),
113                m.wrapping_into(),
114                self.inv,
115            ))
116        } else {
117            let data = T::precompute_mod_mul_data(&m);
118            (x1 % m)
119                .mod_mul_precomputed(self.radix, m, &data)
120                .mod_add(x0 % m, m)
121        }
122    }
123
124    // The value $x_2 2^{2\text{W}} + x_1 2^\text{W} + x_0$ modulo $m$, by Horner's rule, as by
125    // FLINT's `NMOD_RED3`.
126    #[inline]
127    pub(crate) fn reduce(&self, x2: T, x1: T, x0: T) -> T {
128        self.reduce_2(self.reduce_2(x2, x1), x0)
129    }
130
131    // A sum from `column_sum`, modulo $m$.
132    #[inline]
133    pub(crate) fn reduce_sum(&self, (x2, x1, x0): (T, T, T)) -> T {
134        match self.words {
135            1 => x0 % self.m,
136            2 => self.reduce_2(x1, x0),
137            _ => self.reduce(x2, x1, x0),
138        }
139    }
140}
141
142// Adds the product of `x` and `y` to the three-word accumulator `(a2, a1, a0)`.
143#[inline]
144pub(crate) fn accumulate<T: PrimitiveUnsigned>(acc: &mut (T, T, T), x: T, y: T) {
145    let (hi, lo) = T::x_mul_y_to_zz(x, y);
146    let (a2, a1, a0) = *acc;
147    *acc = T::xxx_add_yyy_to_zzz(a2, a1, a0, T::ZERO, hi, lo);
148}
149
150// The sum of the products of `xs[i]` and `ys[ys.len() - 1 - i]`, for `i` less than `xs.len()`, as a
151// three-word value, accumulated in `d.words` words; `xs` and `ys` must have the same length, at
152// most the number of terms `d` was made for. When three words are used, whenever the number of
153// products would exceed `d.max_terms` the sum so far is reduced modulo $m$, so the result is
154// congruent to the true sum and, with room to spare, can be doubled without overflowing.
155#[inline]
156pub(crate) fn column_sum<T: PrimitiveUnsigned>(xs: &[T], ys: &[T], d: &ModData<T>) -> (T, T, T) {
157    match d.words {
158        1 => {
159            let mut sum = T::ZERO;
160            for (&x, &y) in xs.iter().zip(ys.iter().rev()) {
161                sum.wrapping_add_assign(x.wrapping_mul(y));
162            }
163            (T::ZERO, T::ZERO, sum)
164        }
165        2 => {
166            let (mut hi, mut lo) = (T::ZERO, T::ZERO);
167            for (&x, &y) in xs.iter().zip(ys.iter().rev()) {
168                let (p_hi, p_lo) = T::x_mul_y_to_zz(x, y);
169                (hi, lo) = T::xx_add_yy_to_zz(hi, lo, p_hi, p_lo);
170            }
171            (T::ZERO, hi, lo)
172        }
173        _ => {
174            let mut acc = (T::ZERO, T::ZERO, T::ZERO);
175            for (i, (x_chunk, y_chunk)) in xs
176                .chunks(d.max_terms)
177                .zip(ys.rchunks(d.max_terms))
178                .enumerate()
179            {
180                if i != 0 {
181                    acc = (T::ZERO, T::ZERO, d.reduce(acc.0, acc.1, acc.2));
182                }
183                for (&x, &y) in x_chunk.iter().zip(y_chunk.iter().rev()) {
184                    accumulate(&mut acc, x, y);
185                }
186            }
187            acc
188        }
189    }
190}
191
192// Adds each element of `ys` to the element of `xs` at the same index, modulo `m`.
193pub(crate) fn mod_add_assign_slice<T: PrimitiveUnsigned>(xs: &mut [T], ys: &[T], m: T) {
194    for (x, &y) in xs.iter_mut().zip(ys) {
195        *x = x.mod_add(y, m);
196    }
197}
198
199// Subtracts each element of `ys` from the element of `xs` at the same index, modulo `m`.
200pub(crate) fn mod_sub_assign_slice<T: PrimitiveUnsigned>(xs: &mut [T], ys: &[T], m: T) {
201    for (x, &y) in xs.iter_mut().zip(ys) {
202        *x = x.mod_sub(y, m);
203    }
204}
205
206// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, reduced modulo $m$,
207// by schoolbook multiplication, one coefficient at a time. `out` must have length `xs.len() +
208// ys.len() - 1`.
209pub(crate) fn mod_mul_classical<T: PrimitiveUnsigned>(
210    out: &mut [T],
211    xs: &[T],
212    ys: &[T],
213    d: &ModData<T>,
214) {
215    let n = xs.len();
216    let m = ys.len();
217    for (k, o) in out.iter_mut().enumerate() {
218        let start = k.saturating_sub(m - 1);
219        let stop = core::cmp::min(k, n - 1);
220        let acc = column_sum(&xs[start..=stop], &ys[k - stop..=k - start], d);
221        *o = d.reduce_sum(acc);
222    }
223}
224
225// The scratch length needed by `mod_mul_karatsuba_balanced` for factors of length `n`.
226pub(crate) const fn mod_karatsuba_scratch_len(mut n: usize, threshold: usize) -> usize {
227    let mut len = 0;
228    while n >= threshold {
229        let c = n - (n >> 1);
230        len += (c << 2) - 1;
231        n = c;
232    }
233    len
234}
235
236// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, which have the same
237// nonzero length $n$, modulo $m$, by Karatsuba multiplication, falling back to schoolbook
238// multiplication below the threshold. `out` must have length $2n - 1$.
239fn mod_mul_karatsuba_balanced<T: PrimitiveUnsigned>(
240    out: &mut [T],
241    xs: &[T],
242    ys: &[T],
243    d: &ModData<T>,
244    scratch: &mut [T],
245) {
246    let n = xs.len();
247    if n < MOD_MUL_KARATSUBA_THRESHOLD {
248        mod_mul_classical(out, xs, ys, d);
249        return;
250    }
251    // 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 +
252    // y_1) - x_0 y_0 - x_1 y_1) + x^{2h} x_1 y_1.
253    let m = d.m;
254    let h = n >> 1;
255    let c = n - h;
256    let two_h = h << 1;
257    let (x0, x1) = xs.split_at(h);
258    let (y0, y1) = ys.split_at(h);
259    split_into_chunks_mut!(scratch, c, [x_sum, y_sum], scratch);
260    let (middle, scratch) = scratch.split_at_mut((c << 1) - 1);
261    let (low, high) = out.split_at_mut(two_h);
262    mod_mul_karatsuba_balanced(&mut low[..two_h - 1], x0, y0, d, scratch);
263    low[two_h - 1] = T::ZERO;
264    mod_mul_karatsuba_balanced(high, x1, y1, d, scratch);
265    x_sum.copy_from_slice(x1);
266    mod_add_assign_slice(x_sum, x0, m);
267    y_sum.copy_from_slice(y1);
268    mod_add_assign_slice(y_sum, y0, m);
269    mod_mul_karatsuba_balanced(middle, x_sum, y_sum, d, scratch);
270    mod_sub_assign_slice(middle, &out[..two_h - 1], m);
271    mod_sub_assign_slice(middle, &out[two_h..], m);
272    mod_add_assign_slice(&mut out[h..], middle, m);
273}
274
275// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty,
276// modulo $m$, by Karatsuba multiplication, falling back to schoolbook multiplication for short
277// factors. When the factors' lengths differ, the longer is cut into pieces as long as the shorter,
278// and the products of the pieces are added together.
279pub(crate) fn mod_mul_karatsuba<T: PrimitiveUnsigned>(
280    out: &mut [T],
281    xs: &[T],
282    ys: &[T],
283    d: &ModData<T>,
284) {
285    let (xs, ys) = if xs.len() >= ys.len() {
286        (xs, ys)
287    } else {
288        (ys, xs)
289    };
290    let n = xs.len();
291    let m = ys.len();
292    if m < MOD_MUL_KARATSUBA_THRESHOLD {
293        mod_mul_classical(out, xs, ys, d);
294        return;
295    }
296    let mut scratch = vec![T::ZERO; mod_karatsuba_scratch_len(m, MOD_MUL_KARATSUBA_THRESHOLD)];
297    if n == m {
298        mod_mul_karatsuba_balanced(out, xs, ys, d, &mut scratch);
299        return;
300    }
301    out.fill(T::ZERO);
302    let mut product = vec![T::ZERO; (m << 1) - 1];
303    for (k, piece) in xs.chunks(m).enumerate() {
304        let product = &mut product[..piece.len() + m - 1];
305        if piece.len() == m {
306            mod_mul_karatsuba_balanced(product, piece, ys, d, &mut scratch);
307        } else {
308            mod_mul_karatsuba(product, ys, piece, d);
309        }
310        mod_add_assign_slice(&mut out[k * m..], product, d.m);
311    }
312}
313
314fn assert_lengths<T>(out: &[T], xs: &[T], ys: &[T]) {
315    assert!(!xs.is_empty());
316    assert!(!ys.is_empty());
317    assert_eq!(out.len(), xs.len() + ys.len() - 1);
318}
319
320// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
321// reduced modulo `m`, modulo `m`, by schoolbook multiplication. `out` must have length `xs.len() +
322// ys.len() - 1`.
323crate_test_fn! {
324#[allow(dead_code)]
325mod_mul_to_out_classical<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T], ys: &[T], m: T) {
326    assert_lengths(out, xs, ys);
327    mod_mul_classical(out, xs, ys, &ModData::new(m, xs.len().min(ys.len())));
328}}
329
330// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
331// reduced modulo `m`, modulo `m`, by Karatsuba multiplication. `out` must have length `xs.len() +
332// ys.len() - 1`.
333crate_test_fn! {
334#[allow(dead_code)]
335mod_mul_to_out_karatsuba<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T], ys: &[T], m: T) {
336    assert_lengths(out, xs, ys);
337    mod_mul_karatsuba(out, xs, ys, &ModData::new(m, xs.len().min(ys.len())));
338}}
339
340/// Sets `out` to the product of the polynomials with coefficients `xs` and `ys`, both nonempty and
341/// reduced modulo `m`, modulo `m`. `out` must have length `xs.len() + ys.len() - 1`, and `m` must
342/// be positive.
343///
344/// This is not part of the public API; it is public so that `malachite-nz` can multiply
345/// `NaturalPolynomial`s modulo a word.
346#[doc(hidden)]
347pub fn mod_mul_to_out<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T], ys: &[T], m: T) {
348    assert_lengths(out, xs, ys);
349    mod_mul_karatsuba(out, xs, ys, &ModData::new(m, xs.len().min(ys.len())));
350}
351
352// The product of the polynomials with coefficients `xs` and `ys`, both reduced modulo `m`, modulo
353// `m`.
354pub(crate) fn mod_mul_helper<T: PrimitiveUnsigned>(
355    xs: &[T],
356    ys: &[T],
357    m: T,
358) -> UnsignedPolynomial<T> {
359    if xs.is_empty() || ys.is_empty() {
360        return UnsignedPolynomial::ZERO;
361    }
362    let mut out = vec![T::ZERO; xs.len() + ys.len() - 1];
363    mod_mul_to_out(&mut out, xs, ys, m);
364    from_coefficients_trimmed(out)
365}
366
367impl<T: PrimitiveUnsigned> ModMul<Self, T> for UnsignedPolynomial<T> {
368    type Output = Self;
369
370    /// Multiplies two [`UnsignedPolynomial`]s modulo $m$, taking both by value. The coefficients of
371    /// both must already be reduced modulo $m$.
372    ///
373    /// $$
374    /// f(p, q, m) = pq \bmod m.
375    /// $$
376    ///
377    /// When $m$ is not prime, the leading coefficient of the product can vanish modulo $m$, and
378    /// then the degree of the product is lower than the sum of the degrees.
379    ///
380    /// # Worst-case complexity
381    /// $T(n) = O(n^{\log_2 3})$
382    ///
383    /// $M(n) = O(n)$
384    ///
385    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
386    ///
387    /// # Panics
388    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
389    /// `m`.
390    ///
391    /// # Examples
392    /// ```
393    /// use core::str::FromStr;
394    /// use malachite_base::num::arithmetic::traits::ModMul;
395    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
396    ///
397    /// // The product is 2*x^3+11*x^2+19*x+10; its coefficients modulo 7.
398    /// assert_eq!(
399    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
400    ///         .unwrap()
401    ///         .mod_mul(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7)
402    ///         .to_string(),
403    ///     "2*x^3+4*x^2+5*x+3"
404    /// );
405    /// // The leading coefficient of the product, 6, vanishes modulo 6, so the degree drops.
406    /// assert_eq!(
407    ///     UnsignedPolynomial::<u8>::from_str("2*x+1")
408    ///         .unwrap()
409    ///         .mod_mul(UnsignedPolynomial::<u8>::from_str("3*x+1").unwrap(), 6)
410    ///         .to_string(),
411    ///     "5*x+1"
412    /// );
413    /// ```
414    ///
415    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
416    fn mod_mul(self, other: Self, m: T) -> Self {
417        assert_reduced(&self, &other, m);
418        mod_mul_helper(&self.coefficients, &other.coefficients, m)
419    }
420}
421
422impl<T: PrimitiveUnsigned> ModMul<&Self, T> for UnsignedPolynomial<T> {
423    type Output = Self;
424
425    /// Multiplies two [`UnsignedPolynomial`]s modulo $m$, taking the first by value and the second
426    /// by reference. The coefficients of both must already be reduced modulo $m$.
427    ///
428    /// $$
429    /// f(p, q, m) = pq \bmod m.
430    /// $$
431    ///
432    /// When $m$ is not prime, the leading coefficient of the product can vanish modulo $m$, and
433    /// then the degree of the product is lower than the sum of the degrees.
434    ///
435    /// # Worst-case complexity
436    /// $T(n) = O(n^{\log_2 3})$
437    ///
438    /// $M(n) = O(n)$
439    ///
440    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
441    ///
442    /// # Panics
443    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
444    /// `m`.
445    ///
446    /// # Examples
447    /// ```
448    /// use core::str::FromStr;
449    /// use malachite_base::num::arithmetic::traits::ModMul;
450    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
451    ///
452    /// // The product is 2*x^3+11*x^2+19*x+10; its coefficients modulo 7.
453    /// assert_eq!(
454    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
455    ///         .unwrap()
456    ///         .mod_mul(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7)
457    ///         .to_string(),
458    ///     "2*x^3+4*x^2+5*x+3"
459    /// );
460    /// // The leading coefficient of the product, 6, vanishes modulo 6, so the degree drops.
461    /// assert_eq!(
462    ///     UnsignedPolynomial::<u8>::from_str("2*x+1")
463    ///         .unwrap()
464    ///         .mod_mul(&UnsignedPolynomial::<u8>::from_str("3*x+1").unwrap(), 6)
465    ///         .to_string(),
466    ///     "5*x+1"
467    /// );
468    /// ```
469    ///
470    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
471    fn mod_mul(self, other: &Self, m: T) -> Self {
472        assert_reduced(&self, other, m);
473        mod_mul_helper(&self.coefficients, &other.coefficients, m)
474    }
475}
476
477impl<T: PrimitiveUnsigned> ModMul<UnsignedPolynomial<T>, T> for &UnsignedPolynomial<T> {
478    type Output = UnsignedPolynomial<T>;
479
480    /// Multiplies two [`UnsignedPolynomial`]s modulo $m$, taking the first by reference and the
481    /// second by value. The coefficients of both must already be reduced modulo $m$.
482    ///
483    /// $$
484    /// f(p, q, m) = pq \bmod m.
485    /// $$
486    ///
487    /// When $m$ is not prime, the leading coefficient of the product can vanish modulo $m$, and
488    /// then the degree of the product is lower than the sum of the degrees.
489    ///
490    /// # Worst-case complexity
491    /// $T(n) = O(n^{\log_2 3})$
492    ///
493    /// $M(n) = O(n)$
494    ///
495    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
496    ///
497    /// # Panics
498    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
499    /// `m`.
500    ///
501    /// # Examples
502    /// ```
503    /// use core::str::FromStr;
504    /// use malachite_base::num::arithmetic::traits::ModMul;
505    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
506    ///
507    /// // The product is 2*x^3+11*x^2+19*x+10; its coefficients modulo 7.
508    /// assert_eq!(
509    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
510    ///         .mod_mul(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7)
511    ///         .to_string(),
512    ///     "2*x^3+4*x^2+5*x+3"
513    /// );
514    /// // The leading coefficient of the product, 6, vanishes modulo 6, so the degree drops.
515    /// assert_eq!(
516    ///     (&UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap())
517    ///         .mod_mul(UnsignedPolynomial::<u8>::from_str("3*x+1").unwrap(), 6)
518    ///         .to_string(),
519    ///     "5*x+1"
520    /// );
521    /// ```
522    ///
523    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
524    fn mod_mul(self, other: UnsignedPolynomial<T>, m: T) -> UnsignedPolynomial<T> {
525        assert_reduced(self, &other, m);
526        mod_mul_helper(&self.coefficients, &other.coefficients, m)
527    }
528}
529
530impl<T: PrimitiveUnsigned> ModMul<&UnsignedPolynomial<T>, T> for &UnsignedPolynomial<T> {
531    type Output = UnsignedPolynomial<T>;
532
533    /// Multiplies two [`UnsignedPolynomial`]s modulo $m$, taking both by reference. The
534    /// coefficients of both must already be reduced modulo $m$.
535    ///
536    /// $$
537    /// f(p, q, m) = pq \bmod m.
538    /// $$
539    ///
540    /// When $m$ is not prime, the leading coefficient of the product can vanish modulo $m$, and
541    /// then the degree of the product is lower than the sum of the degrees.
542    ///
543    /// # Worst-case complexity
544    /// $T(n) = O(n^{\log_2 3})$
545    ///
546    /// $M(n) = O(n)$
547    ///
548    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
549    ///
550    /// # Panics
551    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
552    /// `m`.
553    ///
554    /// # Examples
555    /// ```
556    /// use core::str::FromStr;
557    /// use malachite_base::num::arithmetic::traits::ModMul;
558    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
559    ///
560    /// // The product is 2*x^3+11*x^2+19*x+10; its coefficients modulo 7.
561    /// assert_eq!(
562    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
563    ///         .mod_mul(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7)
564    ///         .to_string(),
565    ///     "2*x^3+4*x^2+5*x+3"
566    /// );
567    /// // The leading coefficient of the product, 6, vanishes modulo 6, so the degree drops.
568    /// assert_eq!(
569    ///     (&UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap())
570    ///         .mod_mul(&UnsignedPolynomial::<u8>::from_str("3*x+1").unwrap(), 6)
571    ///         .to_string(),
572    ///     "5*x+1"
573    /// );
574    /// ```
575    ///
576    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
577    fn mod_mul(self, other: &UnsignedPolynomial<T>, m: T) -> UnsignedPolynomial<T> {
578        assert_reduced(self, other, m);
579        mod_mul_helper(&self.coefficients, &other.coefficients, m)
580    }
581}
582
583impl<T: PrimitiveUnsigned> ModMulAssign<Self, T> for UnsignedPolynomial<T> {
584    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $m$ in place,
585    /// taking the right-hand side by value. The coefficients of both must already be reduced modulo
586    /// $m$.
587    ///
588    /// $$
589    /// p \gets pq \bmod m.
590    /// $$
591    ///
592    /// # Worst-case complexity
593    /// $T(n) = O(n^{\log_2 3})$
594    ///
595    /// $M(n) = O(n)$
596    ///
597    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
598    ///
599    /// # Panics
600    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
601    /// `m`.
602    ///
603    /// # Examples
604    /// ```
605    /// use core::str::FromStr;
606    /// use malachite_base::num::arithmetic::traits::ModMulAssign;
607    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
608    ///
609    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
610    /// p.mod_mul_assign(UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7);
611    /// assert_eq!(p.to_string(), "2*x^3+4*x^2+5*x+3");
612    /// ```
613    ///
614    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
615    fn mod_mul_assign(&mut self, other: Self, m: T) {
616        assert_reduced(self, &other, m);
617        *self = mod_mul_helper(&self.coefficients, &other.coefficients, m);
618    }
619}
620
621impl<T: PrimitiveUnsigned> ModMulAssign<&Self, T> for UnsignedPolynomial<T> {
622    /// Multiplies an [`UnsignedPolynomial`] by another [`UnsignedPolynomial`] modulo $m$ in place,
623    /// taking the right-hand side by reference. The coefficients of both must already be reduced
624    /// modulo $m$.
625    ///
626    /// $$
627    /// p \gets pq \bmod m.
628    /// $$
629    ///
630    /// # Worst-case complexity
631    /// $T(n) = O(n^{\log_2 3})$
632    ///
633    /// $M(n) = O(n)$
634    ///
635    /// where $T$ is time, $M$ is additional memory, and $n$ is the length of the longer polynomial.
636    ///
637    /// # Panics
638    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
639    /// `m`.
640    ///
641    /// # Examples
642    /// ```
643    /// use core::str::FromStr;
644    /// use malachite_base::num::arithmetic::traits::ModMulAssign;
645    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
646    ///
647    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
648    /// p.mod_mul_assign(&UnsignedPolynomial::<u8>::from_str("2*x+5").unwrap(), 7);
649    /// assert_eq!(p.to_string(), "2*x^3+4*x^2+5*x+3");
650    /// ```
651    ///
652    /// This is equivalent to `nmod_poly_mul` from `nmod_poly/mul.c`, FLINT 3.6.0.
653    fn mod_mul_assign(&mut self, other: &Self, m: T) {
654        assert_reduced(self, other, m);
655        *self = mod_mul_helper(&self.coefficients, &other.coefficients, m);
656    }
657}