malachite_nz/integer_polynomial/arithmetic/square/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 © 2014 Fredrik Johansson
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::integer_polynomial::IntegerPolynomial;
16use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
17use crate::integer_polynomial::arithmetic::mul_middle::fft::mul_middle_to_out_fft;
18use crate::integer_polynomial::arithmetic::square::classical::square_to_out_classical;
19use crate::integer_polynomial::arithmetic::square::karatsuba::square_to_out_karatsuba;
20use crate::integer_polynomial::arithmetic::square::kronecker::square_to_out_kronecker;
21use crate::integer_polynomial::arithmetic::square::schonhage_strassen::*;
22use crate::integer_polynomial::arithmetic::square::tiny::{
23 square_to_out_tiny_1, square_to_out_tiny_2,
24};
25use crate::integer_polynomial::arithmetic::vec::max_bits::vec_max_bits;
26use crate::integer_polynomial::arithmetic::vec::{
27 TinyKernel, classical_preferred, fft_preferred, karatsuba_preferred,
28 schonhage_strassen_preferred, tiny_kernel,
29};
30use alloc::vec;
31use alloc::vec::Vec;
32use malachite_base::num::arithmetic::traits::{Square, SquareAssign};
33use malachite_base::num::conversion::traits::ExactFrom;
34
35pub mod classical;
36pub mod karatsuba;
37pub mod kronecker;
38pub mod schonhage_strassen;
39pub mod tiny;
40
41// Sets `out` to the coefficients of the square of the polynomial with coefficients `xs`, which is
42// nonempty. `out` must have length `2 * xs.len() - 1`.
43//
44// # Worst-case complexity
45// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
46//
47// $M(n, m) = O(n(m + \log n) \log (nm))$
48//
49// where $T$ is time, $M$ is additional memory, $n$ is `xs.len()`, and $m$ is the largest number of
50// significant bits of any element of `xs`.
51//
52// This is equivalent to `_fmpz_poly_sqr` from `fmpz_poly/sqr.c`, FLINT 3.6.0, except that it
53// chooses Schönhage–Strassen in a measured window (see `schonhage_strassen_preferred`) rather
54// than FLINT's.
55crate_test_fn! {square_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C]) {
56 if xs.len() == 1 {
57 out[0] = xs[0].square_ref();
58 return;
59 }
60 let bits = vec_max_bits(xs).0;
61 let len = u64::exact_from(xs.len());
62 if fft_preferred(len, bits, bits, 80, 160)
63 && mul_middle_to_out_fft(out, xs, xs, 0, (xs.len() << 1) - 1)
64 {
65 return;
66 }
67 match tiny_kernel(bits, bits, len, len < 50 + 3 * bits) {
68 Some(TinyKernel::OneWord) => square_to_out_tiny_1(out, xs),
69 Some(TinyKernel::TwoWord) => square_to_out_tiny_2(out, xs),
70 None if classical_preferred(len, bits, bits) => {
71 square_to_out_classical(out, xs);
72 }
73 None if karatsuba_preferred(len, bits, bits) => {
74 square_to_out_karatsuba(out, xs);
75 }
76 None if schonhage_strassen_preferred(len, len, bits, bits, 4097) => {
77 square_to_out_schonhage_strassen(out, xs);
78 }
79 None => square_to_out_kronecker(out, xs),
80 }
81}}
82
83// This is equivalent to `fmpz_poly_sqr` from `fmpz_poly/sqr.c`, FLINT 3.6.0.
84pub(crate) fn square_ref<C: PolynomialCoefficient>(xs: &[C]) -> Vec<C> {
85 if xs.is_empty() {
86 return Vec::new();
87 }
88 let mut out = vec![C::ZERO; (xs.len() << 1) - 1];
89 square_to_out(&mut out, xs);
90 out
91}
92
93impl Square for IntegerPolynomial {
94 type Output = Self;
95
96 /// Squares an [`IntegerPolynomial`], taking it by value.
97 ///
98 /// $$
99 /// f(p) = p^2.
100 /// $$
101 ///
102 /// Squaring takes roughly half the coefficient multiplications of multiplying two different
103 /// polynomials of the same length.
104 ///
105 /// # Worst-case complexity
106 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
107 ///
108 /// $M(n, m) = O(n(m + \log n) \log (nm))$
109 ///
110 /// where $T$ is time, $M$ is additional memory, $n$ is the length of the polynomial, and $m$ is
111 /// the largest number of significant bits of any of its coefficients.
112 ///
113 /// # Examples
114 /// ```
115 /// use core::str::FromStr;
116 /// use malachite_base::num::arithmetic::traits::Square;
117 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
118 ///
119 /// assert_eq!(
120 /// (IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
121 /// .square()
122 /// .to_string(),
123 /// "x^4-6*x^3+13*x^2-12*x+4"
124 /// );
125 /// assert_eq!(
126 /// (IntegerPolynomial::from_str("-x+1").unwrap())
127 /// .square()
128 /// .to_string(),
129 /// "x^2-2*x+1"
130 /// );
131 /// ```
132 ///
133 /// This is equivalent to `fmpz_poly_sqr` from `fmpz_poly/sqr.c`, FLINT 3.6.0.
134 #[inline]
135 fn square(mut self) -> Self {
136 self.square_assign();
137 self
138 }
139}
140
141impl Square for &IntegerPolynomial {
142 type Output = IntegerPolynomial;
143
144 /// Squares an [`IntegerPolynomial`], taking it by reference.
145 ///
146 /// $$
147 /// f(p) = p^2.
148 /// $$
149 ///
150 /// Squaring takes roughly half the coefficient multiplications of multiplying two different
151 /// polynomials of the same length.
152 ///
153 /// # Worst-case complexity
154 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
155 ///
156 /// $M(n, m) = O(n(m + \log n) \log (nm))$
157 ///
158 /// where $T$ is time, $M$ is additional memory, $n$ is the length of the polynomial, and $m$ is
159 /// the largest number of significant bits of any of its coefficients.
160 ///
161 /// # Examples
162 /// ```
163 /// use core::str::FromStr;
164 /// use malachite_base::num::arithmetic::traits::Square;
165 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
166 ///
167 /// assert_eq!(
168 /// (&IntegerPolynomial::from_str("x^2-3*x+2").unwrap())
169 /// .square()
170 /// .to_string(),
171 /// "x^4-6*x^3+13*x^2-12*x+4"
172 /// );
173 /// assert_eq!(
174 /// (&IntegerPolynomial::from_str("-x+1").unwrap())
175 /// .square()
176 /// .to_string(),
177 /// "x^2-2*x+1"
178 /// );
179 /// ```
180 ///
181 /// This is equivalent to `fmpz_poly_sqr` from `fmpz_poly/sqr.c`, FLINT 3.6.0.
182 #[inline]
183 fn square(self) -> IntegerPolynomial {
184 IntegerPolynomial {
185 coefficients: square_ref(&self.coefficients),
186 }
187 }
188}
189
190impl SquareAssign for IntegerPolynomial {
191 /// Squares an [`IntegerPolynomial`] in place.
192 ///
193 /// $$
194 /// p \gets p^2.
195 /// $$
196 ///
197 /// # Worst-case complexity
198 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
199 ///
200 /// $M(n, m) = O(n(m + \log n) \log (nm))$
201 ///
202 /// where $T$ is time, $M$ is additional memory, $n$ is the length of the polynomial, and $m$ is
203 /// the largest number of significant bits of any of its coefficients.
204 ///
205 /// # Examples
206 /// ```
207 /// use core::str::FromStr;
208 /// use malachite_base::num::arithmetic::traits::SquareAssign;
209 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
210 ///
211 /// let mut p = IntegerPolynomial::from_str("x^2-3*x+2").unwrap();
212 /// p.square_assign();
213 /// assert_eq!(p.to_string(), "x^4-6*x^3+13*x^2-12*x+4");
214 /// ```
215 ///
216 /// This is equivalent to `fmpz_poly_sqr` from `fmpz_poly/sqr.c`, FLINT 3.6.0.
217 #[inline]
218 fn square_assign(&mut self) {
219 // The square of a constant is computed in place.
220 if let [c] = self.coefficients.as_mut_slice() {
221 c.square_assign();
222 } else {
223 self.coefficients = square_ref(&self.coefficients);
224 }
225 }
226}