malachite_q/rational_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::Rational;
10use crate::rational_vector::RationalVector;
11use malachite_base::num::arithmetic::traits::{Content, ContentAndPrimitivePart, PrimitivePart};
12use malachite_nz::integer_vector::IntegerVector;
13use malachite_nz::natural::Natural;
14
15// The content of a vector or polynomial written as integers $A$ over a common denominator $d$ is
16// $\operatorname{cont}(A)/d$. It is already in lowest terms when the numerators and denominator
17// share no factor, as they do for a canonical `RationalPolynomial` and for
18// `to_numerators_and_denominator`.
19pub(crate) const fn content_over(numerator_content: Natural, denominator: Natural) -> Rational {
20 Rational {
21 sign: true,
22 numerator: numerator_content,
23 denominator,
24 }
25}
26
27impl Content for RationalVector {
28 type Output = Rational;
29
30 /// Computes the content of a [`RationalVector`], taking the vector by value.
31 ///
32 /// The content is the unique non-negative rational $c$ such that the vector is $c$ times a
33 /// vector of coprime integers. It is the GCD of the numerators that
34 /// [`to_numerators_and_denominator`](RationalVector::to_numerators_and_denominator) gives,
35 /// divided by the denominator, the least common multiple of the elements' denominators; that
36 /// fraction is already in lowest terms. The content of a vector of zeros, and of the
37 /// 0-dimensional vector, is zero.
38 ///
39 /// $$
40 /// f(v) = \frac{\gcd(a_0, a_1, \ldots, a_{m-1})}{d}, \\quad \text{where} \\quad
41 /// v_i = \frac{a_i}{d} \text{ and } d = \operatorname{lcm}(\operatorname{den}(v_0), \ldots,
42 /// \operatorname{den}(v_{m-1})).
43 /// $$
44 ///
45 /// # Worst-case complexity
46 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
47 ///
48 /// $M(n, m) = O(mn)$
49 ///
50 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
51 /// elements' numerators and denominators, and $m$ is the dimension.
52 ///
53 /// # Examples
54 /// ```
55 /// use core::str::FromStr;
56 /// use malachite_base::num::arithmetic::traits::Content;
57 /// use malachite_q::rational_vector::RationalVector;
58 ///
59 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
60 /// assert_eq!(v.content().to_string(), "1/6");
61 ///
62 /// let v = RationalVector::from_str("(2/3, 4/3)").unwrap();
63 /// assert_eq!(v.content().to_string(), "2/3");
64 /// ```
65 #[inline]
66 fn content(self) -> Rational {
67 let (numerators, denominator) = self.to_numerators_and_denominator();
68 content_over(numerators.content(), denominator)
69 }
70}
71
72impl Content for &RationalVector {
73 type Output = Rational;
74
75 /// Computes the content of a [`RationalVector`], taking the vector by reference.
76 ///
77 /// The content is the unique non-negative rational $c$ such that the vector is $c$ times a
78 /// vector of coprime integers. It is the GCD of the numerators that
79 /// [`to_numerators_and_denominator`](RationalVector::to_numerators_and_denominator) gives,
80 /// divided by the denominator, the least common multiple of the elements' denominators; that
81 /// fraction is already in lowest terms. The content of a vector of zeros, and of the
82 /// 0-dimensional vector, is zero.
83 ///
84 /// $$
85 /// f(v) = \frac{\gcd(a_0, a_1, \ldots, a_{m-1})}{d}, \\quad \text{where} \\quad
86 /// v_i = \frac{a_i}{d} \text{ and } d = \operatorname{lcm}(\operatorname{den}(v_0), \ldots,
87 /// \operatorname{den}(v_{m-1})).
88 /// $$
89 ///
90 /// # Worst-case complexity
91 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
92 ///
93 /// $M(n, m) = O(mn)$
94 ///
95 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
96 /// elements' numerators and denominators, and $m$ is the dimension.
97 ///
98 /// # Examples
99 /// ```
100 /// use core::str::FromStr;
101 /// use malachite_base::num::arithmetic::traits::Content;
102 /// use malachite_q::rational_vector::RationalVector;
103 ///
104 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
105 /// assert_eq!((&v).content().to_string(), "1/6");
106 ///
107 /// let v = RationalVector::from_str("(2/3, 4/3)").unwrap();
108 /// assert_eq!((&v).content().to_string(), "2/3");
109 /// ```
110 #[inline]
111 fn content(self) -> Rational {
112 let (numerators, denominator) = self.to_numerators_and_denominator();
113 content_over(numerators.content(), denominator)
114 }
115}
116
117impl PrimitivePart for RationalVector {
118 type Output = IntegerVector;
119
120 /// Computes the primitive part of a [`RationalVector`], taking the vector by value.
121 ///
122 /// This is the vector divided by its content, with every element keeping its sign. Its elements
123 /// are coprime integers, so it is an [`IntegerVector`]: the primitive part of the numerators
124 /// that [`to_numerators_and_denominator`](RationalVector::to_numerators_and_denominator) gives,
125 /// the denominator playing no part. For the one whose first nonzero element is positive, see
126 /// [`canonical_primitive_part`](
127 /// malachite_base::num::arithmetic::traits::CanonicalPrimitivePart::canonical_primitive_part).
128 ///
129 /// $$
130 /// v = \operatorname{cont}(v) \operatorname{pp}(v).
131 /// $$
132 ///
133 /// The primitive part of a vector of zeros is a vector of zeros of the same dimension.
134 ///
135 /// Taking the vector by value saves nothing, since the numerators are built afresh either way.
136 ///
137 /// # Worst-case complexity
138 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
139 ///
140 /// $M(n, m) = O(mn)$
141 ///
142 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
143 /// elements' numerators and denominators, and $m$ is the dimension.
144 ///
145 /// # Examples
146 /// ```
147 /// use core::str::FromStr;
148 /// use malachite_base::num::arithmetic::traits::PrimitivePart;
149 /// use malachite_q::rational_vector::RationalVector;
150 ///
151 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
152 /// assert_eq!(v.primitive_part().to_string(), "(-3, 4, -30)");
153 /// ```
154 #[inline]
155 fn primitive_part(self) -> IntegerVector {
156 self.to_numerators_and_denominator().0.primitive_part()
157 }
158}
159
160impl PrimitivePart for &RationalVector {
161 type Output = IntegerVector;
162
163 /// Computes the primitive part of a [`RationalVector`], taking the vector by reference.
164 ///
165 /// This is the vector divided by its content, with every element keeping its sign. Its elements
166 /// are coprime integers, so it is an [`IntegerVector`]: the primitive part of the numerators
167 /// that [`to_numerators_and_denominator`](RationalVector::to_numerators_and_denominator) gives,
168 /// the denominator playing no part. For the one whose first nonzero element is positive, see
169 /// [`canonical_primitive_part`](
170 /// malachite_base::num::arithmetic::traits::CanonicalPrimitivePart::canonical_primitive_part).
171 ///
172 /// $$
173 /// v = \operatorname{cont}(v) \operatorname{pp}(v).
174 /// $$
175 ///
176 /// The primitive part of a vector of zeros is a vector of zeros of the same dimension.
177 ///
178 /// # Worst-case complexity
179 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
180 ///
181 /// $M(n, m) = O(mn)$
182 ///
183 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
184 /// elements' numerators and denominators, and $m$ is the dimension.
185 ///
186 /// # Examples
187 /// ```
188 /// use core::str::FromStr;
189 /// use malachite_base::num::arithmetic::traits::PrimitivePart;
190 /// use malachite_q::rational_vector::RationalVector;
191 ///
192 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
193 /// assert_eq!((&v).primitive_part().to_string(), "(-3, 4, -30)");
194 /// ```
195 #[inline]
196 fn primitive_part(self) -> IntegerVector {
197 self.to_numerators_and_denominator().0.primitive_part()
198 }
199}
200
201impl ContentAndPrimitivePart for RationalVector {
202 type Content = Rational;
203 type PrimitivePart = IntegerVector;
204
205 /// Computes the content and the primitive part of a [`RationalVector`] together, taking the
206 /// vector by value.
207 ///
208 /// See [`content`](Content::content) and [`primitive_part`](PrimitivePart::primitive_part); the
209 /// numerators are cleared and the content found once rather than twice.
210 ///
211 /// # Worst-case complexity
212 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
213 ///
214 /// $M(n, m) = O(mn)$
215 ///
216 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
217 /// elements' numerators and denominators, and $m$ is the dimension.
218 ///
219 /// # Examples
220 /// ```
221 /// use core::str::FromStr;
222 /// use malachite_base::num::arithmetic::traits::ContentAndPrimitivePart;
223 /// use malachite_q::rational_vector::RationalVector;
224 ///
225 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
226 /// let (content, primitive_part) = v.content_and_primitive_part();
227 /// assert_eq!(content.to_string(), "1/6");
228 /// assert_eq!(primitive_part.to_string(), "(-3, 4, -30)");
229 /// ```
230 #[inline]
231 fn content_and_primitive_part(self) -> (Rational, IntegerVector) {
232 let (numerators, denominator) = self.to_numerators_and_denominator();
233 let (content, primitive_part) = numerators.content_and_primitive_part();
234 (content_over(content, denominator), primitive_part)
235 }
236}
237
238impl ContentAndPrimitivePart for &RationalVector {
239 type Content = Rational;
240 type PrimitivePart = IntegerVector;
241
242 /// Computes the content and the primitive part of a [`RationalVector`] together, taking the
243 /// vector by reference.
244 ///
245 /// See [`content`](Content::content) and [`primitive_part`](PrimitivePart::primitive_part); the
246 /// numerators are cleared and the content found once rather than twice.
247 ///
248 /// # Worst-case complexity
249 /// $T(n, m) = O(mn (\log n)^2 \log\log n)$
250 ///
251 /// $M(n, m) = O(mn)$
252 ///
253 /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
254 /// elements' numerators and denominators, and $m$ is the dimension.
255 ///
256 /// # Examples
257 /// ```
258 /// use core::str::FromStr;
259 /// use malachite_base::num::arithmetic::traits::ContentAndPrimitivePart;
260 /// use malachite_q::rational_vector::RationalVector;
261 ///
262 /// let v = RationalVector::from_str("(-1/2, 2/3, -5)").unwrap();
263 /// let (content, primitive_part) = (&v).content_and_primitive_part();
264 /// assert_eq!(content.to_string(), "1/6");
265 /// assert_eq!(primitive_part.to_string(), "(-3, 4, -30)");
266 /// ```
267 #[inline]
268 fn content_and_primitive_part(self) -> (Rational, IntegerVector) {
269 let (numerators, denominator) = self.to_numerators_and_denominator();
270 let (content, primitive_part) = numerators.content_and_primitive_part();
271 (content_over(content, denominator), primitive_part)
272 }
273}