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