Skip to main content

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}