malachite_nz/integer_polynomial/arithmetic/square_truncated/mod.rs
1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5// Copyright © 2008, 2009 William Hart
6//
7// Copyright © 2010, 2011 Sebastian Pancratz
8//
9// Copyright © 2014 Fredrik Johansson
10//
11// This file is part of Malachite.
12//
13// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
14// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
15// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
16
17use crate::integer_polynomial::IntegerPolynomial;
18use crate::integer_polynomial::arithmetic::coefficient::{
19 PolynomialCoefficient, trim_coefficients,
20};
21use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
22use crate::integer_polynomial::arithmetic::square_truncated::classical::*;
23use crate::integer_polynomial::arithmetic::square_truncated::karatsuba::*;
24use crate::integer_polynomial::arithmetic::square_truncated::kronecker::*;
25use crate::integer_polynomial::arithmetic::square_truncated::schonhage_strassen::*;
26use crate::integer_polynomial::arithmetic::square_truncated::tiny::{
27 square_truncated_to_out_tiny_1, square_truncated_to_out_tiny_2,
28};
29use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
30use crate::integer_polynomial::arithmetic::vec::{
31 TinyKernel, classical_preferred, fft_preferred, karatsuba_preferred,
32 schonhage_strassen_preferred, tiny_kernel,
33};
34use alloc::vec;
35use alloc::vec::Vec;
36use core::cmp::min;
37use malachite_base::num::arithmetic::traits::SquareAssign;
38use malachite_base::num::conversion::traits::ExactFrom;
39use malachite_base::polynomial::{SquareTruncated, SquareTruncatedAssign};
40
41pub mod classical;
42pub mod karatsuba;
43pub mod kronecker;
44pub mod schonhage_strassen;
45pub mod tiny;
46
47// Sets `out` to the first `out.len()` coefficients of the square of the polynomial with
48// coefficients `xs`, which is nonempty. `out.len()` must be positive and at most `2 * xs.len() -
49// 1`.
50//
51// # Worst-case complexity
52// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
53//
54// $M(n, m) = O(n(m + \log n) \log (nm))$
55//
56// where $T$ is time, $M$ is additional memory, $n$ is `out.len()`, and $m$ is the largest number of
57// significant bits of any element of `xs`.
58//
59// This is equivalent to `_fmpz_poly_sqrlow` from `fmpz_poly/sqrlow.c`, FLINT 3.6.0, where `n` is
60// `out.len()`, except that it chooses Schönhage–Strassen in a measured window (see
61// `schonhage_strassen_preferred`) rather than FLINT's.
62crate_test_fn! {square_truncated_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C]) {
63 let n = out.len();
64 let xs = &xs[..min(xs.len(), n)];
65 if xs.len() == 1 {
66 out[0] = xs[0].square_ref();
67 return;
68 }
69 let bits = vec_max_bits(xs).0;
70 let len = u64::exact_from(xs.len());
71 if fft_preferred(len, bits, bits, 100, 240) && mul_middle_to_out_fft(out, xs, xs, 0, n) {
72 return;
73 }
74 let n = u64::exact_from(n);
75 let short_enough = len < 50 + (bits << 1) || (len << 2 >= 3 * n && n < 140 + 6 * bits);
76 match tiny_kernel(bits, bits, len, short_enough) {
77 Some(TinyKernel::OneWord) => square_truncated_to_out_tiny_1(out, xs),
78 Some(TinyKernel::TwoWord) => square_truncated_to_out_tiny_2(out, xs),
79 None if classical_preferred(len, bits, bits) => {
80 square_truncated_to_out_classical(out, xs);
81 }
82 None if karatsuba_preferred(len, bits, bits) => {
83 square_truncated_to_out_karatsuba(out, xs);
84 }
85 None if schonhage_strassen_preferred(len, len, bits, bits, 4097) => {
86 square_truncated_to_out_schonhage_strassen(out, xs);
87 }
88 None => square_truncated_to_out_kronecker(out, xs),
89 }
90}}
91
92// The coefficients of the square of the polynomial with coefficients `xs`, keeping only the
93// coefficients of $x^i$ for $i$ less than `len`, without zeros at the end.
94//
95// This is equivalent to `fmpz_poly_sqrlow` from `fmpz_poly/sqrlow.c`, FLINT 3.6.0.
96pub(crate) fn square_truncated_ref<C: PolynomialCoefficient>(xs: &[C], len: u64) -> Vec<C> {
97 if xs.is_empty() || len == 0 {
98 return Vec::new();
99 }
100 let n = usize::try_from(len)
101 .unwrap_or(usize::MAX)
102 .min((xs.len() << 1) - 1);
103 let mut out = vec![C::ZERO; n];
104 square_truncated_to_out(&mut out, xs);
105 trim_coefficients(&mut out);
106 out
107}
108
109impl SquareTruncated for IntegerPolynomial {
110 type Output = Self;
111
112 /// Squares an [`IntegerPolynomial`], keeping only the coefficients of $x^i$ for $i$ less than
113 /// `len`, taking it by value.
114 ///
115 /// $$
116 /// f(p, n) = p^2 \bmod x^n.
117 /// $$
118 ///
119 /// The polynomial need not already be truncated: this is the square of its image modulo $x^n$,
120 /// so only its first `len` coefficients are read.
121 ///
122 /// # Worst-case complexity
123 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
124 ///
125 /// $M(n, m) = O(n(m + \log n) \log (nm))$
126 ///
127 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
128 /// significant bits of any of the first `len` coefficients of the polynomial.
129 ///
130 /// # Examples
131 /// ```
132 /// use core::str::FromStr;
133 /// use malachite_base::polynomial::SquareTruncated;
134 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
135 ///
136 /// assert_eq!(
137 /// (IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
138 /// .square_truncated(3)
139 /// .to_string(),
140 /// "13*x^2-12*x+4"
141 /// );
142 /// // The cross terms combine with the square of the linear coefficient.
143 /// assert_eq!(
144 /// (IntegerPolynomial::from_str("x^2+x-1").unwrap())
145 /// .square_truncated(3)
146 /// .to_string(),
147 /// "-x^2-2*x+1"
148 /// );
149 /// ```
150 ///
151 /// This is equivalent to `fmpz_poly_sqrlow` from `fmpz_poly/sqrlow.c`, FLINT 3.6.0.
152 #[inline]
153 fn square_truncated(mut self, len: u64) -> Self {
154 self.square_truncated_assign(len);
155 self
156 }
157}
158
159impl SquareTruncated for &IntegerPolynomial {
160 type Output = IntegerPolynomial;
161
162 /// Squares an [`IntegerPolynomial`], keeping only the coefficients of $x^i$ for $i$ less than
163 /// `len`, taking it by reference.
164 ///
165 /// $$
166 /// f(p, n) = p^2 \bmod x^n.
167 /// $$
168 ///
169 /// The polynomial need not already be truncated: this is the square of its image modulo $x^n$,
170 /// so only its first `len` coefficients are read.
171 ///
172 /// # Worst-case complexity
173 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
174 ///
175 /// $M(n, m) = O(n(m + \log n) \log (nm))$
176 ///
177 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
178 /// significant bits of any of the first `len` coefficients of the polynomial.
179 ///
180 /// # Examples
181 /// ```
182 /// use core::str::FromStr;
183 /// use malachite_base::polynomial::SquareTruncated;
184 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
185 ///
186 /// assert_eq!(
187 /// (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
188 /// .square_truncated(3)
189 /// .to_string(),
190 /// "13*x^2-12*x+4"
191 /// );
192 /// // The cross terms combine with the square of the linear coefficient.
193 /// assert_eq!(
194 /// (&IntegerPolynomial::from_str("x^2+x-1").unwrap())
195 /// .square_truncated(3)
196 /// .to_string(),
197 /// "-x^2-2*x+1"
198 /// );
199 /// ```
200 ///
201 /// This is equivalent to `fmpz_poly_sqrlow` from `fmpz_poly/sqrlow.c`, FLINT 3.6.0.
202 #[inline]
203 fn square_truncated(self, len: u64) -> IntegerPolynomial {
204 IntegerPolynomial {
205 coefficients: square_truncated_ref(&self.coefficients, len),
206 }
207 }
208}
209
210impl SquareTruncatedAssign for IntegerPolynomial {
211 /// Squares an [`IntegerPolynomial`] in place, keeping only the coefficients of $x^i$ for $i$
212 /// less than `len`.
213 ///
214 /// $$
215 /// p \gets p^2 \bmod x^n.
216 /// $$
217 ///
218 /// # Worst-case complexity
219 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
220 ///
221 /// $M(n, m) = O(n(m + \log n) \log (nm))$
222 ///
223 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is the largest number of
224 /// significant bits of any of the first `len` coefficients of the polynomial.
225 ///
226 /// # Examples
227 /// ```
228 /// use core::str::FromStr;
229 /// use malachite_base::polynomial::SquareTruncatedAssign;
230 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
231 ///
232 /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
233 /// p.square_truncated_assign(3);
234 /// assert_eq!(p.to_string(), "13*x^2-12*x+4");
235 /// ```
236 ///
237 /// This is equivalent to `fmpz_poly_sqrlow` from `fmpz_poly/sqrlow.c`, FLINT 3.6.0.
238 #[inline]
239 fn square_truncated_assign(&mut self, len: u64) {
240 // The square of a constant is computed in place.
241 if len != 0
242 && let [c] = self.coefficients.as_mut_slice()
243 {
244 c.square_assign();
245 } else {
246 self.coefficients = square_truncated_ref(&self.coefficients, len);
247 }
248 }
249}