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}