Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_power_of_2_add.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::{ModPowerOf2Add, ModPowerOf2AddAssign, ModPowerOf2IsReduced};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::unsigned_polynomial::UnsignedPolynomial;
12use alloc::vec::Vec;
13use core::cmp::min;
14use core::mem::swap;
15
16pub(crate) fn assert_reduced<T: PrimitiveUnsigned>(
17    p: &UnsignedPolynomial<T>,
18    q: &UnsignedPolynomial<T>,
19    pow: u64,
20) {
21    assert!(pow <= T::WIDTH);
22    assert!(
23        p.mod_power_of_2_is_reduced(pow),
24        "self must be reduced mod 2^pow, but {p} has a coefficient >= 2^{pow}"
25    );
26    assert!(
27        q.mod_power_of_2_is_reduced(pow),
28        "other must be reduced mod 2^pow, but {q} has a coefficient >= 2^{pow}"
29    );
30}
31
32// Adds `ys` into `xs` modulo 2^pow, copying the coefficients of `ys` past the end of `xs`. The
33// caller trims, since leading coefficients can cancel.
34pub(crate) fn add_assign_ref<T: PrimitiveUnsigned>(xs: &mut Vec<T>, ys: &[T], pow: u64) {
35    let common = min(xs.len(), ys.len());
36    for (x, &y) in xs.iter_mut().zip(&ys[..common]) {
37        *x = x.wrapping_add(y).mod_power_of_2(pow);
38    }
39    if ys.len() > common {
40        xs.extend_from_slice(&ys[common..]);
41    }
42}
43
44// Adds `ys` into `xs` modulo 2^pow, reusing whichever of the two is longer. The caller trims.
45pub(crate) fn add_assign_val<T: PrimitiveUnsigned>(xs: &mut Vec<T>, mut ys: Vec<T>, pow: u64) {
46    if ys.len() > xs.len() {
47        swap(xs, &mut ys);
48    }
49    for (x, y) in xs.iter_mut().zip(ys) {
50        *x = x.wrapping_add(y).mod_power_of_2(pow);
51    }
52}
53
54impl<T: PrimitiveUnsigned> ModPowerOf2Add<Self> for UnsignedPolynomial<T> {
55    type Output = Self;
56
57    /// Adds two [`UnsignedPolynomial`]s modulo $2^k$, taking both by value. The coefficients of
58    /// both must already be reduced modulo $2^k$.
59    ///
60    /// $$
61    /// f(p, q, k) = p + q \bmod 2^k.
62    /// $$
63    ///
64    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
65    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
66    /// then the degree of the sum is lower.
67    ///
68    /// # Worst-case complexity
69    /// $T(n) = O(n)$
70    ///
71    /// $M(n) = O(1)$
72    ///
73    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
74    /// longer polynomial times `pow`.
75    ///
76    /// # Panics
77    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
78    /// greater than or equal to $2^k$.
79    ///
80    /// # Examples
81    /// ```
82    /// use core::str::FromStr;
83    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Add;
84    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
85    ///
86    /// // The leading coefficients cancel, and so do the linear ones.
87    /// assert_eq!(
88    ///     UnsignedPolynomial::<u8>::from_str("5*x^2+x+3")
89    ///         .unwrap()
90    ///         .mod_power_of_2_add(
91    ///             UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
92    ///             3
93    ///         )
94    ///         .to_string(),
95    ///     "4"
96    /// );
97    /// // Wrapping around makes the constant term 1.
98    /// assert_eq!(
99    ///     UnsignedPolynomial::<u8>::from_str("x+3")
100    ///         .unwrap()
101    ///         .mod_power_of_2_add(UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3)
102    ///         .to_string(),
103    ///     "x^2+x+1"
104    /// );
105    /// ```
106    ///
107    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
108    /// $2^k$.
109    fn mod_power_of_2_add(mut self, other: Self, pow: u64) -> Self {
110        assert_reduced(&self, &other, pow);
111        add_assign_val(&mut self.coefficients, other.coefficients, pow);
112        self.trim();
113        self
114    }
115}
116
117impl<T: PrimitiveUnsigned> ModPowerOf2Add<&Self> for UnsignedPolynomial<T> {
118    type Output = Self;
119
120    /// Adds two [`UnsignedPolynomial`]s modulo $2^k$, taking the first by value and the second by
121    /// reference. The coefficients of both must already be reduced modulo $2^k$.
122    ///
123    /// $$
124    /// f(p, q, k) = p + q \bmod 2^k.
125    /// $$
126    ///
127    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
128    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
129    /// then the degree of the sum is lower.
130    ///
131    /// # Worst-case complexity
132    /// $T(n) = O(n)$
133    ///
134    /// $M(n) = O(n)$
135    ///
136    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
137    /// longer polynomial times `pow`.
138    ///
139    /// # Panics
140    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
141    /// greater than or equal to $2^k$.
142    ///
143    /// # Examples
144    /// ```
145    /// use core::str::FromStr;
146    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Add;
147    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
148    ///
149    /// // The leading coefficients cancel, and so do the linear ones.
150    /// assert_eq!(
151    ///     UnsignedPolynomial::<u8>::from_str("5*x^2+x+3")
152    ///         .unwrap()
153    ///         .mod_power_of_2_add(
154    ///             &UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
155    ///             3
156    ///         )
157    ///         .to_string(),
158    ///     "4"
159    /// );
160    /// // Wrapping around makes the constant term 1.
161    /// assert_eq!(
162    ///     UnsignedPolynomial::<u8>::from_str("x+3")
163    ///         .unwrap()
164    ///         .mod_power_of_2_add(&UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3)
165    ///         .to_string(),
166    ///     "x^2+x+1"
167    /// );
168    /// ```
169    ///
170    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
171    /// $2^k$.
172    fn mod_power_of_2_add(mut self, other: &Self, pow: u64) -> Self {
173        assert_reduced(&self, other, pow);
174        add_assign_ref(&mut self.coefficients, &other.coefficients, pow);
175        self.trim();
176        self
177    }
178}
179
180impl<T: PrimitiveUnsigned> ModPowerOf2Add<UnsignedPolynomial<T>> for &UnsignedPolynomial<T> {
181    type Output = UnsignedPolynomial<T>;
182
183    /// Adds two [`UnsignedPolynomial`]s modulo $2^k$, taking the first by reference and the second
184    /// by value. The coefficients of both must already be reduced modulo $2^k$.
185    ///
186    /// $$
187    /// f(p, q, k) = p + q \bmod 2^k.
188    /// $$
189    ///
190    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
191    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
192    /// then the degree of the sum is lower.
193    ///
194    /// # Worst-case complexity
195    /// $T(n) = O(n)$
196    ///
197    /// $M(n) = O(n)$
198    ///
199    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
200    /// longer polynomial times `pow`.
201    ///
202    /// # Panics
203    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
204    /// greater than or equal to $2^k$.
205    ///
206    /// # Examples
207    /// ```
208    /// use core::str::FromStr;
209    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Add;
210    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
211    ///
212    /// // The leading coefficients cancel, and so do the linear ones.
213    /// assert_eq!(
214    ///     (&UnsignedPolynomial::<u8>::from_str("5*x^2+x+3").unwrap())
215    ///         .mod_power_of_2_add(
216    ///             UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
217    ///             3
218    ///         )
219    ///         .to_string(),
220    ///     "4"
221    /// );
222    /// // Wrapping around makes the constant term 1.
223    /// assert_eq!(
224    ///     (&UnsignedPolynomial::<u8>::from_str("x+3").unwrap())
225    ///         .mod_power_of_2_add(UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3)
226    ///         .to_string(),
227    ///     "x^2+x+1"
228    /// );
229    /// ```
230    ///
231    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
232    /// $2^k$.
233    fn mod_power_of_2_add(
234        self,
235        mut other: UnsignedPolynomial<T>,
236        pow: u64,
237    ) -> UnsignedPolynomial<T> {
238        assert_reduced(self, &other, pow);
239        add_assign_ref(&mut other.coefficients, &self.coefficients, pow);
240        other.trim();
241        other
242    }
243}
244
245impl<T: PrimitiveUnsigned> ModPowerOf2Add<&UnsignedPolynomial<T>> for &UnsignedPolynomial<T> {
246    type Output = UnsignedPolynomial<T>;
247
248    /// Adds two [`UnsignedPolynomial`]s modulo $2^k$, taking both by reference. The coefficients of
249    /// both must already be reduced modulo $2^k$.
250    ///
251    /// $$
252    /// f(p, q, k) = p + q \bmod 2^k.
253    /// $$
254    ///
255    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
256    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
257    /// then the degree of the sum is lower.
258    ///
259    /// # Worst-case complexity
260    /// $T(n) = O(n)$
261    ///
262    /// $M(n) = O(n)$
263    ///
264    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
265    /// longer polynomial times `pow`.
266    ///
267    /// # Panics
268    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
269    /// greater than or equal to $2^k$.
270    ///
271    /// # Examples
272    /// ```
273    /// use core::str::FromStr;
274    /// use malachite_base::num::arithmetic::traits::ModPowerOf2Add;
275    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
276    ///
277    /// // The leading coefficients cancel, and so do the linear ones.
278    /// assert_eq!(
279    ///     (&UnsignedPolynomial::<u8>::from_str("5*x^2+x+3").unwrap())
280    ///         .mod_power_of_2_add(
281    ///             &UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
282    ///             3
283    ///         )
284    ///         .to_string(),
285    ///     "4"
286    /// );
287    /// // Wrapping around makes the constant term 1.
288    /// assert_eq!(
289    ///     (&UnsignedPolynomial::<u8>::from_str("x+3").unwrap())
290    ///         .mod_power_of_2_add(&UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3)
291    ///         .to_string(),
292    ///     "x^2+x+1"
293    /// );
294    /// ```
295    ///
296    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
297    /// $2^k$.
298    fn mod_power_of_2_add(self, other: &UnsignedPolynomial<T>, pow: u64) -> UnsignedPolynomial<T> {
299        assert_reduced(self, other, pow);
300        let mut coefficients = self.coefficients.clone();
301        add_assign_ref(&mut coefficients, &other.coefficients, pow);
302        let mut result = UnsignedPolynomial { coefficients };
303        result.trim();
304        result
305    }
306}
307
308impl<T: PrimitiveUnsigned> ModPowerOf2AddAssign<Self> for UnsignedPolynomial<T> {
309    /// Adds a [`UnsignedPolynomial`] to a [`UnsignedPolynomial`] modulo $2^k$, in place, taking the
310    /// second by value. The coefficients of both must already be reduced modulo $2^k$.
311    ///
312    /// $$
313    /// p \gets p + q \bmod 2^k.
314    /// $$
315    ///
316    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
317    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
318    /// then the degree of the sum is lower.
319    ///
320    /// # Worst-case complexity
321    /// $T(n) = O(n)$
322    ///
323    /// $M(n) = O(1)$
324    ///
325    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
326    /// longer polynomial times `pow`.
327    ///
328    /// # Panics
329    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
330    /// greater than or equal to $2^k$.
331    ///
332    /// # Examples
333    /// ```
334    /// use core::str::FromStr;
335    /// use malachite_base::num::arithmetic::traits::ModPowerOf2AddAssign;
336    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
337    ///
338    /// // The leading coefficients cancel, and so do the linear ones.
339    /// let mut p = UnsignedPolynomial::<u8>::from_str("5*x^2+x+3").unwrap();
340    /// p.mod_power_of_2_add_assign(
341    ///     UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
342    ///     3,
343    /// );
344    /// assert_eq!(p.to_string(), "4");
345    ///
346    /// // Wrapping around makes the constant term 1.
347    /// let mut p = UnsignedPolynomial::<u8>::from_str("x+3").unwrap();
348    /// p.mod_power_of_2_add_assign(UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3);
349    /// assert_eq!(p.to_string(), "x^2+x+1");
350    /// ```
351    ///
352    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
353    /// $2^k$.
354    fn mod_power_of_2_add_assign(&mut self, other: Self, pow: u64) {
355        assert_reduced(self, &other, pow);
356        add_assign_val(&mut self.coefficients, other.coefficients, pow);
357        self.trim();
358    }
359}
360
361impl<T: PrimitiveUnsigned> ModPowerOf2AddAssign<&Self> for UnsignedPolynomial<T> {
362    /// Adds a [`UnsignedPolynomial`] to a [`UnsignedPolynomial`] modulo $2^k$, in place, taking the
363    /// second by reference. The coefficients of both must already be reduced modulo $2^k$.
364    ///
365    /// $$
366    /// p \gets p + q \bmod 2^k.
367    /// $$
368    ///
369    /// Coefficients past the end of the shorter polynomial are taken to be zero. When the two
370    /// polynomials have the same degree, their leading coefficients can cancel modulo $2^k$, and
371    /// then the degree of the sum is lower.
372    ///
373    /// # Worst-case complexity
374    /// $T(n) = O(n)$
375    ///
376    /// $M(n) = O(n)$
377    ///
378    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
379    /// longer polynomial times `pow`.
380    ///
381    /// # Panics
382    /// Panics if `pow` is greater than `T::WIDTH`, or if any coefficient of `self` or `other` is
383    /// greater than or equal to $2^k$.
384    ///
385    /// # Examples
386    /// ```
387    /// use core::str::FromStr;
388    /// use malachite_base::num::arithmetic::traits::ModPowerOf2AddAssign;
389    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
390    ///
391    /// // The leading coefficients cancel, and so do the linear ones.
392    /// let mut p = UnsignedPolynomial::<u8>::from_str("5*x^2+x+3").unwrap();
393    /// p.mod_power_of_2_add_assign(
394    ///     &UnsignedPolynomial::<u8>::from_str("3*x^2+7*x+1").unwrap(),
395    ///     3,
396    /// );
397    /// assert_eq!(p.to_string(), "4");
398    ///
399    /// // Wrapping around makes the constant term 1.
400    /// let mut p = UnsignedPolynomial::<u8>::from_str("x+3").unwrap();
401    /// p.mod_power_of_2_add_assign(&UnsignedPolynomial::<u8>::from_str("x^2+6").unwrap(), 3);
402    /// assert_eq!(p.to_string(), "x^2+x+1");
403    /// ```
404    ///
405    /// This is equivalent to `nmod_poly_add` from `nmod_poly/add.c`, FLINT 3.6.0, with the modulus
406    /// $2^k$.
407    fn mod_power_of_2_add_assign(&mut self, other: &Self, pow: u64) {
408        assert_reduced(self, other, pow);
409        add_assign_ref(&mut self.coefficients, &other.coefficients, pow);
410        self.trim();
411    }
412}