malachite_nz/integer_polynomial/arithmetic/balanced_mod.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::integer::Integer;
10use crate::integer_polynomial::IntegerPolynomial;
11use alloc::vec::Vec;
12use malachite_base::num::arithmetic::traits::{BalancedMod, BalancedModAssign};
13use malachite_base::polynomial::Polynomial;
14
15impl BalancedMod<Integer> for IntegerPolynomial {
16 type Output = Self;
17
18 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
19 /// representative closest to zero, taking the polynomial by value and the modulus by value.
20 ///
21 /// Each coefficient $r_i$ of the result satisfies $-|m|/2 < r_i \leq |m|/2$ and $r_i \equiv p_i
22 /// \bmod m$, which determine it uniquely, as with [`BalancedMod`] for [`Integer`]s. A remainder
23 /// of exactly $|m|/2$ is positive, and only the magnitude of $m$ matters.
24 ///
25 /// Reducing can lower the degree, and can even give the zero polynomial: a leading coefficient
26 /// that is a multiple of $m$ becomes zero, and a polynomial does not hold trailing zero
27 /// coefficients. So $10x^2 + 7x + 5$ modulo $10$ is $-3x + 5$.
28 ///
29 /// # Worst-case complexity
30 /// $T(n) = O(n \log n \log\log n)$
31 ///
32 /// $M(n) = O(n \log n)$
33 ///
34 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
35 /// polynomial's coefficients.
36 ///
37 /// # Panics
38 /// Panics if `m` is zero.
39 ///
40 /// # Examples
41 /// ```
42 /// use core::str::FromStr;
43 /// use malachite_base::num::arithmetic::traits::BalancedMod;
44 /// use malachite_nz::integer::Integer;
45 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
46 ///
47 /// // Each coefficient goes to the representative closest to zero.
48 /// let p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
49 /// assert_eq!(
50 /// p.clone().balanced_mod(Integer::from(10)).to_string(),
51 /// "x^2-3*x-3"
52 /// );
53 ///
54 /// // Half the modulus stays positive, only the modulus's magnitude matters, and reducing the
55 /// // leading coefficient to zero lowers the degree.
56 /// let p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
57 /// assert_eq!(
58 /// p.clone().balanced_mod(Integer::from(-10)).to_string(),
59 /// "-3*x+5"
60 /// );
61 /// ```
62 #[inline]
63 fn balanced_mod(mut self, m: Integer) -> Self {
64 self.balanced_mod_assign(m);
65 self
66 }
67}
68
69impl<'a> BalancedMod<&'a Integer> for IntegerPolynomial {
70 type Output = Self;
71
72 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
73 /// representative closest to zero, taking the polynomial by value and the modulus by reference.
74 ///
75 /// See the documentation for the [`BalancedMod`] implementation on [`IntegerPolynomial`] that
76 /// takes both arguments by value for details.
77 ///
78 /// # Worst-case complexity
79 /// $T(n) = O(n \log n \log\log n)$
80 ///
81 /// $M(n) = O(n \log n)$
82 ///
83 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
84 /// polynomial's coefficients.
85 ///
86 /// # Panics
87 /// Panics if `m` is zero.
88 ///
89 /// # Examples
90 /// ```
91 /// use core::str::FromStr;
92 /// use malachite_base::num::arithmetic::traits::BalancedMod;
93 /// use malachite_nz::integer::Integer;
94 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
95 ///
96 /// // Each coefficient goes to the representative closest to zero.
97 /// let p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
98 /// assert_eq!(
99 /// p.clone().balanced_mod(&Integer::from(10)).to_string(),
100 /// "x^2-3*x-3"
101 /// );
102 ///
103 /// // Half the modulus stays positive, only the modulus's magnitude matters, and reducing the
104 /// // leading coefficient to zero lowers the degree.
105 /// let p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
106 /// assert_eq!(
107 /// p.clone().balanced_mod(&Integer::from(-10)).to_string(),
108 /// "-3*x+5"
109 /// );
110 /// ```
111 #[inline]
112 fn balanced_mod(mut self, m: &'a Integer) -> Self {
113 self.balanced_mod_assign(m);
114 self
115 }
116}
117
118impl BalancedMod<Integer> for &IntegerPolynomial {
119 type Output = IntegerPolynomial;
120
121 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
122 /// representative closest to zero, taking the polynomial by reference and the modulus by value.
123 ///
124 /// See the documentation for the [`BalancedMod`] implementation on [`IntegerPolynomial`] that
125 /// takes both arguments by value for details.
126 ///
127 /// # Worst-case complexity
128 /// $T(n) = O(n \log n \log\log n)$
129 ///
130 /// $M(n) = O(n \log n)$
131 ///
132 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
133 /// polynomial's coefficients.
134 ///
135 /// # Panics
136 /// Panics if `m` is zero.
137 ///
138 /// # Examples
139 /// ```
140 /// use core::str::FromStr;
141 /// use malachite_base::num::arithmetic::traits::BalancedMod;
142 /// use malachite_nz::integer::Integer;
143 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
144 ///
145 /// // Each coefficient goes to the representative closest to zero.
146 /// let p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
147 /// assert_eq!(
148 /// (&p).balanced_mod(Integer::from(10)).to_string(),
149 /// "x^2-3*x-3"
150 /// );
151 ///
152 /// // Half the modulus stays positive, only the modulus's magnitude matters, and reducing the
153 /// // leading coefficient to zero lowers the degree.
154 /// let p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
155 /// assert_eq!((&p).balanced_mod(Integer::from(-10)).to_string(), "-3*x+5");
156 /// ```
157 #[inline]
158 fn balanced_mod(self, m: Integer) -> IntegerPolynomial {
159 self.balanced_mod(&m)
160 }
161}
162
163impl<'a> BalancedMod<&'a Integer> for &IntegerPolynomial {
164 type Output = IntegerPolynomial;
165
166 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
167 /// representative closest to zero, taking the polynomial by reference and the modulus by
168 /// reference.
169 ///
170 /// See the documentation for the [`BalancedMod`] implementation on [`IntegerPolynomial`] that
171 /// takes both arguments by value for details.
172 ///
173 /// # Worst-case complexity
174 /// $T(n) = O(n \log n \log\log n)$
175 ///
176 /// $M(n) = O(n \log n)$
177 ///
178 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
179 /// polynomial's coefficients.
180 ///
181 /// # Panics
182 /// Panics if `m` is zero.
183 ///
184 /// # Examples
185 /// ```
186 /// use core::str::FromStr;
187 /// use malachite_base::num::arithmetic::traits::BalancedMod;
188 /// use malachite_nz::integer::Integer;
189 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
190 ///
191 /// // Each coefficient goes to the representative closest to zero.
192 /// let p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
193 /// assert_eq!(
194 /// (&p).balanced_mod(&Integer::from(10)).to_string(),
195 /// "x^2-3*x-3"
196 /// );
197 ///
198 /// // Half the modulus stays positive, only the modulus's magnitude matters, and reducing the
199 /// // leading coefficient to zero lowers the degree.
200 /// let p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
201 /// assert_eq!((&p).balanced_mod(&Integer::from(-10)).to_string(), "-3*x+5");
202 /// ```
203 fn balanced_mod(self, m: &'a Integer) -> IntegerPolynomial {
204 assert_ne!(*m, 0u32, "division by zero");
205 // `from_coefficients_asc` trims, which is what makes the degree fall when the leading
206 // coefficient reduces to zero.
207 IntegerPolynomial::from_coefficients_asc(
208 self.coefficients
209 .iter()
210 .map(|c| c.balanced_mod(m))
211 .collect::<Vec<_>>(),
212 )
213 }
214}
215
216impl BalancedModAssign<Integer> for IntegerPolynomial {
217 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
218 /// representative closest to zero, in place, taking the modulus by value.
219 ///
220 /// See the documentation for the [`BalancedMod`] implementation on [`IntegerPolynomial`] that
221 /// takes both arguments by value for details.
222 ///
223 /// # Worst-case complexity
224 /// $T(n) = O(n \log n \log\log n)$
225 ///
226 /// $M(n) = O(n \log n)$
227 ///
228 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
229 /// polynomial's coefficients.
230 ///
231 /// # Panics
232 /// Panics if `m` is zero.
233 ///
234 /// # Examples
235 /// ```
236 /// use core::str::FromStr;
237 /// use malachite_base::num::arithmetic::traits::BalancedModAssign;
238 /// use malachite_nz::integer::Integer;
239 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
240 ///
241 /// let mut p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
242 /// p.balanced_mod_assign(Integer::from(10));
243 /// assert_eq!(p.to_string(), "x^2-3*x-3");
244 ///
245 /// let mut p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
246 /// p.balanced_mod_assign(Integer::from(-10));
247 /// assert_eq!(p.to_string(), "-3*x+5");
248 /// ```
249 #[inline]
250 fn balanced_mod_assign(&mut self, m: Integer) {
251 self.balanced_mod_assign(&m);
252 }
253}
254
255impl<'a> BalancedModAssign<&'a Integer> for IntegerPolynomial {
256 /// Reduces every coefficient of an [`IntegerPolynomial`] modulo an [`Integer`] to the
257 /// representative closest to zero, in place, taking the modulus by reference.
258 ///
259 /// See the documentation for the [`BalancedMod`] implementation on [`IntegerPolynomial`] that
260 /// takes both arguments by value for details.
261 ///
262 /// # Worst-case complexity
263 /// $T(n) = O(n \log n \log\log n)$
264 ///
265 /// $M(n) = O(n \log n)$
266 ///
267 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits in the
268 /// polynomial's coefficients.
269 ///
270 /// # Panics
271 /// Panics if `m` is zero.
272 ///
273 /// # Examples
274 /// ```
275 /// use core::str::FromStr;
276 /// use malachite_base::num::arithmetic::traits::BalancedModAssign;
277 /// use malachite_nz::integer::Integer;
278 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
279 ///
280 /// let mut p = IntegerPolynomial::from_str("x^2+27*x-23").unwrap();
281 /// p.balanced_mod_assign(&Integer::from(10));
282 /// assert_eq!(p.to_string(), "x^2-3*x-3");
283 ///
284 /// let mut p = IntegerPolynomial::from_str("10*x^2+7*x+5").unwrap();
285 /// p.balanced_mod_assign(&Integer::from(-10));
286 /// assert_eq!(p.to_string(), "-3*x+5");
287 /// ```
288 fn balanced_mod_assign(&mut self, m: &'a Integer) {
289 assert_ne!(*m, 0u32, "division by zero");
290 for c in &mut self.coefficients {
291 c.balanced_mod_assign(m);
292 }
293 self.trim();
294 }
295}