malachite_base/unsigned_polynomial/arithmetic/mod_op.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::{Mod, ModAssign};
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::polynomial::Polynomial;
12use crate::unsigned_polynomial::UnsignedPolynomial;
13use alloc::vec::Vec;
14use core::ops::{Rem, RemAssign};
15
16impl<T: PrimitiveUnsigned> Rem<T> for UnsignedPolynomial<T> {
17 type Output = Self;
18
19 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], keeping the remainders,
20 /// taking the polynomial by value.
21 ///
22 /// $p \\% m$ is the polynomial whose $i$th coefficient is $p_i \\% m$, and this is the
23 /// remainder of dividing $p$ by the constant polynomial $m$ — under the convention that
24 /// applies over the integers, where a remainder is bounded coefficient by coefficient rather
25 /// than by degree. Over a field the answer would be different: there a remainder must have
26 /// lower degree than the divisor, and a nonzero constant has degree 0, so dividing by one
27 /// leaves a remainder of 0. The `T`s are not a field, division by $m$ is not exact, and
28 /// bounding the remainder's coefficients is what is left.
29 ///
30 /// There is a quotient to go with it: the polynomial whose $i$th coefficient is $p_i / m$, for
31 /// which $p = mq + r$ holds exactly.
32 ///
33 /// The result is reduced modulo $m$, which is to say that
34 /// [`mod_is_reduced`](crate::num::arithmetic::traits::ModIsReduced::mod_is_reduced) returns
35 /// `true` for it.
36 ///
37 /// Reducing can lower the degree, and can even give the zero polynomial: a leading coefficient
38 /// that is a multiple of $m$ becomes zero, and a polynomial does not hold trailing zero
39 /// coefficients. So $4x^2 + 3$ modulo $4$ is the constant $3$, not a quadratic with a zero
40 /// leading coefficient.
41 ///
42 /// $$
43 /// f(p, m) = q, \\quad \text{where} \\quad q_i = p_i - m \left \lfloor \frac{p_i}{m}
44 /// \right \rfloor.
45 /// $$
46 ///
47 /// # Worst-case complexity
48 /// $T(n) = O(n)$
49 ///
50 /// $M(n) = O(1)$
51 ///
52 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
53 ///
54 /// # Panics
55 /// Panics if `m` is 0.
56 ///
57 /// # Examples
58 /// ```
59 /// use core::str::FromStr;
60 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
61 ///
62 /// // Every coefficient is taken modulo 3.
63 /// assert_eq!(
64 /// (UnsignedPolynomial::<u64>::from_str("x^2+4*x+5").unwrap() % 3).to_string(),
65 /// "x^2+x+2"
66 /// );
67 ///
68 /// // Reducing the leading coefficient to zero lowers the degree.
69 /// assert_eq!(
70 /// (UnsignedPolynomial::<u64>::from_str("4*x^2+3").unwrap() % 4).to_string(),
71 /// "3"
72 /// );
73 ///
74 /// // Modulo 1 every coefficient is zero, so the whole polynomial is.
75 /// assert_eq!(
76 /// (UnsignedPolynomial::<u64>::from_str("x^2+4*x+5").unwrap() % 1).to_string(),
77 /// "0"
78 /// );
79 /// ```
80 ///
81 /// This is equivalent to `fmpz_poly_scalar_mod_fmpz` from `fmpz_poly/scalar_mod_fmpz.c`, FLINT
82 /// 3.6.0, for a polynomial whose coefficients are all nonnegative.
83 #[inline]
84 fn rem(mut self, m: T) -> Self {
85 self %= m;
86 self
87 }
88}
89
90impl<T: PrimitiveUnsigned> Rem<T> for &UnsignedPolynomial<T> {
91 type Output = UnsignedPolynomial<T>;
92
93 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], keeping the remainders,
94 /// taking the polynomial by reference.
95 ///
96 /// See the documentation for the [`Rem`] implementation on [`UnsignedPolynomial`] for details,
97 /// including how reducing can lower the degree.
98 ///
99 /// # Worst-case complexity
100 /// $T(n) = O(n)$
101 ///
102 /// $M(n) = O(n)$
103 ///
104 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
105 ///
106 /// # Panics
107 /// Panics if `m` is 0.
108 ///
109 /// # Examples
110 /// ```
111 /// use core::str::FromStr;
112 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
113 ///
114 /// let p = UnsignedPolynomial::<u64>::from_str("4*x^2+3").unwrap();
115 /// assert_eq!((&p % 4).to_string(), "3");
116 /// // The polynomial is left alone.
117 /// assert_eq!(p.to_string(), "4*x^2+3");
118 /// ```
119 #[inline]
120 fn rem(self, m: T) -> UnsignedPolynomial<T> {
121 assert_ne!(m, T::ZERO, "division by zero");
122 // `from_coefficients_asc` trims, which is what makes the degree fall when the leading
123 // coefficient reduces to zero.
124 UnsignedPolynomial::from_coefficients_asc(
125 self.coefficients_asc()
126 .iter()
127 .map(|&c| c % m)
128 .collect::<Vec<_>>(),
129 )
130 }
131}
132
133impl<T: PrimitiveUnsigned> RemAssign<T> for UnsignedPolynomial<T> {
134 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], replacing the polynomial
135 /// by the one whose coefficients are the remainders.
136 ///
137 /// See the documentation for the [`Rem`] implementation on [`UnsignedPolynomial`] for details,
138 /// including how reducing can lower the degree.
139 ///
140 /// # Worst-case complexity
141 /// $T(n) = O(n)$
142 ///
143 /// $M(n) = O(1)$
144 ///
145 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
146 ///
147 /// # Panics
148 /// Panics if `m` is 0.
149 ///
150 /// # Examples
151 /// ```
152 /// use core::str::FromStr;
153 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
154 ///
155 /// let mut p = UnsignedPolynomial::<u64>::from_str("x^2+4*x+5").unwrap();
156 /// p %= 3;
157 /// assert_eq!(p.to_string(), "x^2+x+2");
158 ///
159 /// let mut p = UnsignedPolynomial::<u64>::from_str("4*x^2+3").unwrap();
160 /// p %= 4;
161 /// assert_eq!(p.to_string(), "3");
162 /// ```
163 fn rem_assign(&mut self, m: T) {
164 assert_ne!(m, T::ZERO, "division by zero");
165 for c in &mut self.coefficients {
166 *c %= m;
167 }
168 self.trim();
169 }
170}
171
172impl<T: PrimitiveUnsigned> Mod<T> for UnsignedPolynomial<T> {
173 type Output = Self;
174
175 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], keeping the remainders,
176 /// taking the polynomial by value.
177 ///
178 /// A [`UnsignedPolynomial`]'s coefficients are never negative, so there is nothing for this to
179 /// do that `%` does not: the two agree everywhere, and this is the same operation under the
180 /// name the mod-family traits use. See the documentation for the [`Rem`] implementation for
181 /// details, including how reducing can lower the degree.
182 ///
183 /// # Worst-case complexity
184 /// $T(n) = O(n)$
185 ///
186 /// $M(n) = O(1)$
187 ///
188 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
189 ///
190 /// # Panics
191 /// Panics if `m` is 0.
192 ///
193 /// # Examples
194 /// ```
195 /// use core::str::FromStr;
196 /// use malachite_base::num::arithmetic::traits::Mod;
197 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
198 ///
199 /// assert_eq!(
200 /// UnsignedPolynomial::<u64>::from_str("x^2+4*x+5")
201 /// .unwrap()
202 /// .mod_op(3)
203 /// .to_string(),
204 /// "x^2+x+2"
205 /// );
206 ///
207 /// // Reducing the leading coefficient to zero lowers the degree.
208 /// assert_eq!(
209 /// UnsignedPolynomial::<u64>::from_str("4*x^2+3")
210 /// .unwrap()
211 /// .mod_op(4)
212 /// .to_string(),
213 /// "3"
214 /// );
215 /// ```
216 #[inline]
217 fn mod_op(self, m: T) -> Self {
218 self % m
219 }
220}
221
222impl<T: PrimitiveUnsigned> Mod<T> for &UnsignedPolynomial<T> {
223 type Output = UnsignedPolynomial<T>;
224
225 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], keeping the remainders,
226 /// taking the polynomial by reference.
227 ///
228 /// This agrees with `%` everywhere, a [`UnsignedPolynomial`]'s coefficients never being
229 /// negative. See the documentation for the [`Rem`] implementation on [`UnsignedPolynomial`] for
230 /// details.
231 ///
232 /// # Worst-case complexity
233 /// $T(n) = O(n)$
234 ///
235 /// $M(n) = O(n)$
236 ///
237 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
238 ///
239 /// # Panics
240 /// Panics if `m` is 0.
241 ///
242 /// # Examples
243 /// ```
244 /// use core::str::FromStr;
245 /// use malachite_base::num::arithmetic::traits::Mod;
246 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
247 ///
248 /// let p = UnsignedPolynomial::<u64>::from_str("4*x^2+3").unwrap();
249 /// assert_eq!((&p).mod_op(4).to_string(), "3");
250 /// // The polynomial is left alone.
251 /// assert_eq!(p.to_string(), "4*x^2+3");
252 /// ```
253 #[inline]
254 fn mod_op(self, m: T) -> UnsignedPolynomial<T> {
255 self % m
256 }
257}
258
259impl<T: PrimitiveUnsigned> ModAssign<T> for UnsignedPolynomial<T> {
260 /// Divides every coefficient of a [`UnsignedPolynomial`] by a [`u64`], replacing the polynomial
261 /// by the one whose coefficients are the remainders.
262 ///
263 /// This agrees with `%=` everywhere, a [`UnsignedPolynomial`]'s coefficients never being
264 /// negative. See the documentation for the [`Rem`] implementation on [`UnsignedPolynomial`] for
265 /// details.
266 ///
267 /// # Worst-case complexity
268 /// $T(n) = O(n)$
269 ///
270 /// $M(n) = O(1)$
271 ///
272 /// where $T$ is time, $M$ is additional memory, and $n$ is the number of coefficients.
273 ///
274 /// # Panics
275 /// Panics if `m` is 0.
276 ///
277 /// # Examples
278 /// ```
279 /// use core::str::FromStr;
280 /// use malachite_base::num::arithmetic::traits::ModAssign;
281 /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
282 ///
283 /// let mut p = UnsignedPolynomial::<u64>::from_str("x^2+4*x+5").unwrap();
284 /// p.mod_assign(3);
285 /// assert_eq!(p.to_string(), "x^2+x+2");
286 /// ```
287 #[inline]
288 fn mod_assign(&mut self, m: T) {
289 *self %= m;
290 }
291}