Skip to main content

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}