Skip to content

Commit 9dd3f28

Browse files
committed
add vararg gcdx(a, b, c...)
1 parent 17445fe commit 9dd3f28

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

base/intfuncs.jl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,24 +165,34 @@ end
165165

166166
# return (gcd(a, b), x, y) such that ax+by == gcd(a, b)
167167
"""
168-
gcdx(a, b)
168+
gcdx(a, b...)
169169
170170
Computes the greatest common (positive) divisor of `a` and `b` and their Bézout
171171
coefficients, i.e. the integer coefficients `u` and `v` that satisfy
172-
``ua+vb = d = gcd(a, b)``. ``gcdx(a, b)`` returns ``(d, u, v)``.
172+
``u*a + v*b = d = gcd(a, b)``. ``gcdx(a, b)`` returns ``(d, u, v)``.
173+
174+
For more arguments than two, i.e., `gcdx(a, b, c, ...)` the Bézout coefficients are computed
175+
recursively, returning a solution `(d, u, v, w, ...)` to
176+
``u*a + v*b + w*c + ... = d = gcd(a, b, c, ...)``.
173177
174178
The arguments may be integer and rational numbers.
175179
176180
!!! compat "Julia 1.4"
177181
Rational arguments require Julia 1.4 or later.
178182
183+
!!! compat "Julia 1.11"
184+
More or fewer arguments than two require Julia 1.11 or later.
185+
179186
# Examples
180187
```jldoctest
181188
julia> gcdx(12, 42)
182189
(6, -3, 1)
183190
184191
julia> gcdx(240, 46)
185192
(2, -9, 47)
193+
194+
julia> gcdx(15, 12, 20)
195+
(1, 7, -7, -1)
186196
```
187197
188198
!!! note
@@ -215,6 +225,18 @@ Base.@assume_effects :terminates_locally function gcdx(a::Integer, b::Integer)
215225
end
216226
gcdx(a::Real, b::Real) = gcdx(promote(a,b)...)
217227
gcdx(a::T, b::T) where T<:Real = throw(MethodError(gcdx, (a,b)))
228+
gcdx(a::Real) = (gcd(a), signbit(a) ? -one(a) : one(a))
229+
function gcdx(a::Real, b::Real, cs::Real...)
230+
# a solution to the 3-arg `gcdx(a,b,c)` problem, `u*a + v*b + w*c = gcd(a,b,c)`, can be
231+
# obtained recursively from the 2-arg problem, in three steps:
232+
# 1. solve `gcdx(a, b)`: `i*a + j*b = d′ = gcd(a,b)` for `(i,j)`
233+
# 2. solve `gcdx(d′, c)`: `x*gcd(a,b) + yc = gcd(gcd(a,b),c) = gcd(a,b,c)` for `(x,y)`
234+
# 3. return `d = gcd(a,b,c)`, `u = i*x`, `v = j*x`, and `w = y`
235+
# the N-arg solution proceeds similarly, by recursion
236+
d, i, j = gcdx(a, b)
237+
d′, x, ys... = gcdx(d, cs...)
238+
return d′, i*x, j*x, ys...
239+
end
218240

219241
# multiplicative inverse of n mod m, error if none
220242

test/rational.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,22 @@ end
702702
end
703703
end
704704

705+
@testset "gcdx for 1 and 3+ arguments" begin
706+
# one-argument
707+
@test gcdx(7) == (7, 1)
708+
@test gcdx(-7) == (7, -1)
709+
@test gcdx(1//4) == (1//4, 1)
710+
711+
# 3+ arguments
712+
@test gcdx(2//3) == gcdx(2//3) == (2//3, 1)
713+
@test gcdx(15, 12, 20) == (1, 7, -7, -1)
714+
@test gcdx(60//4, 60//5, 60//3) == (1//1, 7, -7, -1)
715+
abcd = (105, 1638, 2145, 3185)
716+
d, uvwp... = gcdx(abcd...)
717+
@test d == sum(abcd .* uvwp) # u*a + v*b + w*c + p*d == gcd(a, b, c, d)
718+
@test (@inferred gcdx(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) isa NTuple{11, Int}
719+
end
720+
705721
@testset "Binary operations with Integer" begin
706722
@test 1//2 - 1 == -1//2
707723
@test -1//2 + 1 == 1//2

0 commit comments

Comments
 (0)