Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_add_truncated.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::basic::unsigneds::PrimitiveUnsigned;
10use crate::polynomial::{ModAddTruncated, ModAddTruncatedAssign, Polynomial};
11use crate::unsigned_polynomial::UnsignedPolynomial;
12use crate::unsigned_polynomial::arithmetic::mod_add::{
13    add_assign_ref, add_assign_val, assert_reduced,
14};
15
16// The first `len` elements of `xs`, or all of them if there are fewer.
17fn prefix<T: PrimitiveUnsigned>(xs: &[T], len: u64) -> &[T] {
18    &xs[..usize::try_from(len).map_or(xs.len(), |len| len.min(xs.len()))]
19}
20
21impl<T: PrimitiveUnsigned> ModAddTruncated<Self, T> for UnsignedPolynomial<T> {
22    type Output = Self;
23
24    /// Adds two [`UnsignedPolynomial`]s modulo `m`, keeping only the coefficients of $x^i$ for $i$
25    /// less than `len`, taking both by value. The coefficients of both must already be reduced
26    /// modulo `m`.
27    ///
28    /// $$
29    /// f(p, q, n, m) = ((p + q) \bmod x^n) \bmod m.
30    /// $$
31    ///
32    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
33    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
34    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
35    ///
36    /// # Worst-case complexity
37    /// $T(n) = O(n)$
38    ///
39    /// $M(n) = O(n)$
40    ///
41    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
42    /// longer polynomial.
43    ///
44    /// # Panics
45    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
46    /// `m`.
47    ///
48    /// # Examples
49    /// ```
50    /// use core::str::FromStr;
51    /// use malachite_base::polynomial::ModAddTruncated;
52    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
53    ///
54    /// // The linear and constant coefficients wrap around to 0.
55    /// assert_eq!(
56    ///     UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5")
57    ///         .unwrap()
58    ///         .mod_add_truncated(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7)
59    ///         .to_string(),
60    ///     "6*x^2"
61    /// );
62    /// assert_eq!(
63    ///     UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5")
64    ///         .unwrap()
65    ///         .mod_add_truncated(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7)
66    ///         .to_string(),
67    ///     "0"
68    /// );
69    /// ```
70    ///
71    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
72    fn mod_add_truncated(mut self, mut other: Self, len: u64, m: T) -> Self {
73        assert_reduced(&self, &other, m);
74        self.truncate_assign(len);
75        other.truncate_assign(len);
76        add_assign_val(&mut self.coefficients, other.coefficients, m);
77        self.trim();
78        self
79    }
80}
81
82impl<T: PrimitiveUnsigned> ModAddTruncated<&Self, T> for UnsignedPolynomial<T> {
83    type Output = Self;
84
85    /// Adds two [`UnsignedPolynomial`]s modulo `m`, keeping only the coefficients of $x^i$ for $i$
86    /// less than `len`, taking the first by value and the second by reference. The coefficients of
87    /// both must already be reduced modulo `m`.
88    ///
89    /// $$
90    /// f(p, q, n, m) = ((p + q) \bmod x^n) \bmod m.
91    /// $$
92    ///
93    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
94    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
95    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
96    ///
97    /// # Worst-case complexity
98    /// $T(n) = O(n)$
99    ///
100    /// $M(n) = O(n)$
101    ///
102    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
103    /// longer polynomial.
104    ///
105    /// # Panics
106    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
107    /// `m`.
108    ///
109    /// # Examples
110    /// ```
111    /// use core::str::FromStr;
112    /// use malachite_base::polynomial::ModAddTruncated;
113    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
114    ///
115    /// // The linear and constant coefficients wrap around to 0.
116    /// assert_eq!(
117    ///     UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5")
118    ///         .unwrap()
119    ///         .mod_add_truncated(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7)
120    ///         .to_string(),
121    ///     "6*x^2"
122    /// );
123    /// assert_eq!(
124    ///     UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5")
125    ///         .unwrap()
126    ///         .mod_add_truncated(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7)
127    ///         .to_string(),
128    ///     "0"
129    /// );
130    /// ```
131    ///
132    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
133    fn mod_add_truncated(mut self, other: &Self, len: u64, m: T) -> Self {
134        assert_reduced(&self, other, m);
135        self.truncate_assign(len);
136        add_assign_ref(&mut self.coefficients, prefix(&other.coefficients, len), m);
137        self.trim();
138        self
139    }
140}
141
142impl<T: PrimitiveUnsigned> ModAddTruncated<UnsignedPolynomial<T>, T> for &UnsignedPolynomial<T> {
143    type Output = UnsignedPolynomial<T>;
144
145    /// Adds two [`UnsignedPolynomial`]s modulo `m`, keeping only the coefficients of $x^i$ for $i$
146    /// less than `len`, taking the first by reference and the second by value. The coefficients of
147    /// both must already be reduced modulo `m`.
148    ///
149    /// $$
150    /// f(p, q, n, m) = ((p + q) \bmod x^n) \bmod m.
151    /// $$
152    ///
153    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
154    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
155    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
156    ///
157    /// # Worst-case complexity
158    /// $T(n) = O(n)$
159    ///
160    /// $M(n) = O(n)$
161    ///
162    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
163    /// longer polynomial.
164    ///
165    /// # Panics
166    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
167    /// `m`.
168    ///
169    /// # Examples
170    /// ```
171    /// use core::str::FromStr;
172    /// use malachite_base::polynomial::ModAddTruncated;
173    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
174    ///
175    /// // The linear and constant coefficients wrap around to 0.
176    /// assert_eq!(
177    ///     (&UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap())
178    ///         .mod_add_truncated(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7)
179    ///         .to_string(),
180    ///     "6*x^2"
181    /// );
182    /// assert_eq!(
183    ///     (&UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap())
184    ///         .mod_add_truncated(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7)
185    ///         .to_string(),
186    ///     "0"
187    /// );
188    /// ```
189    ///
190    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
191    fn mod_add_truncated(
192        self,
193        mut other: UnsignedPolynomial<T>,
194        len: u64,
195        m: T,
196    ) -> UnsignedPolynomial<T> {
197        assert_reduced(self, &other, m);
198        other.truncate_assign(len);
199        add_assign_ref(&mut other.coefficients, prefix(&self.coefficients, len), m);
200        other.trim();
201        other
202    }
203}
204
205impl<T: PrimitiveUnsigned> ModAddTruncated<&UnsignedPolynomial<T>, T> for &UnsignedPolynomial<T> {
206    type Output = UnsignedPolynomial<T>;
207
208    /// Adds two [`UnsignedPolynomial`]s modulo `m`, keeping only the coefficients of $x^i$ for $i$
209    /// less than `len`, taking both by reference. The coefficients of both must already be reduced
210    /// modulo `m`.
211    ///
212    /// $$
213    /// f(p, q, n, m) = ((p + q) \bmod x^n) \bmod m.
214    /// $$
215    ///
216    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
217    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
218    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
219    ///
220    /// # Worst-case complexity
221    /// $T(n) = O(n)$
222    ///
223    /// $M(n) = O(n)$
224    ///
225    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
226    /// longer polynomial.
227    ///
228    /// # Panics
229    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
230    /// `m`.
231    ///
232    /// # Examples
233    /// ```
234    /// use core::str::FromStr;
235    /// use malachite_base::polynomial::ModAddTruncated;
236    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
237    ///
238    /// // The linear and constant coefficients wrap around to 0.
239    /// assert_eq!(
240    ///     (&UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap())
241    ///         .mod_add_truncated(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7)
242    ///         .to_string(),
243    ///     "6*x^2"
244    /// );
245    /// assert_eq!(
246    ///     (&UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap())
247    ///         .mod_add_truncated(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7)
248    ///         .to_string(),
249    ///     "0"
250    /// );
251    /// ```
252    ///
253    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
254    fn mod_add_truncated(
255        self,
256        other: &UnsignedPolynomial<T>,
257        len: u64,
258        m: T,
259    ) -> UnsignedPolynomial<T> {
260        assert_reduced(self, other, m);
261        let mut coefficients = prefix(&self.coefficients, len).to_vec();
262        add_assign_ref(&mut coefficients, prefix(&other.coefficients, len), m);
263        let mut result = UnsignedPolynomial { coefficients };
264        result.trim();
265        result
266    }
267}
268
269impl<T: PrimitiveUnsigned> ModAddTruncatedAssign<Self, T> for UnsignedPolynomial<T> {
270    /// Adds an [`UnsignedPolynomial`] to an [`UnsignedPolynomial`] modulo `m` in place, keeping
271    /// only the coefficients of $x^i$ for $i$ less than `len`, taking the second polynomial by
272    /// value. The coefficients of both must already be reduced modulo `m`.
273    ///
274    /// $$
275    /// p \gets ((p + q) \bmod x^n) \bmod m.
276    /// $$
277    ///
278    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
279    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
280    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
281    ///
282    /// # Worst-case complexity
283    /// $T(n) = O(n)$
284    ///
285    /// $M(n) = O(n)$
286    ///
287    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
288    /// longer polynomial.
289    ///
290    /// # Panics
291    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
292    /// `m`.
293    ///
294    /// # Examples
295    /// ```
296    /// use core::str::FromStr;
297    /// use malachite_base::polynomial::ModAddTruncatedAssign;
298    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
299    ///
300    /// // The linear and constant coefficients wrap around to 0.
301    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap();
302    /// p.mod_add_truncated_assign(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7);
303    /// assert_eq!(p.to_string(), "6*x^2");
304    ///
305    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap();
306    /// p.mod_add_truncated_assign(UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7);
307    /// assert_eq!(p.to_string(), "0");
308    /// ```
309    ///
310    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
311    fn mod_add_truncated_assign(&mut self, mut other: Self, len: u64, m: T) {
312        assert_reduced(self, &other, m);
313        self.truncate_assign(len);
314        other.truncate_assign(len);
315        add_assign_val(&mut self.coefficients, other.coefficients, m);
316        self.trim();
317    }
318}
319
320impl<T: PrimitiveUnsigned> ModAddTruncatedAssign<&Self, T> for UnsignedPolynomial<T> {
321    /// Adds an [`UnsignedPolynomial`] to an [`UnsignedPolynomial`] modulo `m` in place, keeping
322    /// only the coefficients of $x^i$ for $i$ less than `len`, taking the second polynomial by
323    /// reference. The coefficients of both must already be reduced modulo `m`.
324    ///
325    /// $$
326    /// p \gets ((p + q) \bmod x^n) \bmod m.
327    /// $$
328    ///
329    /// The polynomials need not already be truncated: this is the sum of their images modulo $x^n$,
330    /// so only the first `len` coefficients of each are read. The sum is trimmed, so when
331    /// coefficients cancel modulo `m` at the top of the kept range, the degree is lower still.
332    ///
333    /// # Worst-case complexity
334    /// $T(n) = O(n)$
335    ///
336    /// $M(n) = O(n)$
337    ///
338    /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients of the
339    /// longer polynomial.
340    ///
341    /// # Panics
342    /// Panics if `m` is 0, or if any coefficient of `self` or `other` is greater than or equal to
343    /// `m`.
344    ///
345    /// # Examples
346    /// ```
347    /// use core::str::FromStr;
348    /// use malachite_base::polynomial::ModAddTruncatedAssign;
349    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
350    ///
351    /// // The linear and constant coefficients wrap around to 0.
352    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap();
353    /// p.mod_add_truncated_assign(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 3, 7);
354    /// assert_eq!(p.to_string(), "6*x^2");
355    ///
356    /// let mut p = UnsignedPolynomial::<u8>::from_str("x^3+2*x^2+x+5").unwrap();
357    /// p.mod_add_truncated_assign(&UnsignedPolynomial::from_str("4*x^2+6*x+2").unwrap(), 1, 7);
358    /// assert_eq!(p.to_string(), "0");
359    /// ```
360    ///
361    /// This is equivalent to `nmod_poly_add_series` from `nmod_poly/add_series.c`, FLINT 3.6.0.
362    fn mod_add_truncated_assign(&mut self, other: &Self, len: u64, m: T) {
363        assert_reduced(self, other, m);
364        self.truncate_assign(len);
365        add_assign_ref(&mut self.coefficients, prefix(&other.coefficients, len), m);
366        self.trim();
367    }
368}