Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gcd, lcm, gcdx for Rational #33910

Merged
merged 3 commits into from
Dec 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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