Skip to main content

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