Last active
May 1, 2020 09:53
-
-
Save JeffreySarnoff/2d47f8922d7cd63be80b74f1095a3481 to your computer and use it in GitHub Desktop.
revising rational.jl (first part of the file)`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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