Skip to main content

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}