malachite_nz/integer_vector/arithmetic/canonical_primitive_part.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_vector::IntegerVector;
10use crate::integer_vector::arithmetic::content::{content, normalize_in_place, normalized};
11use crate::natural::Natural;
12use malachite_base::num::arithmetic::traits::{
13 CanonicalPrimitivePart, CanonicalPrimitivePartAssign, ContentAndCanonicalPrimitivePart,
14};
15use malachite_base::vector::Vector;
16
17// Whether the canonical primitive part must be negated: when the pivot, the first nonzero element,
18// is negative.
19fn negate(v: &IntegerVector) -> bool {
20 v.pivot().is_some_and(|x| !x.sign)
21}
22
23impl CanonicalPrimitivePart for IntegerVector {
24 type Output = Self;
25
26 /// Computes the canonical primitive part of an [`IntegerVector`], taking the vector by value.
27 ///
28 /// This is the
29 /// [`primitive_part`](malachite_base::num::arithmetic::traits::PrimitivePart::primitive_part),
30 /// negated if its first nonzero element is negative, so that the first nonzero element is
31 /// positive. A vector and its negation have the same canonical primitive part. The canonical
32 /// primitive part of a vector of zeros is itself, keeping its dimension.
33 ///
34 /// $$
35 /// v = \pm \operatorname{cont}(v) \operatorname{cpp}(v).
36 /// $$
37 ///
38 /// # Worst-case complexity
39 /// $T(n) = O(n (\log n)^2 \log\log n)$
40 ///
41 /// $M(n) = O(1)$
42 ///
43 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
44 /// elements.
45 ///
46 /// # Examples
47 /// ```
48 /// use core::str::FromStr;
49 /// use malachite_base::num::arithmetic::traits::CanonicalPrimitivePart;
50 /// use malachite_nz::integer_vector::IntegerVector;
51 ///
52 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
53 /// assert_eq!(v.canonical_primitive_part().to_string(), "(3, -2, 5)");
54 ///
55 /// // Leading zeros are skipped: the first nonzero element is made positive.
56 /// let v = IntegerVector::from_str("(0, -2, 4)").unwrap();
57 /// assert_eq!(v.canonical_primitive_part().to_string(), "(0, 1, -2)");
58 /// ```
59 #[inline]
60 fn canonical_primitive_part(mut self) -> Self {
61 self.canonical_primitive_part_assign();
62 self
63 }
64}
65
66impl CanonicalPrimitivePart for &IntegerVector {
67 type Output = IntegerVector;
68
69 /// Computes the canonical primitive part of an [`IntegerVector`], taking the vector by
70 /// reference.
71 ///
72 /// This is the
73 /// [`primitive_part`](malachite_base::num::arithmetic::traits::PrimitivePart::primitive_part),
74 /// negated if its first nonzero element is negative, so that the first nonzero element is
75 /// positive. A vector and its negation have the same canonical primitive part. The canonical
76 /// primitive part of a vector of zeros is itself, keeping its dimension.
77 ///
78 /// $$
79 /// v = \pm \operatorname{cont}(v) \operatorname{cpp}(v).
80 /// $$
81 ///
82 /// # Worst-case complexity
83 /// $T(n) = O(n (\log n)^2 \log\log n)$
84 ///
85 /// $M(n) = O(n)$
86 ///
87 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
88 /// elements.
89 ///
90 /// # Examples
91 /// ```
92 /// use core::str::FromStr;
93 /// use malachite_base::num::arithmetic::traits::CanonicalPrimitivePart;
94 /// use malachite_nz::integer_vector::IntegerVector;
95 ///
96 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
97 /// assert_eq!((&v).canonical_primitive_part().to_string(), "(3, -2, 5)");
98 /// ```
99 #[inline]
100 fn canonical_primitive_part(self) -> IntegerVector {
101 let content = content(&self.elements);
102 IntegerVector {
103 elements: normalized(&self.elements, &content, negate(self)),
104 }
105 }
106}
107
108impl CanonicalPrimitivePartAssign for IntegerVector {
109 /// Replaces an [`IntegerVector`] with its canonical primitive part.
110 ///
111 /// See [`canonical_primitive_part`](CanonicalPrimitivePart::canonical_primitive_part).
112 ///
113 /// # Worst-case complexity
114 /// $T(n) = O(n (\log n)^2 \log\log n)$
115 ///
116 /// $M(n) = O(1)$
117 ///
118 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
119 /// elements.
120 ///
121 /// # Examples
122 /// ```
123 /// use core::str::FromStr;
124 /// use malachite_base::num::arithmetic::traits::CanonicalPrimitivePartAssign;
125 /// use malachite_nz::integer_vector::IntegerVector;
126 ///
127 /// let mut v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
128 /// v.canonical_primitive_part_assign();
129 /// assert_eq!(v.to_string(), "(3, -2, 5)");
130 /// ```
131 #[inline]
132 fn canonical_primitive_part_assign(&mut self) {
133 let content = content(&self.elements);
134 let negate = negate(self);
135 normalize_in_place(&mut self.elements, &content, negate);
136 }
137}
138
139impl ContentAndCanonicalPrimitivePart for IntegerVector {
140 type Content = Natural;
141 type CanonicalPrimitivePart = Self;
142
143 /// Computes the content and the canonical primitive part of an [`IntegerVector`] together,
144 /// taking the vector by value.
145 ///
146 /// See [`content`](malachite_base::num::arithmetic::traits::Content::content) and
147 /// [`canonical_primitive_part`](CanonicalPrimitivePart::canonical_primitive_part); the content
148 /// is found once rather than twice.
149 ///
150 /// # Worst-case complexity
151 /// $T(n) = O(n (\log n)^2 \log\log n)$
152 ///
153 /// $M(n) = O(1)$
154 ///
155 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
156 /// elements.
157 ///
158 /// # Examples
159 /// ```
160 /// use core::str::FromStr;
161 /// use malachite_base::num::arithmetic::traits::ContentAndCanonicalPrimitivePart;
162 /// use malachite_nz::integer_vector::IntegerVector;
163 ///
164 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
165 /// let (content, primitive_part) = v.content_and_canonical_primitive_part();
166 /// assert_eq!(content, 2u32);
167 /// assert_eq!(primitive_part.to_string(), "(3, -2, 5)");
168 /// ```
169 #[inline]
170 fn content_and_canonical_primitive_part(mut self) -> (Natural, Self) {
171 let content = content(&self.elements);
172 let negate = negate(&self);
173 normalize_in_place(&mut self.elements, &content, negate);
174 (content, self)
175 }
176}
177
178impl ContentAndCanonicalPrimitivePart for &IntegerVector {
179 type Content = Natural;
180 type CanonicalPrimitivePart = IntegerVector;
181
182 /// Computes the content and the canonical primitive part of an [`IntegerVector`] together,
183 /// taking the vector by reference.
184 ///
185 /// See [`content`](malachite_base::num::arithmetic::traits::Content::content) and
186 /// [`canonical_primitive_part`](CanonicalPrimitivePart::canonical_primitive_part); the content
187 /// is found once rather than twice.
188 ///
189 /// # Worst-case complexity
190 /// $T(n) = O(n (\log n)^2 \log\log n)$
191 ///
192 /// $M(n) = O(n)$
193 ///
194 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
195 /// elements.
196 ///
197 /// # Examples
198 /// ```
199 /// use core::str::FromStr;
200 /// use malachite_base::num::arithmetic::traits::ContentAndCanonicalPrimitivePart;
201 /// use malachite_nz::integer_vector::IntegerVector;
202 ///
203 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
204 /// let (content, primitive_part) = (&v).content_and_canonical_primitive_part();
205 /// assert_eq!(content, 2u32);
206 /// assert_eq!(primitive_part.to_string(), "(3, -2, 5)");
207 /// ```
208 #[inline]
209 fn content_and_canonical_primitive_part(self) -> (Natural, IntegerVector) {
210 let content = content(&self.elements);
211 let elements = normalized(&self.elements, &content, negate(self));
212 (content, IntegerVector { elements })
213 }
214}