Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_power_of_2_square_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::arithmetic::traits::ModPowerOf2IsReduced;
9use crate::num::basic::traits::Zero;
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::polynomial::{ModPowerOf2SquareTruncated, ModPowerOf2SquareTruncatedAssign};
12use crate::unsigned_polynomial::UnsignedPolynomial;
13use crate::unsigned_polynomial::arithmetic::mod_power_of_2_mul::{
14    MOD_POWER_OF_2_SQUARE_KARATSUBA_THRESHOLD, add_wrapping_assign, from_coefficients_trimmed,
15    mask_coefficients,
16};
17use crate::unsigned_polynomial::arithmetic::mod_power_of_2_mul_truncated::*;
18use crate::unsigned_polynomial::arithmetic::mod_power_of_2_square::square_karatsuba_wrapping;
19use alloc::vec;
20use core::cmp::min;
21
22// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
23// coefficients `xs`, modulo $2^\text{W}$, by schoolbook multiplication: each product of two
24// different coefficients is computed once and doubled.
25pub(crate) fn square_truncated_classical_wrapping<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T]) {
26    let len = out.len();
27    out.fill(T::ZERO);
28    for (i, &x) in xs.iter().enumerate() {
29        if (i << 1) + 1 >= len {
30            break;
31        }
32        if x != T::ZERO {
33            for (o, &y) in out[(i << 1) + 1..].iter_mut().zip(&xs[i + 1..]) {
34                o.wrapping_add_assign(x.wrapping_mul(y));
35            }
36        }
37    }
38    for o in out.iter_mut() {
39        *o = o.wrapping_add(*o);
40    }
41    for (o, &x) in out.iter_mut().step_by(2).zip(xs) {
42        o.wrapping_add_assign(x.wrapping_mul(x));
43    }
44}
45
46// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
47// coefficients `xs`, which is nonempty, modulo $2^\text{W}$. With $n$ equal to `out.len()` and $h =
48// \lceil n/2 \rceil$, write x = x_0 + x^h x_1; since $2h \geq n$, x^2 mod x^n is x_0^2 + 2 x^h x_0
49// x_1 mod x^n. The square is a full square of a half-length polynomial, computed by Karatsuba
50// multiplication, and the cross product is a truncated product of half the length.
51pub(crate) fn square_truncated_karatsuba_wrapping<T: PrimitiveUnsigned>(out: &mut [T], xs: &[T]) {
52    let len = out.len();
53    let xs = &xs[..min(xs.len(), len)];
54    let n = xs.len();
55    let full_len = (n << 1) - 1;
56    if full_len <= len {
57        square_karatsuba_wrapping(&mut out[..full_len], xs);
58        out[full_len..].fill(T::ZERO);
59        return;
60    }
61    if n < MOD_POWER_OF_2_SQUARE_KARATSUBA_THRESHOLD {
62        square_truncated_classical_wrapping(out, xs);
63        return;
64    }
65    let h = len.div_ceil(2);
66    let x0 = &xs[..min(h, n)];
67    // The square of x_0 has at most 2h - 1 <= `len` coefficients, so this is a full square.
68    square_truncated_karatsuba_wrapping(out, x0);
69    if n > h {
70        let mut cross = vec![T::ZERO; len - h];
71        mul_truncated_karatsuba_wrapping(&mut cross, x0, &xs[h..]);
72        add_wrapping_assign(&mut out[h..], &cross);
73        add_wrapping_assign(&mut out[h..], &cross);
74    }
75}
76
77fn assert_lengths<T>(out: &[T], xs: &[T]) {
78    assert!(!out.is_empty());
79    assert!(!xs.is_empty());
80}
81
82// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
83// coefficients `xs`, both nonempty and the input reduced modulo $2^k$, where $k$ is `pow`, by
84// schoolbook multiplication. `pow` must be no greater than `T::WIDTH`.
85crate_test_fn! {
86#[allow(dead_code)]
87mod_power_of_2_square_truncated_to_out_classical<T: PrimitiveUnsigned>(
88    out: &mut [T],
89    xs: &[T],
90    pow: u64,
91) {
92    assert_lengths(out, xs);
93    assert!(pow <= T::WIDTH);
94    square_truncated_classical_wrapping(out, xs);
95    mask_coefficients(out, pow);
96}}
97
98// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
99// coefficients `xs`, both nonempty and the input reduced modulo $2^k$, where $k$ is `pow`, by
100// Karatsuba multiplication. `pow` must be no greater than `T::WIDTH`.
101crate_test_fn! {
102#[allow(dead_code)]
103mod_power_of_2_square_truncated_to_out_karatsuba<T: PrimitiveUnsigned>(
104    out: &mut [T],
105    xs: &[T],
106    pow: u64,
107) {
108    assert_lengths(out, xs);
109    assert!(pow <= T::WIDTH);
110    square_truncated_karatsuba_wrapping(out, xs);
111    mask_coefficients(out, pow);
112}}
113
114/// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
115/// coefficients `xs`, both nonempty and the input reduced modulo $2^k$, where $k$ is `pow`. `pow`
116/// must be no greater than `T::WIDTH`.
117///
118/// This is not part of the public API; it is public so that `malachite-nz` can square
119/// `NaturalPolynomial`s with word-sized coefficients modulo $2^k$.
120#[doc(hidden)]
121pub fn mod_power_of_2_square_truncated_to_out<T: PrimitiveUnsigned>(
122    out: &mut [T],
123    xs: &[T],
124    pow: u64,
125) {
126    assert_lengths(out, xs);
127    assert!(pow <= T::WIDTH);
128    square_truncated_karatsuba_wrapping(out, xs);
129    mask_coefficients(out, pow);
130}
131
132fn assert_reduced<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, pow: u64) {
133    assert!(pow <= T::WIDTH);
134    assert!(
135        p.mod_power_of_2_is_reduced(pow),
136        "self must be reduced mod 2^pow, but {p} has a coefficient >= 2^{pow}"
137    );
138}
139
140// The square of the polynomial with coefficients `xs`, reduced modulo $2^k$, where $k$ is `pow`,
141// truncated to `len` coefficients and reduced modulo $2^k$.
142pub(crate) fn mod_power_of_2_square_truncated_helper<T: PrimitiveUnsigned>(
143    xs: &[T],
144    len: u64,
145    pow: u64,
146) -> UnsignedPolynomial<T> {
147    if len == 0 || xs.is_empty() {
148        return UnsignedPolynomial::ZERO;
149    }
150    let mut out = vec![T::ZERO; truncated_len(xs.len(), xs.len(), len)];
151    mod_power_of_2_square_truncated_to_out(&mut out, xs, pow);
152    from_coefficients_trimmed(out)
153}
154
155impl<T: PrimitiveUnsigned> ModPowerOf2SquareTruncated for UnsignedPolynomial<T> {
156    type Output = Self;
157
158    /// Squares an [`UnsignedPolynomial`] modulo $2^k$, keeping only the coefficients of $x^i$ for
159    /// $i$ less than `len`, taking it by value. Its coefficients must already be reduced modulo
160    /// $2^k$.
161    ///
162    /// $$
163    /// f(p, n, k) = (p^2 \bmod x^n) \bmod 2^k.
164    /// $$
165    ///
166    /// The polynomial need not already be truncated: this is the square of its image modulo $x^n$,
167    /// so only its first `len` coefficients are read.
168    ///
169    /// # Worst-case complexity
170    /// $T(n) = O(n^{\log_2 3})$
171    ///
172    /// $M(n) = O(n)$
173    ///
174    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
175    ///
176    /// # Panics
177    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` is greater than
178    /// or equal to $2^k$.
179    ///
180    /// # Examples
181    /// ```
182    /// use core::str::FromStr;
183    /// use malachite_base::polynomial::ModPowerOf2SquareTruncated;
184    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
185    ///
186    /// // The square is x^4+6*x^3+13*x^2+12*x+4; its low three coefficients, modulo 8.
187    /// assert_eq!(
188    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2")
189    ///         .unwrap()
190    ///         .mod_power_of_2_square_truncated(3, 3)
191    ///         .to_string(),
192    ///     "5*x^2+4*x+4"
193    /// );
194    /// // The linear coefficient of the square, 8, vanishes modulo 8.
195    /// assert_eq!(
196    ///     UnsignedPolynomial::<u8>::from_str("4*x+1")
197    ///         .unwrap()
198    ///         .mod_power_of_2_square_truncated(2, 3)
199    ///         .to_string(),
200    ///     "1"
201    /// );
202    /// ```
203    ///
204    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with both
205    /// factors equal and the modulus $2^k$.
206    fn mod_power_of_2_square_truncated(self, len: u64, pow: u64) -> Self {
207        assert_reduced(&self, pow);
208        mod_power_of_2_square_truncated_helper(&self.coefficients, len, pow)
209    }
210}
211
212impl<T: PrimitiveUnsigned> ModPowerOf2SquareTruncated for &UnsignedPolynomial<T> {
213    type Output = UnsignedPolynomial<T>;
214
215    /// Squares an [`UnsignedPolynomial`] modulo $2^k$, keeping only the coefficients of $x^i$ for
216    /// $i$ less than `len`, taking it by reference. Its coefficients must already be reduced modulo
217    /// $2^k$.
218    ///
219    /// $$
220    /// f(p, n, k) = (p^2 \bmod x^n) \bmod 2^k.
221    /// $$
222    ///
223    /// The polynomial need not already be truncated: this is the square of its image modulo $x^n$,
224    /// so only its first `len` coefficients are read.
225    ///
226    /// # Worst-case complexity
227    /// $T(n) = O(n^{\log_2 3})$
228    ///
229    /// $M(n) = O(n)$
230    ///
231    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
232    ///
233    /// # Panics
234    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` is greater than
235    /// or equal to $2^k$.
236    ///
237    /// # Examples
238    /// ```
239    /// use core::str::FromStr;
240    /// use malachite_base::polynomial::ModPowerOf2SquareTruncated;
241    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
242    ///
243    /// // The square is x^4+6*x^3+13*x^2+12*x+4; its low three coefficients, modulo 8.
244    /// assert_eq!(
245    ///     (&UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap())
246    ///         .mod_power_of_2_square_truncated(3, 3)
247    ///         .to_string(),
248    ///     "5*x^2+4*x+4"
249    /// );
250    /// // The linear coefficient of the square, 8, vanishes modulo 8.
251    /// assert_eq!(
252    ///     (&UnsignedPolynomial::<u8>::from_str("4*x+1").unwrap())
253    ///         .mod_power_of_2_square_truncated(2, 3)
254    ///         .to_string(),
255    ///     "1"
256    /// );
257    /// ```
258    ///
259    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with both
260    /// factors equal and the modulus $2^k$.
261    fn mod_power_of_2_square_truncated(self, len: u64, pow: u64) -> UnsignedPolynomial<T> {
262        assert_reduced(self, pow);
263        mod_power_of_2_square_truncated_helper(&self.coefficients, len, pow)
264    }
265}
266
267impl<T: PrimitiveUnsigned> ModPowerOf2SquareTruncatedAssign for UnsignedPolynomial<T> {
268    /// Squares an [`UnsignedPolynomial`] modulo $2^k$ in place, keeping only the coefficients of
269    /// $x^i$ for $i$ less than `len`. Its coefficients must already be reduced modulo $2^k$.
270    ///
271    /// $$
272    /// p \gets (p^2 \bmod x^n) \bmod 2^k.
273    /// $$
274    ///
275    /// # Worst-case complexity
276    /// $T(n) = O(n^{\log_2 3})$
277    ///
278    /// $M(n) = O(n)$
279    ///
280    /// where $T$ is time, $M$ is additional memory, and $n$ is `len`.
281    ///
282    /// # Panics
283    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` is greater than
284    /// or equal to $2^k$.
285    ///
286    /// # Examples
287    /// ```
288    /// use core::str::FromStr;
289    /// use malachite_base::polynomial::ModPowerOf2SquareTruncatedAssign;
290    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
291    ///
292    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap();
293    /// p.mod_power_of_2_square_truncated_assign(3, 3);
294    /// assert_eq!(p.to_string(), "5*x^2+4*x+4");
295    /// ```
296    ///
297    /// This is equivalent to `nmod_poly_mullow` from `nmod_poly/mullow.c`, FLINT 3.6.0, with both
298    /// factors equal and the modulus $2^k$.
299    fn mod_power_of_2_square_truncated_assign(&mut self, len: u64, pow: u64) {
300        assert_reduced(self, pow);
301        *self = mod_power_of_2_square_truncated_helper(&self.coefficients, len, pow);
302    }
303}