Skip to content

Instantly share code, notes, and snippets.

@JeffreySarnoff
Last active May 1, 2020 09:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JeffreySarnoff/2d47f8922d7cd63be80b74f1095a3481 to your computer and use it in GitHub Desktop.
Save JeffreySarnoff/2d47f8922d7cd63be80b74f1095a3481 to your computer and use it in GitHub Desktop.
revising rational.jl (first part of the file)`
"""
Rational{T<:Integer} <: Real
Rational number type, with numerator and denominator of type `T`.
Rationals are checked for overflow.
"""
struct Rational{T<:Integer} <: Real
num::T
den::T
function Rational{T}(num::T, den::T; reduce::Bool) where {T<:Integer}
iszero(den) && iszero(num) && __throw_rational_argerror_zero(T)
num, den = prepare(num, den)
if reduce
num, den = divgcd(num, den)
end
return new{T}(num, den)
end
end
@inline function prepare(num::T, den::T) where T<:Integer
if signbit(den)
den = -den
signbit(den) && __throw_rational_argerror_typemin(T)
num = -num
elseif iszero(den)
num = copysign(one(T), num)
end
return num, den
end
@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)"))
@noinline __throw_rational_argerror_typemin(T) = throw(ArgumentError("invalid rational: denominator can't be typemin($T)"))
Rational(num::T, den::T) where {T<:Integer} = Rational{T}(num, den, reduce=true)
Rational(num::Integer, den::Integer) = Rational(promote(num, den)...)
Rational{T}(num::T, den::T) where {T<:Integer} = Rational{T}(num, den, reduce=true)
Rational{T}(num::Integer, den::Integer) where {T<:Integer} = Rational(promote(num, den)...)
Rational(n::T) where {T<:Integer} = Rational(n, one(T))
function divgcd(x::Integer,y::Integer)
g = gcd(x,y)
div(x,g), div(y,g)
end
"""
//(num, den)
Divide two integers or rational numbers, giving a [`Rational`](@ref) result.
# Examples
```jldoctest
julia> 3 // 5
3//5
julia> (3 // 5) // (2 // 1)
3//10
```
"""
//(n::Integer, d::Integer) = Rational(n,d)
function //(x::Rational, y::Integer)
xn, yn = divgcd(x.num,y)
prepare(xn, checked_mul(x.den, yn))
end
function //(x::Integer, y::Rational)
xn, yn = divgcd(x,y.num)
prepare(checked_mul(xn, y.den), yn)
end
function //(x::Rational, y::Rational)
xn,yn = divgcd(x.num,y.num)
xd,yd = divgcd(x.den,y.den)
prepare(checked_mul(xn, yd), checked_mul(xd, yn))
end
//(x::Complex, y::Real) = complex(real(x)//y, imag(x)//y)
//(x::Number, y::Complex) = x*conj(y)//abs2(y)
//(X::AbstractArray, y::Number) = X .// y
function show(io::IO, x::Rational)
show(io, numerator(x))
print(io, "//")
show(io, denominator(x))
end
function read(s::IO, ::Type{Rational{T}}) where T<:Integer
r = read(s,T)
i = read(s,T)
r//i
end
function write(s::IO, z::Rational)
write(s,numerator(z),denominator(z))
end
function Rational{T}(x::Rational) where T<:Integer
Rational{T}(convert(T, x.num), convert(T,x.den); checked=false)
end
function Rational{T}(x::Integer) where T<:Integer
Rational{T}(convert(T, x), one(T); checked=false)
end
Rational(x::Rational) = x
Bool(x::Rational) = x==0 ? false : x==1 ? true :
throw(InexactError(:Bool, Bool, x)) # to resolve ambiguity
(::Type{T})(x::Rational) where {T<:Integer} = (isinteger(x) ? convert(T, x.num) :
throw(InexactError(nameof(T), T, x)))
AbstractFloat(x::Rational) = float(x.num)/float(x.den)
function (::Type{T})(x::Rational{S}) where T<:AbstractFloat where S
P = promote_type(T,S)
convert(T, convert(P,x.num)/convert(P,x.den))
end
function Rational{T}(x::AbstractFloat) where T<:Integer
r = rationalize(T, x, tol=0)
x == convert(typeof(x), r) || throw(InexactError(:Rational, Rational{T}, x))
r
end
Rational(x::Float64) = Rational{Int64}(x)
Rational(x::Float32) = Rational{Int}(x)
big(q::Rational) = Rational{BigInt}(big(numerator(q)), big(denominator(q)); reduce=false)
big(z::Complex{<:Rational{<:Integer}}) = Complex{Rational{BigInt}}(z)
promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)}
promote_rule(::Type{Rational{T}}, ::Type{Rational{S}}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)}
promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:AbstractFloat} = promote_type(T,S)
widen(::Type{Rational{T}}) where {T} = Rational{widen(T)}
@noinline __throw_negate_unsigned() = throw(OverflowError("cannot negate unsigned number"))
"""
rationalize([T<:Integer=Int,] x; tol::Real=eps(x))
Approximate floating point number `x` as a [`Rational`](@ref) number with components
of the given integer type. The result will differ from `x` by no more than `tol`.
# Examples
```jldoctest
julia> rationalize(5.6)
28//5
julia> a = rationalize(BigInt, 10.3)
103//10
julia> typeof(numerator(a))
BigInt
```
"""
function rationalize(::Type{T}, x::AbstractFloat, tol::Real) where T<:Integer
if tol < 0
throw(ArgumentError("negative tolerance $tol"))
end
T<:Unsigned && x < 0 && __throw_negate_unsigned()
isnan(x) && return T(x)//one(T)
isinf(x) && return Rational(x < 0 ? -one(T) : one(T), zero(T); checked=false)
p, q = (x < 0 ? -one(T) : one(T)), zero(T)
pp, qq = zero(T), one(T)
x = abs(x)
a = trunc(x)
r = x-a
y = one(x)
tolx = oftype(x, tol)
nt, t, tt = tolx, zero(tolx), tolx
ia = np = nq = zero(T)
# compute the successive convergents of the continued fraction
# np // nq = (p*a + pp) // (q*a + qq)
while r > nt
try
ia = convert(T,a)
np = checked_add(checked_mul(ia,p),pp)
nq = checked_add(checked_mul(ia,q),qq)
p, pp = np, p
q, qq = nq, q
catch e
isa(e,InexactError) || isa(e,OverflowError) || rethrow()
return p // q
end
# naive approach of using
# x = 1/r; a = trunc(x); r = x - a
# is inexact, so we store x as x/y
x, y = y, r
a, r = divrem(x,y)
# maintain
# x0 = (p + (-1)^i * r) / q
t, tt = nt, t
nt = a*t+tt
end
# find optimal semiconvergent
# smallest a such that x-a*y < a*t+tt
a = cld(x-tt,y+t)
try
ia = convert(T,a)
np = checked_add(checked_mul(ia,p),pp)
nq = checked_add(checked_mul(ia,q),qq)
return np // nq
catch e
isa(e,InexactError) || isa(e,OverflowError) || rethrow()
return p // q
end
end
rationalize(::Type{T}, x::AbstractFloat; tol::Real = eps(x)) where {T<:Integer} = rationalize(T, x, tol)::Rational{T}
rationalize(x::AbstractFloat; kvs...) = rationalize(Int, x; kvs...)
"""
numerator(x)
Numerator of the rational representation of `x`.
# Examples
```jldoctest
julia> numerator(2//3)
2
julia> numerator(4)
4
```
"""
numerator(x::Integer) = x
numerator(x::Rational) = x.num
"""
denominator(x)
Denominator of the rational representation of `x`.
# Examples
```jldoctest
julia> denominator(2//3)
3
julia> denominator(4)
1
```
"""
denominator(x::Integer) = one(x)
denominator(x::Rational) = x.den
sign(x::Rational) = oftype(x, sign(x.num))
signbit(x::Rational) = signbit(x.num)
copysign(x::Rational{T}, y::Real) where {T<:Integer} = Rational{T}(copysign(x.num, y), x.den; reduce=false)
copysign(x::Rational{T}, y::Rational) where {T<:Integer} = Rational{T}(copysign(x.num, y.num), x.den; reduce=false)
abs(x::Rational{T}) where {T<:Integer} = Rational{T}(abs(x.num), x.den; reduce=false)
typemin(::Type{Rational{T}}) where {T<:Signed} = Rational{T}(-one(T), zero(T); reduce=false)
typemin(::Type{Rational{T}}) where {T<:Integer} = Rational{T}(zero(T), one(T); reduce=false)
typemax(::Type{Rational{T}}) where {T<:Integer} = Rational{T}(one(T), zero(T); reduce=false)
isinteger(x::Rational{T}) where {T<Integer} = x.den === one(T)
+(x::Rational{T}) where {T<Integer} = Rational{T}(+x.num, x.den; reduce=false)
-(x::Rational{T}) where {T<Integer} = Rational{T}(-x.num, x.den; reduce=false)
function -(x::Rational{T}) where T<:BitSigned
x.num == typemin(T) && __throw_rational_numerator_typemin(T)
Rational{T}(-x.num, x.den; reduce=false)
end
@noinline __throw_rational_numerator_typemin(T) = throw(OverflowError("rational numerator is typemin($T)"))
function -(x::Rational{T}) where T<:Unsigned
x.num != zero(T) && __throw_negate_unsigned()
x
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment