Skip to content

Commit

Permalink
gcd, lcm, gcdx for Rational (JuliaLang#33910)
Browse files Browse the repository at this point in the history
  • Loading branch information
KlausC authored and StefanKarpinski committed Dec 15, 2019
1 parent e467661 commit 3f92832
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 9 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ New language features
* Function composition now supports multiple functions: `∘(f, g, h) = f ∘ g ∘ h`
and splatting `∘(fs...)` for composing an iterable collection of functions ([#33568]).

* Functions `gcd`, `lcm`, and `gcdx` now support `Rational` arguments ([#33910]).

* `a[begin]` can now be used to address the first element of an integer-indexed collection `a`.
The index is computed by `firstindex(a)` ([#33946]).

Expand Down
31 changes: 22 additions & 9 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
gcd(x,y)
Greatest common (positive) divisor (or zero if `x` and `y` are both zero).
The arguments may be integer and rational numbers.
!!! compat "Julia 1.4"
Rational arguments require Julia 1.4 or later.
# Examples
```jldoctest
Expand Down Expand Up @@ -53,6 +57,10 @@ end
lcm(x,y)
Least common (non-negative) multiple.
The arguments may be integer and rational numbers.
!!! compat "Julia 1.4"
Rational arguments require Julia 1.4 or later.
# Examples
```jldoctest
Expand All @@ -72,16 +80,16 @@ function lcm(a::T, b::T) where T<:Integer
end
end

gcd(a::Integer) = a
lcm(a::Integer) = a
gcd(a::Integer, b::Integer) = gcd(promote(a,b)...)
lcm(a::Integer, b::Integer) = lcm(promote(a,b)...)
gcd(a::Integer, b::Integer...) = gcd(a, gcd(b...))
lcm(a::Integer, b::Integer...) = lcm(a, lcm(b...))
gcd(a::Union{Integer,Rational}) = a
lcm(a::Union{Integer,Rational}) = a
gcd(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = gcd(promote(a,b)...)
lcm(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = lcm(promote(a,b)...)
gcd(a::Union{Integer,Rational}, b::Union{Integer,Rational}...) = gcd(a, gcd(b...))
lcm(a::Union{Integer,Rational}, b::Union{Integer,Rational}...) = lcm(a, lcm(b...))

lcm(abc::AbstractArray{<:Integer}) = reduce(lcm, abc; init=one(eltype(abc)))
lcm(abc::AbstractArray{<:Union{Integer,Rational}}) = reduce(lcm, abc; init=one(eltype(abc)))

function gcd(abc::AbstractArray{<:Integer})
function gcd(abc::AbstractArray{<:Union{Integer,Rational}})
a = zero(eltype(abc))
for b in abc
a = gcd(a,b)
Expand All @@ -100,6 +108,11 @@ Computes the greatest common (positive) divisor of `x` and `y` and their Bézout
coefficients, i.e. the integer coefficients `u` and `v` that satisfy
``ux+vy = d = gcd(x,y)``. ``gcdx(x,y)`` returns ``(d,u,v)``.
The arguments may be integer and rational numbers.
!!! compat "Julia 1.4"
Rational arguments require Julia 1.4 or later.
# Examples
```jldoctest
julia> gcdx(12, 42)
Expand Down Expand Up @@ -133,7 +146,7 @@ function gcdx(a::T, b::T) where T<:Integer
end
a < 0 ? (-a, -s0, -t0) : (a, s0, t0)
end
gcdx(a::Integer, b::Integer) = gcdx(promote(a,b)...)
gcdx(a::Union{Integer,Rational}, b::Union{Integer,Rational}) = gcdx(promote(a,b)...)

# multiplicative inverse of n mod m, error if none

Expand Down
17 changes: 17 additions & 0 deletions base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -457,3 +457,20 @@ function lerpi(j::Integer, d::Integer, a::Rational, b::Rational)
end

float(::Type{Rational{T}}) where {T<:Integer} = float(T)

gcd(x::Rational, y::Rational) = gcd(x.num, y.num) // lcm(x.den, y.den)
lcm(x::Rational, y::Rational) = lcm(x.num, y.num) // gcd(x.den, y.den)
function gcdx(x::Rational, y::Rational)
c = gcd(x, y)
if iszero(c.num)
a, b = one(c.num), c.num
elseif iszero(c.den)
a = ifelse(iszero(x.den), one(c.den), c.den)
b = ifelse(iszero(y.den), one(c.den), c.den)
else
idiv(x, c) = div(x.num, c.num) * div(c.den, x.den)
_, a, b = gcdx(idiv(x, c), idiv(y, c))
end
c, a, b
end

20 changes: 20 additions & 0 deletions test/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,23 @@ end
@test -Rational(true) == -1//1
@test -Rational(false) == 0//1
end

# issue #27039
@testset "gcd, lcm, gcdx for Rational" begin
a = 6 // 35
b = 10 // 21
@test gcd(a, b) == 2//105
@test lcm(a, b) == 30//7
@test gcdx(a, b) == (2//105, -11, 4)

@test gcdx(1//0, 1//2) == (1//0, 1, 0)
@test gcdx(1//2, 1//0) == (1//0, 0, 1)
@test gcdx(1//0, 1//0) == (1//0, 1, 1)
@test gcdx(1//0, 0//1) == (1//0, 1, 0)

@test gcdx(1//3, 2) == (1//3, 1, 0)
@test lcm(1//3, 1) == 1//1
@test lcm(3//1, 1//0) == 3//1
@test lcm(0//1, 1//0) == 0//1
end

0 comments on commit 3f92832

Please sign in to comment.