malachite_nz/integer_vector/arithmetic/content_chained.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 malachite_base::num::arithmetic::traits::{ContentChained, Gcd, GcdAssign};
13use malachite_base::num::basic::traits::One;
14
15// Returns the GCD of `gcd` and the absolute values of the elements of `xs`, where `abs` gives the
16// absolute value of an element.
17//
18// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT 3.6.0.
19pub(crate) fn content_chained_helper<T>(
20 xs: &[T],
21 abs: impl Fn(&T) -> &Natural,
22 mut gcd: Natural,
23) -> Natural {
24 // Zeros make no difference, so they are skipped at both ends.
25 let Some(start) = xs.iter().position(|c| *abs(c) != 0u32) else {
26 return gcd;
27 };
28 let end = xs.iter().rposition(|c| *abs(c) != 0u32).unwrap();
29 let mut xs = &xs[start..=end];
30 // An element of absolute value 1 makes the GCD 1. Only the ends are checked, since for the
31 // coefficients of a polynomial they are the likeliest to be ±1.
32 if let [first, .., last] = xs
33 && (*abs(first) == 1u32 || *abs(last) == 1u32)
34 {
35 return Natural::ONE;
36 }
37 // The elements are taken in pairs from both ends, stopping as soon as the GCD reaches 1.
38 while gcd != 1u32
39 && let [first, middle @ .., last] = xs
40 {
41 gcd.gcd_assign(abs(first).gcd(abs(last)));
42 xs = middle;
43 }
44 if gcd != 1u32
45 && let [middle] = xs
46 {
47 gcd.gcd_assign(abs(middle));
48 }
49 gcd
50}
51
52// Returns the GCD of `x` and the absolute values of the elements of `xs`.
53//
54// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT 3.6.0.
55#[doc(hidden)]
56#[inline]
57pub fn integers_content_chained(xs: &[Integer], x: &Natural) -> Natural {
58 content_chained_helper(xs, Integer::unsigned_abs_ref, x.clone())
59}
60
61impl ContentChained<Natural> for IntegerVector {
62 type Output = Natural;
63
64 /// Computes the GCD of `x` and the elements of an [`IntegerVector`], taking the vector by value
65 /// and `x` by value.
66 ///
67 /// The GCD of `x` and the elements of the 0-dimensional vector, or of a vector of zeros, is
68 /// `x`. The computation stops as soon as the GCD reaches 1.
69 ///
70 /// $$
71 /// f(v, x) = \gcd(x, v_0, v_1, \ldots, v_{n-1}),
72 /// $$
73 ///
74 /// where $n$ is the dimension of $v$.
75 ///
76 /// # Worst-case complexity
77 /// $T(n) = O(n (\log n)^2 \log\log n)$
78 ///
79 /// $M(n) = O(n)$
80 ///
81 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
82 /// elements and of `x`.
83 ///
84 /// # Examples
85 /// ```
86 /// use core::str::FromStr;
87 /// use malachite_base::num::arithmetic::traits::ContentChained;
88 /// use malachite_base::num::basic::traits::Zero;
89 /// use malachite_nz::integer_vector::IntegerVector;
90 /// use malachite_nz::natural::Natural;
91 ///
92 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
93 /// assert_eq!(v.clone().content_chained(Natural::from(4u32)), 2u32);
94 /// assert_eq!(v.content_chained(Natural::ZERO), 2u32);
95 /// let v = IntegerVector::from_str("()").unwrap();
96 /// assert_eq!(v.content_chained(Natural::from(6u32)), 6u32);
97 /// ```
98 ///
99 /// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT
100 /// 3.6.0.
101 #[inline]
102 fn content_chained(self, x: Natural) -> Natural {
103 content_chained_helper(&self.elements, Integer::unsigned_abs_ref, x)
104 }
105}
106
107impl ContentChained<&Natural> for IntegerVector {
108 type Output = Natural;
109
110 /// Computes the GCD of `x` and the elements of an [`IntegerVector`], taking the vector by value
111 /// and `x` by reference.
112 ///
113 /// The GCD of `x` and the elements of the 0-dimensional vector, or of a vector of zeros, is
114 /// `x`. The computation stops as soon as the GCD reaches 1.
115 ///
116 /// $$
117 /// f(v, x) = \gcd(x, v_0, v_1, \ldots, v_{n-1}),
118 /// $$
119 ///
120 /// where $n$ is the dimension of $v$.
121 ///
122 /// # Worst-case complexity
123 /// $T(n) = O(n (\log n)^2 \log\log n)$
124 ///
125 /// $M(n) = O(n)$
126 ///
127 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
128 /// elements and of `x`.
129 ///
130 /// # Examples
131 /// ```
132 /// use core::str::FromStr;
133 /// use malachite_base::num::arithmetic::traits::ContentChained;
134 /// use malachite_base::num::basic::traits::Zero;
135 /// use malachite_nz::integer_vector::IntegerVector;
136 /// use malachite_nz::natural::Natural;
137 ///
138 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
139 /// assert_eq!(v.clone().content_chained(&Natural::from(4u32)), 2u32);
140 /// assert_eq!(v.content_chained(&Natural::ZERO), 2u32);
141 /// let v = IntegerVector::from_str("()").unwrap();
142 /// assert_eq!(v.content_chained(&Natural::from(6u32)), 6u32);
143 /// ```
144 ///
145 /// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT
146 /// 3.6.0.
147 #[inline]
148 fn content_chained(self, x: &Natural) -> Natural {
149 content_chained_helper(&self.elements, Integer::unsigned_abs_ref, x.clone())
150 }
151}
152
153impl ContentChained<Natural> for &IntegerVector {
154 type Output = Natural;
155
156 /// Computes the GCD of `x` and the elements of an [`IntegerVector`], taking the vector by
157 /// reference and `x` by value.
158 ///
159 /// The GCD of `x` and the elements of the 0-dimensional vector, or of a vector of zeros, is
160 /// `x`. The computation stops as soon as the GCD reaches 1.
161 ///
162 /// $$
163 /// f(v, x) = \gcd(x, v_0, v_1, \ldots, v_{n-1}),
164 /// $$
165 ///
166 /// where $n$ is the dimension of $v$.
167 ///
168 /// # Worst-case complexity
169 /// $T(n) = O(n (\log n)^2 \log\log n)$
170 ///
171 /// $M(n) = O(n)$
172 ///
173 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
174 /// elements and of `x`.
175 ///
176 /// # Examples
177 /// ```
178 /// use core::str::FromStr;
179 /// use malachite_base::num::arithmetic::traits::ContentChained;
180 /// use malachite_base::num::basic::traits::Zero;
181 /// use malachite_nz::integer_vector::IntegerVector;
182 /// use malachite_nz::natural::Natural;
183 ///
184 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
185 /// assert_eq!((&v).content_chained(Natural::from(4u32)), 2u32);
186 /// assert_eq!((&v).content_chained(Natural::ZERO), 2u32);
187 /// let v = IntegerVector::from_str("()").unwrap();
188 /// assert_eq!((&v).content_chained(Natural::from(6u32)), 6u32);
189 /// ```
190 ///
191 /// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT
192 /// 3.6.0.
193 #[inline]
194 fn content_chained(self, x: Natural) -> Natural {
195 content_chained_helper(&self.elements, Integer::unsigned_abs_ref, x)
196 }
197}
198
199impl ContentChained<&Natural> for &IntegerVector {
200 type Output = Natural;
201
202 /// Computes the GCD of `x` and the elements of an [`IntegerVector`], taking the vector by
203 /// reference and `x` by reference.
204 ///
205 /// The GCD of `x` and the elements of the 0-dimensional vector, or of a vector of zeros, is
206 /// `x`. The computation stops as soon as the GCD reaches 1.
207 ///
208 /// $$
209 /// f(v, x) = \gcd(x, v_0, v_1, \ldots, v_{n-1}),
210 /// $$
211 ///
212 /// where $n$ is the dimension of $v$.
213 ///
214 /// # Worst-case complexity
215 /// $T(n) = O(n (\log n)^2 \log\log n)$
216 ///
217 /// $M(n) = O(n)$
218 ///
219 /// where $T$ is time, $M$ is additional memory, and $n$ is the total number of bits of the
220 /// elements and of `x`.
221 ///
222 /// # Examples
223 /// ```
224 /// use core::str::FromStr;
225 /// use malachite_base::num::arithmetic::traits::ContentChained;
226 /// use malachite_base::num::basic::traits::Zero;
227 /// use malachite_nz::integer_vector::IntegerVector;
228 /// use malachite_nz::natural::Natural;
229 ///
230 /// let v = IntegerVector::from_str("(-6, 4, -10)").unwrap();
231 /// assert_eq!((&v).content_chained(&Natural::from(4u32)), 2u32);
232 /// assert_eq!((&v).content_chained(&Natural::ZERO), 2u32);
233 /// let v = IntegerVector::from_str("()").unwrap();
234 /// assert_eq!((&v).content_chained(&Natural::from(6u32)), 6u32);
235 /// ```
236 ///
237 /// This is equivalent to `_fmpz_vec_content_chained` from `fmpz_vec/content_chained.c`, FLINT
238 /// 3.6.0.
239 #[inline]
240 fn content_chained(self, x: &Natural) -> Natural {
241 content_chained_helper(&self.elements, Integer::unsigned_abs_ref, x.clone())
242 }
243}