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