Skip to main content

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}