malachite_nz/integer_polynomial/arithmetic/pow_truncated.rs
1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5// Copyright © 2010 Sebastian Pancratz
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::integer_polynomial::IntegerPolynomial;
14use crate::integer_polynomial::arithmetic::coefficient::{
15 PolynomialCoefficient, trim_coefficients, truncate_coefficients,
16};
17use crate::integer_polynomial::arithmetic::mul_truncated::mul_truncated_to_out;
18use crate::integer_polynomial::arithmetic::pow::binexp::binexp_start;
19use crate::integer_polynomial::arithmetic::square_truncated::square_truncated_to_out;
20use alloc::vec;
21use alloc::vec::Vec;
22use core::mem::swap;
23use malachite_base::num::arithmetic::traits::PowAssign;
24use malachite_base::num::conversion::traits::SaturatingFrom;
25use malachite_base::polynomial::{PowTruncated, PowTruncatedAssign};
26
27// Sets `out` to the first `out.len()` coefficients of the `e`th power of the polynomial with
28// coefficients `xs`, which is nonempty, where `e` is at least 3. `out.len()` must be positive and
29// at most `e * (xs.len() - 1) + 1`.
30//
31// This is left-to-right binary exponentiation with truncated squaring and multiplication,
32// alternating between `out` and a scratch buffer as in `pow_to_out_binexp`. Each intermediate power
33// is kept at its true length, capped at `out.len()`.
34//
35// This is equivalent to `_fmpz_poly_pow_trunc` from `fmpz_poly/pow_trunc.c`, FLINT 3.6.0, where `n`
36// is `out.len()`, except that FLINT pads the polynomial with zeros to length `n` and computes every
37// intermediate power to length `n`.
38crate_test_fn! {pow_truncated_to_out<C: PolynomialCoefficient>(out: &mut [C], xs: &[C], e: u64) {
39 let n = out.len();
40 let xs = &xs[..xs.len().min(n)];
41 let len = xs.len();
42 let mut v = vec![C::ZERO; n];
43 let (mut bit, swaps) = binexp_start(e);
44 let (mut r, mut s): (&mut [C], &mut [C]) = if swaps { (&mut v, out) } else { (out, &mut v) };
45 // The first step squares xs itself
46 let mut rlen = ((len << 1) - 1).min(n);
47 square_truncated_to_out(&mut r[..rlen], xs);
48 if bit & e != 0 {
49 let new_len = (rlen + len - 1).min(n);
50 mul_truncated_to_out(&mut s[..new_len], &r[..rlen], xs);
51 rlen = new_len;
52 swap(&mut r, &mut s);
53 }
54 loop {
55 bit >>= 1;
56 if bit == 0 {
57 break;
58 }
59 let new_len = ((rlen << 1) - 1).min(n);
60 square_truncated_to_out(&mut s[..new_len], &r[..rlen]);
61 rlen = new_len;
62 if bit & e != 0 {
63 let new_len = (rlen + len - 1).min(n);
64 mul_truncated_to_out(&mut r[..new_len], &s[..rlen], xs);
65 rlen = new_len;
66 } else {
67 swap(&mut r, &mut s);
68 }
69 }
70}}
71
72// Returns the coefficients of the `e`th power of the polynomial with coefficients `xs`, which has
73// no zeros at the end, keeping only the coefficients of $x^i$ for $i$ less than `len`, without
74// zeros at the end.
75//
76// Writing the polynomial modulo $x^n$ as $x^\ell q$, with $q_0 \neq 0$, its power is $x^{e\ell}
77// (q^e \bmod x^{n - e\ell})$, or 0 if $e\ell \geq n$.
78//
79// This is equivalent to `fmpz_poly_pow_trunc` from `fmpz_poly/pow_trunc.c`, FLINT 3.6.0, except
80// that it removes the factor of $x^\ell$, and that `n` is capped at the length of the untruncated
81// power, so that a `len` too large to allocate means no truncation.
82crate_test_fn! {pow_truncated_ref<C: PolynomialCoefficient>(xs: &[C], e: u64, len: u64) -> Vec<C> {
83 if len == 0 {
84 return Vec::new();
85 }
86 if e == 0 {
87 return vec![C::ONE];
88 }
89 let n = usize::saturating_from(len);
90 let xs = &xs[..xs.len().min(n)];
91 let Some(low) = xs.iter().position(|x| !x.is_zero()) else {
92 return Vec::new();
93 };
94 let shift = usize::saturating_from(e).saturating_mul(low);
95 if shift >= n {
96 return Vec::new();
97 }
98 let q = &xs[low..];
99 let q_len = (n - shift).min(
100 usize::saturating_from(e)
101 .saturating_mul(q.len() - 1)
102 .saturating_add(1),
103 );
104 let mut out = vec![C::ZERO; shift + q_len];
105 let out_q = &mut out[shift..];
106 match (q_len, e) {
107 (1, _) => out_q[0] = q[0].pow_ref(e),
108 (_, 1) => out_q.clone_from_slice(&q[..q_len]),
109 (_, 2) => square_truncated_to_out(out_q, q),
110 _ => pow_truncated_to_out(out_q, q, e),
111 }
112 trim_coefficients(&mut out);
113 out
114}}
115
116// Replaces the coefficients `xs` of a polynomial, which has no zeros at the end, with those of its
117// `e`th power truncated to length `len`, reusing `xs` when the power of a constant is computed or
118// the power is the polynomial itself.
119pub(crate) fn pow_truncated_assign_vec<C: PolynomialCoefficient + PowAssign<u64>>(
120 xs: &mut Vec<C>,
121 e: u64,
122 len: u64,
123) {
124 match (xs.len(), e, len) {
125 (_, _, 0) => xs.clear(),
126 (0, 0, _) => xs.push(C::ONE),
127 (_, 0, _) => {
128 xs.truncate(1);
129 xs[0] = C::ONE;
130 }
131 (0, _, _) => {}
132 (_, 1, _) => truncate_coefficients(xs, len),
133 (1, _, _) => xs[0].pow_assign(e),
134 _ => *xs = pow_truncated_ref(xs, e, len),
135 }
136}
137
138impl PowTruncated for IntegerPolynomial {
139 type Output = Self;
140
141 /// Raises an [`IntegerPolynomial`] to a power, keeping only the coefficients of $x^i$ for $i$
142 /// less than `len`, taking it by value.
143 ///
144 /// $$
145 /// f(p, e, n) = p^e \bmod x^n.
146 /// $$
147 ///
148 /// The polynomial need not already be truncated: this is the power of its image modulo $x^n$,
149 /// so only its first `len` coefficients are read. The zeroth power of every polynomial is 1,
150 /// truncated to 0 when `len` is 0. The power is computed by repeated truncated squaring and
151 /// multiplication.
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 `len`, and $m$ is `exp` times the
159 /// largest number of significant bits of any of the first `len` coefficients of the polynomial.
160 ///
161 /// # Examples
162 /// ```
163 /// use core::str::FromStr;
164 /// use malachite_base::polynomial::PowTruncated;
165 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
166 ///
167 /// assert_eq!(
168 /// (IntegerPolynomial::from_str("x+1").unwrap())
169 /// .pow_truncated(5, 3)
170 /// .to_string(),
171 /// "10*x^2+5*x+1"
172 /// );
173 /// // The power is a multiple of x^4.
174 /// assert_eq!(
175 /// (IntegerPolynomial::from_str("x^2+x").unwrap())
176 /// .pow_truncated(4, 4)
177 /// .to_string(),
178 /// "0"
179 /// );
180 /// ```
181 ///
182 /// This is equivalent to `fmpz_poly_pow_trunc` from `fmpz_poly/pow_trunc.c`, FLINT 3.6.0,
183 /// except that a factor of $x^k$ is removed before powering, and that the intermediate powers
184 /// are kept at their own lengths rather than padded to `len`.
185 #[inline]
186 fn pow_truncated(mut self, exp: u64, len: u64) -> Self {
187 self.pow_truncated_assign(exp, len);
188 self
189 }
190}
191
192impl PowTruncated for &IntegerPolynomial {
193 type Output = IntegerPolynomial;
194
195 /// Raises an [`IntegerPolynomial`] to a power, keeping only the coefficients of $x^i$ for $i$
196 /// less than `len`, taking it by reference.
197 ///
198 /// $$
199 /// f(p, e, n) = p^e \bmod x^n.
200 /// $$
201 ///
202 /// The polynomial need not already be truncated: this is the power of its image modulo $x^n$,
203 /// so only its first `len` coefficients are read. The zeroth power of every polynomial is 1,
204 /// truncated to 0 when `len` is 0. The power is computed by repeated truncated squaring and
205 /// multiplication.
206 ///
207 /// # Worst-case complexity
208 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
209 ///
210 /// $M(n, m) = O(n(m + \log n) \log (nm))$
211 ///
212 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is `exp` times the
213 /// largest number of significant bits of any of the first `len` coefficients of the polynomial.
214 ///
215 /// # Examples
216 /// ```
217 /// use core::str::FromStr;
218 /// use malachite_base::polynomial::PowTruncated;
219 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
220 ///
221 /// assert_eq!(
222 /// (&IntegerPolynomial::from_str("x+1").unwrap())
223 /// .pow_truncated(5, 3)
224 /// .to_string(),
225 /// "10*x^2+5*x+1"
226 /// );
227 /// // The power is a multiple of x^4.
228 /// assert_eq!(
229 /// (&IntegerPolynomial::from_str("x^2+x").unwrap())
230 /// .pow_truncated(4, 4)
231 /// .to_string(),
232 /// "0"
233 /// );
234 /// ```
235 ///
236 /// This is equivalent to `fmpz_poly_pow_trunc` from `fmpz_poly/pow_trunc.c`, FLINT 3.6.0,
237 /// except that a factor of $x^k$ is removed before powering, and that the intermediate powers
238 /// are kept at their own lengths rather than padded to `len`.
239 #[inline]
240 fn pow_truncated(self, exp: u64, len: u64) -> IntegerPolynomial {
241 IntegerPolynomial {
242 coefficients: pow_truncated_ref(&self.coefficients, exp, len),
243 }
244 }
245}
246
247impl PowTruncatedAssign for IntegerPolynomial {
248 /// Raises an [`IntegerPolynomial`] to a power in place, keeping only the coefficients of $x^i$
249 /// for $i$ less than `len`.
250 ///
251 /// $$
252 /// p \gets p^e \bmod x^n.
253 /// $$
254 ///
255 /// The polynomial need not already be truncated: this is the power of its image modulo $x^n$,
256 /// so only its first `len` coefficients are read. The zeroth power of every polynomial is 1,
257 /// truncated to 0 when `len` is 0. The power is computed by repeated truncated squaring and
258 /// multiplication.
259 ///
260 /// # Worst-case complexity
261 /// $T(n, m) = O(n(m + \log n) \log (nm) \log\log (nm))$
262 ///
263 /// $M(n, m) = O(n(m + \log n) \log (nm))$
264 ///
265 /// where $T$ is time, $M$ is additional memory, $n$ is `len`, and $m$ is `exp` times the
266 /// largest number of significant bits of any of the first `len` coefficients of the polynomial.
267 ///
268 /// # Examples
269 /// ```
270 /// use core::str::FromStr;
271 /// use malachite_base::polynomial::PowTruncatedAssign;
272 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
273 ///
274 /// let mut p = IntegerPolynomial::from_str("x+1").unwrap();
275 /// p.pow_truncated_assign(5, 3);
276 /// assert_eq!(p.to_string(), "10*x^2+5*x+1");
277 ///
278 /// // The power is a multiple of x^4.
279 /// let mut p = IntegerPolynomial::from_str("x^2+x").unwrap();
280 /// p.pow_truncated_assign(4, 4);
281 /// assert_eq!(p.to_string(), "0");
282 /// ```
283 ///
284 /// This is equivalent to `fmpz_poly_pow_trunc` from `fmpz_poly/pow_trunc.c`, FLINT 3.6.0,
285 /// except that a factor of $x^k$ is removed before powering, and that the intermediate powers
286 /// are kept at their own lengths rather than padded to `len`.
287 #[inline]
288 fn pow_truncated_assign(&mut self, exp: u64, len: u64) {
289 pow_truncated_assign_vec(&mut self.coefficients, exp, len);
290 }
291}