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}