Skip to main content

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}