Skip to main content

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}