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}