Skip to main content

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