malachite_nz/integer_polynomial/arithmetic/content.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 crate::natural::Natural;
12use alloc::vec::Vec;
13use malachite_base::num::arithmetic::traits::{DivExact, DivExactAssign, GcdAssign, NegAssign};
14use malachite_base::num::basic::traits::Zero;
15use malachite_base::polynomial::{
16 Content, ContentAndPrimitivePart, PrimitivePart, PrimitivePartAssign,
17};
18
19// The GCD of the coefficients' absolute values. It stops as soon as it reaches 1, since nothing can
20// lower it further.
21fn content(coefficients: &[Integer]) -> Natural {
22 let mut gcd = Natural::ZERO;
23 for c in coefficients {
24 gcd.gcd_assign(&c.abs);
25 if gcd == 1u32 {
26 break;
27 }
28 }
29 gcd
30}
31
32// Whether the primitive part must be negated: when the leading coefficient is negative.
33fn negate(coefficients: &[Integer]) -> bool {
34 coefficients.last().is_some_and(|c| !c.sign)
35}
36
37// Divides every coefficient by the content, which divides each of them exactly, and negates them
38// all if the leading coefficient is negative.
39fn normalize_in_place(coefficients: &mut [Integer], content: &Natural) {
40 let negate = negate(coefficients);
41 for c in coefficients {
42 if *content > 1u32 {
43 c.abs.div_exact_assign(content);
44 }
45 if negate {
46 c.neg_assign();
47 }
48 }
49}
50
51// The coefficients divided by the content, and negated if the leading coefficient is negative, as
52// new values.
53fn normalized(coefficients: &[Integer], content: &Natural) -> Vec<Integer> {
54 let negate = negate(coefficients);
55 coefficients
56 .iter()
57 .map(|c| {
58 let abs = if *content > 1u32 {
59 (&c.abs).div_exact(content)
60 } else {
61 c.abs.clone()
62 };
63 Integer::from_sign_and_abs(c.sign != negate, abs)
64 })
65 .collect()
66}
67
68impl Content for IntegerPolynomial {
69 type Output = Natural;
70
71 /// Computes the content of an [`IntegerPolynomial`], the GCD of its coefficients, taking the
72 /// polynomial by value.
73 ///
74 /// The content is non-negative, and the content of the zero polynomial is zero. The GCD is
75 /// taken coefficient by coefficient, stopping early once it reaches 1.
76 ///
77 /// $$
78 /// f(p) = \gcd(c_0, c_1, \ldots, c_{n-1}),
79 /// $$
80 ///
81 /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length.
82 ///
83 /// # Worst-case complexity
84 /// $T(n) = O(n (\log n)^2 \log\log n)$
85 ///
86 /// $M(n) = O(n)$
87 ///
88 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
89 /// coefficients.
90 ///
91 /// # Examples
92 /// ```
93 /// use core::str::FromStr;
94 /// use malachite_base::num::basic::traits::Zero;
95 /// use malachite_base::polynomial::Content;
96 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
97 ///
98 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
99 /// assert_eq!(p.clone().content(), 2);
100 /// assert_eq!(IntegerPolynomial::ZERO.content(), 0);
101 /// ```
102 ///
103 /// This is equivalent to `fmpz_poly_content` from `fmpz_poly/content.c`, FLINT 3.6.0.
104 #[inline]
105 fn content(self) -> Natural {
106 content(&self.coefficients)
107 }
108}
109
110impl Content for &IntegerPolynomial {
111 type Output = Natural;
112
113 /// Computes the content of an [`IntegerPolynomial`], the GCD of its coefficients, taking the
114 /// polynomial by reference.
115 ///
116 /// The content is non-negative, and the content of the zero polynomial is zero. The GCD is
117 /// taken coefficient by coefficient, stopping early once it reaches 1.
118 ///
119 /// $$
120 /// f(p) = \gcd(c_0, c_1, \ldots, c_{n-1}),
121 /// $$
122 ///
123 /// where $c_i$ is the coefficient of $x^i$ in $p$ and $n$ is its length.
124 ///
125 /// # Worst-case complexity
126 /// $T(n) = O(n (\log n)^2 \log\log n)$
127 ///
128 /// $M(n) = O(n)$
129 ///
130 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
131 /// coefficients.
132 ///
133 /// # Examples
134 /// ```
135 /// use core::str::FromStr;
136 /// use malachite_base::num::basic::traits::Zero;
137 /// use malachite_base::polynomial::Content;
138 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
139 ///
140 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
141 /// assert_eq!((&p).content(), 2);
142 /// assert_eq!((&IntegerPolynomial::ZERO).content(), 0);
143 /// ```
144 ///
145 /// This is equivalent to `fmpz_poly_content` from `fmpz_poly/content.c`, FLINT 3.6.0.
146 #[inline]
147 fn content(self) -> Natural {
148 content(&self.coefficients)
149 }
150}
151
152impl PrimitivePart for IntegerPolynomial {
153 type Output = Self;
154
155 /// Computes the primitive part of an [`IntegerPolynomial`], taking the polynomial by value.
156 ///
157 /// This is the polynomial divided by its content, with the sign chosen so that the leading
158 /// coefficient is non-negative. The sign matters: when the leading coefficient is negative, the
159 /// content times the primitive part is the negation of the polynomial, and the identity needs
160 /// the sign of the leading coefficient $\operatorname{lc}(p)$.
161 ///
162 /// $$
163 /// p = \operatorname{sgn}(\operatorname{lc}(p)) \operatorname{cont}(p) \operatorname{pp}(p).
164 /// $$
165 ///
166 /// The primitive part of the zero polynomial is zero.
167 ///
168 /// # Worst-case complexity
169 /// $T(n) = O(n (\log n)^2 \log\log n)$
170 ///
171 /// $M(n) = O(1)$
172 ///
173 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
174 /// coefficients.
175 ///
176 /// # Examples
177 /// ```
178 /// use core::str::FromStr;
179 /// use malachite_base::num::basic::traits::Zero;
180 /// use malachite_base::polynomial::PrimitivePart;
181 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
182 ///
183 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
184 /// assert_eq!(p.clone().primitive_part().to_string(), "3*x^2-2*x+5");
185 /// assert_eq!(
186 /// IntegerPolynomial::ZERO.primitive_part(),
187 /// IntegerPolynomial::ZERO
188 /// );
189 /// ```
190 ///
191 /// This is equivalent to `fmpz_poly_primitive_part` from `fmpz_poly/primitive_part.c`, FLINT
192 /// 3.6.0.
193 #[inline]
194 fn primitive_part(mut self) -> Self {
195 let content = content(&self.coefficients);
196 normalize_in_place(&mut self.coefficients, &content);
197 self
198 }
199}
200
201impl PrimitivePart for &IntegerPolynomial {
202 type Output = IntegerPolynomial;
203
204 /// Computes the primitive part of an [`IntegerPolynomial`], taking the polynomial by reference.
205 ///
206 /// This is the polynomial divided by its content, with the sign chosen so that the leading
207 /// coefficient is non-negative. The sign matters: when the leading coefficient is negative, the
208 /// content times the primitive part is the negation of the polynomial, and the identity needs
209 /// the sign of the leading coefficient $\operatorname{lc}(p)$.
210 ///
211 /// $$
212 /// p = \operatorname{sgn}(\operatorname{lc}(p)) \operatorname{cont}(p) \operatorname{pp}(p).
213 /// $$
214 ///
215 /// The primitive part of the zero polynomial is zero.
216 ///
217 /// # Worst-case complexity
218 /// $T(n) = O(n (\log n)^2 \log\log n)$
219 ///
220 /// $M(n) = O(n)$
221 ///
222 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
223 /// coefficients.
224 ///
225 /// # Examples
226 /// ```
227 /// use core::str::FromStr;
228 /// use malachite_base::num::basic::traits::Zero;
229 /// use malachite_base::polynomial::PrimitivePart;
230 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
231 ///
232 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
233 /// assert_eq!((&p).primitive_part().to_string(), "3*x^2-2*x+5");
234 /// assert_eq!(
235 /// (&IntegerPolynomial::ZERO).primitive_part(),
236 /// IntegerPolynomial::ZERO
237 /// );
238 /// ```
239 ///
240 /// This is equivalent to `fmpz_poly_primitive_part` from `fmpz_poly/primitive_part.c`, FLINT
241 /// 3.6.0.
242 #[inline]
243 fn primitive_part(self) -> IntegerPolynomial {
244 let content = content(&self.coefficients);
245 IntegerPolynomial {
246 coefficients: normalized(&self.coefficients, &content),
247 }
248 }
249}
250
251impl PrimitivePartAssign for IntegerPolynomial {
252 /// Replaces an [`IntegerPolynomial`] with its primitive part.
253 ///
254 /// See [`primitive_part`](PrimitivePart::primitive_part).
255 ///
256 /// # Worst-case complexity
257 /// $T(n) = O(n (\log n)^2 \log\log n)$
258 ///
259 /// $M(n) = O(1)$
260 ///
261 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
262 /// coefficients.
263 ///
264 /// # Examples
265 /// ```
266 /// use core::str::FromStr;
267 /// use malachite_base::polynomial::PrimitivePartAssign;
268 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
269 ///
270 /// let mut p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
271 /// p.primitive_part_assign();
272 /// assert_eq!(p.to_string(), "3*x^2-2*x+5");
273 /// ```
274 #[inline]
275 fn primitive_part_assign(&mut self) {
276 let content = content(&self.coefficients);
277 normalize_in_place(&mut self.coefficients, &content);
278 }
279}
280
281impl ContentAndPrimitivePart for IntegerPolynomial {
282 type Content = Natural;
283 type PrimitivePart = Self;
284
285 /// Computes the content and the primitive part of an [`IntegerPolynomial`] together, taking the
286 /// polynomial by value.
287 ///
288 /// See [`content`](Content::content) and [`primitive_part`](PrimitivePart::primitive_part); the
289 /// content is found once rather than twice.
290 ///
291 /// # Worst-case complexity
292 /// $T(n) = O(n (\log n)^2 \log\log n)$
293 ///
294 /// $M(n) = O(1)$
295 ///
296 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
297 /// coefficients.
298 ///
299 /// # Examples
300 /// ```
301 /// use core::str::FromStr;
302 /// use malachite_base::polynomial::ContentAndPrimitivePart;
303 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
304 ///
305 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
306 /// let (content, primitive_part) = p.clone().content_and_primitive_part();
307 /// assert_eq!(content, 2);
308 /// assert_eq!(primitive_part.to_string(), "3*x^2-2*x+5");
309 /// ```
310 #[inline]
311 fn content_and_primitive_part(mut self) -> (Natural, Self) {
312 let content = content(&self.coefficients);
313 normalize_in_place(&mut self.coefficients, &content);
314 (content, self)
315 }
316}
317
318impl ContentAndPrimitivePart for &IntegerPolynomial {
319 type Content = Natural;
320 type PrimitivePart = IntegerPolynomial;
321
322 /// Computes the content and the primitive part of an [`IntegerPolynomial`] together, taking the
323 /// polynomial by reference.
324 ///
325 /// See [`content`](Content::content) and [`primitive_part`](PrimitivePart::primitive_part); the
326 /// content is found once rather than twice.
327 ///
328 /// # Worst-case complexity
329 /// $T(n) = O(n (\log n)^2 \log\log n)$
330 ///
331 /// $M(n) = O(n)$
332 ///
333 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
334 /// coefficients.
335 ///
336 /// # Examples
337 /// ```
338 /// use core::str::FromStr;
339 /// use malachite_base::polynomial::ContentAndPrimitivePart;
340 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
341 ///
342 /// let p = IntegerPolynomial::from_str("-6*x^2+4*x-10").unwrap();
343 /// let (content, primitive_part) = (&p).content_and_primitive_part();
344 /// assert_eq!(content, 2);
345 /// assert_eq!(primitive_part.to_string(), "3*x^2-2*x+5");
346 /// ```
347 #[inline]
348 fn content_and_primitive_part(self) -> (Natural, IntegerPolynomial) {
349 let content = content(&self.coefficients);
350 let coefficients = normalized(&self.coefficients, &content);
351 (content, IntegerPolynomial { coefficients })
352 }
353}