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}