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}