Skip to main content

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}