Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_power_of_2_integral.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{ModPowerOf2IsReduced, Parity};
10use crate::num::basic::traits::Zero;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use crate::polynomial::{ModPowerOf2Integral, ModPowerOf2IntegralAssign};
13use crate::unsigned_polynomial::UnsignedPolynomial;
14use alloc::vec;
15use alloc::vec::Vec;
16
17fn assert_reduced<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, pow: u64) {
18    assert!(pow <= T::WIDTH);
19    assert!(
20        p.mod_power_of_2_is_reduced(pow),
21        "self must be reduced mod 2^pow, but {p} has a coefficient >= 2^{pow}"
22    );
23}
24
25// The coefficients of the integral modulo $2^k$, where $k$ is `pow`, of the polynomial with
26// coefficients `xs`, which is nonempty and reduced modulo $2^k$. The coefficient of $x^{i-1}$,
27// divided by $i$, goes to $x^i$; a zero coefficient stays zero. The indices of the nonzero
28// coefficients must all be odd, so their product is a unit, and all the divisions share its
29// inverse, as in `mod_integral`. Arithmetic modulo $2^\text{W}$, with indices converted by
30// wrapping, loses nothing, since $2^k$ divides $2^\text{W}$. The result is not trimmed.
31fn mod_power_of_2_integral_coefficients<T: PrimitiveUnsigned>(xs: &[T], pow: u64) -> Vec<T> {
32    let n = xs.len();
33    let mut out = vec![T::ZERO; n + 1];
34    out[1..].copy_from_slice(xs);
35    // The product of the indices above i whose coefficients are nonzero.
36    let mut product = T::ONE.mod_power_of_2(pow);
37    for (i, c) in out.iter_mut().enumerate().skip(2).rev() {
38        if *c != T::ZERO {
39            assert!(
40                i.odd(),
41                "The integral modulo 2^pow is only defined if every nonzero coefficient belongs to \
42                an even power of x, but the coefficient of x^{} is nonzero",
43                i - 1
44            );
45            *c = c.wrapping_mul(product).mod_power_of_2(pow);
46            product = product
47                .wrapping_mul(T::wrapping_from(i))
48                .mod_power_of_2(pow);
49        }
50    }
51    if n >= 2 {
52        // `product` is odd, so it has an inverse.
53        let mut inverse = product.mod_power_of_2_inverse(pow).unwrap();
54        for (i, c) in out.iter_mut().enumerate().skip(2) {
55            if *c != T::ZERO {
56                *c = c.wrapping_mul(inverse).mod_power_of_2(pow);
57                inverse = inverse
58                    .wrapping_mul(T::wrapping_from(i))
59                    .mod_power_of_2(pow);
60            }
61        }
62    }
63    out
64}
65
66fn mod_power_of_2_integral_ref<T: PrimitiveUnsigned>(
67    p: &UnsignedPolynomial<T>,
68    pow: u64,
69) -> UnsignedPolynomial<T> {
70    assert_reduced(p, pow);
71    if p.coefficients.is_empty() {
72        return UnsignedPolynomial::ZERO;
73    }
74    let mut q = UnsignedPolynomial {
75        coefficients: mod_power_of_2_integral_coefficients(&p.coefficients, pow),
76    };
77    q.trim();
78    q
79}
80
81impl<T: PrimitiveUnsigned> ModPowerOf2Integral for UnsignedPolynomial<T> {
82    type Output = Self;
83
84    /// Computes the integral modulo $2^k$ of an [`UnsignedPolynomial`] whose constant term is zero,
85    /// taking the polynomial by value. Its coefficients must already be reduced modulo $2^k$.
86    ///
87    /// $$
88    /// f(p, k) = \int_0^x p(t)\,dt \bmod 2^k = \sum_{i=0}^{n-1} \frac{a_i}{i+1}x^{i+1} \bmod 2^k.
89    /// $$
90    ///
91    /// The coefficient of $x^{i-1}$ is divided by $i$ and moved to $x^i$. Only odd numbers are
92    /// units modulo $2^k$, so every nonzero coefficient must belong to an even power of $x$; a zero
93    /// coefficient stays zero. The divisions share a single inversion modulo $2^k$. The integral of
94    /// zero is zero, for every $k$.
95    ///
96    /// # Worst-case complexity
97    /// $T(n) = O(n)$
98    ///
99    /// $M(n) = O(n)$
100    ///
101    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
102    ///
103    /// # Panics
104    /// Panics if `pow` is greater than `T::WIDTH`, if any coefficient is greater than or equal to
105    /// $2^k$, or if the coefficient of an odd power of $x$ is nonzero.
106    ///
107    /// # Examples
108    /// ```
109    /// use core::str::FromStr;
110    /// use malachite_base::polynomial::ModPowerOf2Integral;
111    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
112    ///
113    /// // Dividing by 3 is multiplying by 3 modulo 8.
114    /// assert_eq!(
115    ///     UnsignedPolynomial::<u8>::from_str("x^2")
116    ///         .unwrap()
117    ///         .mod_power_of_2_integral(3)
118    ///         .to_string(),
119    ///     "3*x^3"
120    /// );
121    /// // Dividing by 5 is multiplying by 13 modulo 16.
122    /// assert_eq!(
123    ///     UnsignedPolynomial::<u8>::from_str("x^4+1")
124    ///         .unwrap()
125    ///         .mod_power_of_2_integral(4)
126    ///         .to_string(),
127    ///     "13*x^5+x"
128    /// );
129    /// ```
130    ///
131    /// This is `nmod_poly_integral` from `nmod_poly/integral.c`, FLINT 3.6.0, with the modulus
132    /// $2^k$, except that FLINT needs every index from 1 to the degree plus 1 to be a unit, even
133    /// when its coefficient is zero, and so cannot integrate any polynomial of degree at least 1
134    /// modulo $2^k$.
135    #[inline]
136    fn mod_power_of_2_integral(self, pow: u64) -> Self {
137        mod_power_of_2_integral_ref(&self, pow)
138    }
139}
140
141impl<T: PrimitiveUnsigned> ModPowerOf2Integral for &UnsignedPolynomial<T> {
142    type Output = UnsignedPolynomial<T>;
143
144    /// Computes the integral modulo $2^k$ of an [`UnsignedPolynomial`] whose constant term is zero,
145    /// taking the polynomial by reference. Its coefficients must already be reduced modulo $2^k$.
146    ///
147    /// $$
148    /// f(p, k) = \int_0^x p(t)\,dt \bmod 2^k = \sum_{i=0}^{n-1} \frac{a_i}{i+1}x^{i+1} \bmod 2^k.
149    /// $$
150    ///
151    /// The coefficient of $x^{i-1}$ is divided by $i$ and moved to $x^i$. Only odd numbers are
152    /// units modulo $2^k$, so every nonzero coefficient must belong to an even power of $x$; a zero
153    /// coefficient stays zero. The divisions share a single inversion modulo $2^k$. The integral of
154    /// zero is zero, for every $k$.
155    ///
156    /// # Worst-case complexity
157    /// $T(n) = O(n)$
158    ///
159    /// $M(n) = O(n)$
160    ///
161    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
162    ///
163    /// # Panics
164    /// Panics if `pow` is greater than `T::WIDTH`, if any coefficient is greater than or equal to
165    /// $2^k$, or if the coefficient of an odd power of $x$ is nonzero.
166    ///
167    /// # Examples
168    /// ```
169    /// use core::str::FromStr;
170    /// use malachite_base::polynomial::ModPowerOf2Integral;
171    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
172    ///
173    /// // Dividing by 3 is multiplying by 3 modulo 8.
174    /// assert_eq!(
175    ///     (&UnsignedPolynomial::<u8>::from_str("x^2").unwrap())
176    ///         .mod_power_of_2_integral(3)
177    ///         .to_string(),
178    ///     "3*x^3"
179    /// );
180    /// // Dividing by 5 is multiplying by 13 modulo 16.
181    /// assert_eq!(
182    ///     (&UnsignedPolynomial::<u8>::from_str("x^4+1").unwrap())
183    ///         .mod_power_of_2_integral(4)
184    ///         .to_string(),
185    ///     "13*x^5+x"
186    /// );
187    /// ```
188    ///
189    /// This is `nmod_poly_integral` from `nmod_poly/integral.c`, FLINT 3.6.0, with the modulus
190    /// $2^k$, except that FLINT needs every index from 1 to the degree plus 1 to be a unit, even
191    /// when its coefficient is zero, and so cannot integrate any polynomial of degree at least 1
192    /// modulo $2^k$.
193    #[inline]
194    fn mod_power_of_2_integral(self, pow: u64) -> UnsignedPolynomial<T> {
195        mod_power_of_2_integral_ref(self, pow)
196    }
197}
198
199impl<T: PrimitiveUnsigned> ModPowerOf2IntegralAssign for UnsignedPolynomial<T> {
200    /// Replaces an [`UnsignedPolynomial`] with its integral modulo $2^k$ whose constant term is
201    /// zero. Its coefficients must already be reduced modulo $2^k$.
202    ///
203    /// $$
204    /// p \gets \int_0^x p(t)\,dt \bmod 2^k = \sum_{i=0}^{n-1} \frac{a_i}{i+1}x^{i+1} \bmod 2^k.
205    /// $$
206    ///
207    /// The coefficient of $x^{i-1}$ is divided by $i$ and moved to $x^i$. Only odd numbers are
208    /// units modulo $2^k$, so every nonzero coefficient must belong to an even power of $x$; a zero
209    /// coefficient stays zero. The divisions share a single inversion modulo $2^k$. The integral of
210    /// zero is zero, for every $k$.
211    ///
212    /// # Worst-case complexity
213    /// $T(n) = O(n)$
214    ///
215    /// $M(n) = O(n)$
216    ///
217    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
218    ///
219    /// # Panics
220    /// Panics if `pow` is greater than `T::WIDTH`, if any coefficient is greater than or equal to
221    /// $2^k$, or if the coefficient of an odd power of $x$ is nonzero.
222    ///
223    /// # Examples
224    /// ```
225    /// use core::str::FromStr;
226    /// use malachite_base::polynomial::ModPowerOf2IntegralAssign;
227    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
228    ///
229    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^2").unwrap();
230    /// p.mod_power_of_2_integral_assign(3);
231    /// assert_eq!(p.to_string(), "3*x^3");
232    /// ```
233    ///
234    /// This is `nmod_poly_integral` from `nmod_poly/integral.c`, FLINT 3.6.0, with the modulus
235    /// $2^k$, except that FLINT needs every index from 1 to the degree plus 1 to be a unit, even
236    /// when its coefficient is zero, and so cannot integrate any polynomial of degree at least 1
237    /// modulo $2^k$.
238    #[inline]
239    fn mod_power_of_2_integral_assign(&mut self, pow: u64) {
240        *self = mod_power_of_2_integral_ref(self, pow);
241    }
242}