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}