Skip to main content

malachite_base/unsigned_polynomial/arithmetic/
mod_make_monic.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::ModIsReduced;
10use crate::num::basic::unsigneds::PrimitiveUnsigned;
11use crate::polynomial::{ModMakeMonic, ModMakeMonicAssign};
12use crate::unsigned_polynomial::UnsignedPolynomial;
13
14// Multiplies every coefficient by the inverse of the leading coefficient, which makes the leading
15// coefficient 1, or returns the GCD of the leading coefficient and m when there is no inverse.
16fn mod_make_monic_in_place<T: PrimitiveUnsigned>(coefficients: &mut [T], m: T) -> Result<(), T> {
17    let Some(&leading) = coefficients.last() else {
18        return Ok(());
19    };
20    let Some(inverse) = leading.mod_inverse(m) else {
21        return Err(leading.gcd(m));
22    };
23    if inverse != T::ONE {
24        let data = T::precompute_mod_mul_data(&m);
25        for c in coefficients {
26            c.mod_mul_precomputed_assign(inverse, m, &data);
27        }
28    }
29    Ok(())
30}
31
32fn assert_reduced<T: PrimitiveUnsigned>(p: &UnsignedPolynomial<T>, m: T) {
33    assert!(
34        p.mod_is_reduced(&m),
35        "self must be reduced mod m, but {p} has a coefficient >= {m}"
36    );
37}
38
39impl<T: PrimitiveUnsigned> ModMakeMonic<T> for UnsignedPolynomial<T> {
40    type Output = Self;
41    type Factor = T;
42
43    /// Makes an [`UnsignedPolynomial`] monic modulo `m`, by multiplying it by the inverse of its
44    /// leading coefficient, taking the polynomial by value. The coefficients must already be
45    /// reduced modulo `m`.
46    ///
47    /// If the leading coefficient has no inverse modulo `m`, the error is its GCD with `m`, a
48    /// nontrivial factor of `m`. The zero polynomial is returned unchanged.
49    ///
50    /// # Worst-case complexity
51    /// $T(n) = O(n)$
52    ///
53    /// $M(n) = O(1)$
54    ///
55    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
56    ///
57    /// # Panics
58    /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
59    ///
60    /// # Examples
61    /// ```
62    /// use core::str::FromStr;
63    /// use malachite_base::num::basic::traits::Zero;
64    /// use malachite_base::polynomial::ModMakeMonic;
65    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
66    ///
67    /// // 3 * 5 = 15, which is 1 mod 7.
68    /// let p = UnsignedPolynomial::<u8>::from_str("3*x^2+x+2").unwrap();
69    /// assert_eq!(
70    ///     p.clone().mod_make_monic(7).unwrap().to_string(),
71    ///     "x^2+5*x+3"
72    /// );
73    /// // 2 has no inverse mod 4, and shares the factor 2 with it.
74    /// let p = UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap();
75    /// assert_eq!(p.clone().mod_make_monic(4), Err(2));
76    /// assert_eq!(
77    ///     UnsignedPolynomial::<u8>::ZERO.mod_make_monic(7),
78    ///     Ok(UnsignedPolynomial::ZERO)
79    /// );
80    /// ```
81    ///
82    /// This is equivalent to `fmpz_mod_poly_make_monic_f` from `fmpz_mod_poly/make_monic.c`, FLINT
83    /// 3.6.0, with the factor returned as the error; `nmod_poly_make_monic` aborts instead.
84    fn mod_make_monic(mut self, m: T) -> Result<Self, T> {
85        assert_reduced(&self, m);
86        mod_make_monic_in_place(&mut self.coefficients, m)?;
87        Ok(self)
88    }
89}
90
91impl<T: PrimitiveUnsigned> ModMakeMonic<T> for &UnsignedPolynomial<T> {
92    type Output = UnsignedPolynomial<T>;
93    type Factor = T;
94
95    /// Makes an [`UnsignedPolynomial`] monic modulo `m`, by multiplying it by the inverse of its
96    /// leading coefficient, taking the polynomial by reference. The coefficients must already be
97    /// reduced modulo `m`.
98    ///
99    /// If the leading coefficient has no inverse modulo `m`, the error is its GCD with `m`, a
100    /// nontrivial factor of `m`. The zero polynomial is returned unchanged.
101    ///
102    /// # Worst-case complexity
103    /// $T(n) = O(n)$
104    ///
105    /// $M(n) = O(n)$
106    ///
107    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
108    ///
109    /// # Panics
110    /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
111    ///
112    /// # Examples
113    /// ```
114    /// use core::str::FromStr;
115    /// use malachite_base::num::basic::traits::Zero;
116    /// use malachite_base::polynomial::ModMakeMonic;
117    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
118    ///
119    /// // 3 * 5 = 15, which is 1 mod 7.
120    /// let p = UnsignedPolynomial::<u8>::from_str("3*x^2+x+2").unwrap();
121    /// assert_eq!((&p).mod_make_monic(7).unwrap().to_string(), "x^2+5*x+3");
122    /// // 2 has no inverse mod 4, and shares the factor 2 with it.
123    /// let p = UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap();
124    /// assert_eq!((&p).mod_make_monic(4), Err(2));
125    /// assert_eq!(
126    ///     (&UnsignedPolynomial::<u8>::ZERO).mod_make_monic(7),
127    ///     Ok(UnsignedPolynomial::ZERO)
128    /// );
129    /// ```
130    ///
131    /// This is equivalent to `fmpz_mod_poly_make_monic_f` from `fmpz_mod_poly/make_monic.c`, FLINT
132    /// 3.6.0, with the factor returned as the error; `nmod_poly_make_monic` aborts instead.
133    fn mod_make_monic(self, m: T) -> Result<UnsignedPolynomial<T>, T> {
134        assert_reduced(self, m);
135        let mut coefficients = self.coefficients.clone();
136        mod_make_monic_in_place(&mut coefficients, m)?;
137        Ok(UnsignedPolynomial { coefficients })
138    }
139}
140
141impl<T: PrimitiveUnsigned> ModMakeMonicAssign<T> for UnsignedPolynomial<T> {
142    type Factor = T;
143
144    /// Makes an [`UnsignedPolynomial`] monic modulo `m` in place, by multiplying it by the inverse
145    /// of its leading coefficient. The coefficients must already be reduced modulo `m`.
146    ///
147    /// If the leading coefficient has no inverse modulo `m`, the polynomial is left unchanged and
148    /// the error is the leading coefficient's GCD with `m`, a nontrivial factor of `m`. The zero
149    /// polynomial is left unchanged.
150    ///
151    /// # Worst-case complexity
152    /// $T(n) = O(n)$
153    ///
154    /// $M(n) = O(1)$
155    ///
156    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.len()`.
157    ///
158    /// # Panics
159    /// Panics if `m` is 0, or if any coefficient of `self` is greater than or equal to `m`.
160    ///
161    /// # Examples
162    /// ```
163    /// use core::str::FromStr;
164    /// use malachite_base::polynomial::ModMakeMonicAssign;
165    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
166    ///
167    /// let mut p = UnsignedPolynomial::<u8>::from_str("3*x^2+x+2").unwrap();
168    /// assert_eq!(p.mod_make_monic_assign(7), Ok(()));
169    /// assert_eq!(p.to_string(), "x^2+5*x+3");
170    ///
171    /// let mut p = UnsignedPolynomial::<u8>::from_str("2*x+1").unwrap();
172    /// assert_eq!(p.mod_make_monic_assign(4), Err(2));
173    /// assert_eq!(p.to_string(), "2*x+1");
174    /// ```
175    fn mod_make_monic_assign(&mut self, m: T) -> Result<(), T> {
176        assert_reduced(self, m);
177        mod_make_monic_in_place(&mut self.coefficients, m)
178    }
179}