Skip to content

Commit 3f92832

Browse files
KlausCStefanKarpinski
authored andcommitted
gcd, lcm, gcdx for Rational (#33910)
1 parent e467661 commit 3f92832

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ New language features
1111
* Function composition now supports multiple functions: `∘(f, g, h) = f ∘ g ∘ h`
1212
and splatting `∘(fs...)` for composing an iterable collection of functions ([#33568]).
1313

14+
* Functions `gcd`, `lcm`, and `gcdx` now support `Rational` arguments ([#33910]).
15+
1416
* `a[begin]` can now be used to address the first element of an integer-indexed collection `a`.
1517
The index is computed by `firstindex(a)` ([#33946]).
1618

base/intfuncs.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
gcd(x,y)
77
88
Greatest common (positive) divisor (or zero if `x` and `y` are both zero).
9+
The arguments may be integer and rational numbers.
10+
11+
!!! compat "Julia 1.4"
12+
Rational arguments require Julia 1.4 or later.
913
1014
# Examples
1115
```jldoctest
@@ -53,6 +57,10 @@ end
5357
lcm(x,y)
5458
5559
Least common (non-negative) multiple.
60+
The arguments may be integer and rational numbers.
61+
62+
!!! compat "Julia 1.4"
63+
Rational arguments require Julia 1.4 or later.
5664
5765
# Examples
5866
```jldoctest
@@ -72,16 +80,16 @@ function lcm(a::T, b::T) where T<:Integer
7280
end
7381
end
7482

75-
gcd(a::Integer) = a
76-
lcm(a::Integer) = a
77-
gcd(a::Integer, b::Integer) = gcd(promote(a,b)...)
78-
lcm(a::Integer, b::Integer) = lcm(promote(a,b)...)
79-
gcd(a::Integer, b::Integer...) = gcd(a, gcd(b...))
80-
lcm(a::Integer, b::Integer...) = lcm(a, lcm(b...))
83+
gcd(a::Union{Integer,Rational}) = a
84+
lcm(a::Union{Integer,Rational}) = a
85+
gcd(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = gcd(promote(a,b)...)
86+
lcm(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = lcm(promote(a,b)...)
87+
gcd(a::Union{Integer,Rational}, b::Union{Integer,Rational}...) = gcd(a, gcd(b...))
88+
lcm(a::Union{Integer,Rational}, b::Union{Integer,Rational}...) = lcm(a, lcm(b...))
8189

82-
lcm(abc::AbstractArray{<:Integer}) = reduce(lcm, abc; init=one(eltype(abc)))
90+
lcm(abc::AbstractArray{<:Union{Integer,Rational}}) = reduce(lcm, abc; init=one(eltype(abc)))
8391

84-
function gcd(abc::AbstractArray{<:Integer})
92+
function gcd(abc::AbstractArray{<:Union{Integer,Rational}})
8593
a = zero(eltype(abc))
8694
for b in abc
8795
a = gcd(a,b)
@@ -100,6 +108,11 @@ Computes the greatest common (positive) divisor of `x` and `y` and their Bézout
100108
coefficients, i.e. the integer coefficients `u` and `v` that satisfy
101109
``ux+vy = d = gcd(x,y)``. ``gcdx(x,y)`` returns ``(d,u,v)``.
102110
111+
The arguments may be integer and rational numbers.
112+
113+
!!! compat "Julia 1.4"
114+
Rational arguments require Julia 1.4 or later.
115+
103116
# Examples
104117
```jldoctest
105118
julia> gcdx(12, 42)
@@ -133,7 +146,7 @@ function gcdx(a::T, b::T) where T<:Integer
133146
end
134147
a < 0 ? (-a, -s0, -t0) : (a, s0, t0)
135148
end
136-
gcdx(a::Integer, b::Integer) = gcdx(promote(a,b)...)
149+
gcdx(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = gcdx(promote(a,b)...)
137150

138151
# multiplicative inverse of n mod m, error if none
139152

base/rational.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,20 @@ function lerpi(j::Integer, d::Integer, a::Rational, b::Rational)
457457
end
458458

459459
float(::Type{Rational{T}}) where {T<:Integer} = float(T)
460+
461+
gcd(x::Rational, y::Rational) = gcd(x.num, y.num) // lcm(x.den, y.den)
462+
lcm(x::Rational, y::Rational) = lcm(x.num, y.num) // gcd(x.den, y.den)
463+
function gcdx(x::Rational, y::Rational)
464+
c = gcd(x, y)
465+
if iszero(c.num)
466+
a, b = one(c.num), c.num
467+
elseif iszero(c.den)
468+
a = ifelse(iszero(x.den), one(c.den), c.den)
469+
b = ifelse(iszero(y.den), one(c.den), c.den)
470+
else
471+
idiv(x, c) = div(x.num, c.num) * div(c.den, x.den)
472+
_, a, b = gcdx(idiv(x, c), idiv(y, c))
473+
end
474+
c, a, b
475+
end
476+

test/rational.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,23 @@ end
377377
@test -Rational(true) == -1//1
378378
@test -Rational(false) == 0//1
379379
end
380+
381+
# issue #27039
382+
@testset "gcd, lcm, gcdx for Rational" begin
383+
a = 6 // 35
384+
b = 10 // 21
385+
@test gcd(a, b) == 2//105
386+
@test lcm(a, b) == 30//7
387+
@test gcdx(a, b) == (2//105, -11, 4)
388+
389+
@test gcdx(1//0, 1//2) == (1//0, 1, 0)
390+
@test gcdx(1//2, 1//0) == (1//0, 0, 1)
391+
@test gcdx(1//0, 1//0) == (1//0, 1, 1)
392+
@test gcdx(1//0, 0//1) == (1//0, 1, 0)
393+
394+
@test gcdx(1//3, 2) == (1//3, 1, 0)
395+
@test lcm(1//3, 1) == 1//1
396+
@test lcm(3//1, 1//0) == 3//1
397+
@test lcm(0//1, 1//0) == 0//1
398+
end
399+

0 commit comments

Comments
 (0)