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}