|
| 1 | +""" |
| 2 | + FixedRational{T,den} |
| 3 | +
|
| 4 | +A rational number with a fixed denominator. Significantly |
| 5 | +faster than `Rational{T}`, as it never needs to compute |
| 6 | +the `gcd` apart from when printing. |
| 7 | +""" |
| 8 | +struct FixedRational{T<:Integer,den} <: Real |
| 9 | + num::T |
| 10 | + global unsafe_fixed_rational(num::Integer, ::Type{T}, ::Val{den}) where {T,den} = new{T,den}(num) |
| 11 | +end |
| 12 | + |
| 13 | +""" |
| 14 | + denom(F::FixedRational) |
| 15 | +
|
| 16 | +Since `den` can be a different type than `T`, this function |
| 17 | +is used to get the denominator as a `T`. |
| 18 | +""" |
| 19 | +denom(::Type{F}) where {T,den,F<:FixedRational{T,den}} = convert(T, den) |
| 20 | + |
| 21 | +# But, for Val(den), we need to use the same type as at init. |
| 22 | +# Otherwise, we would have type instability. |
| 23 | +val_denom(::Type{F}) where {T,den,F<:FixedRational{T,den}} = Val(den) |
| 24 | + |
| 25 | +Base.eltype(::Type{F}) where {T,den,F<:FixedRational{T,den}} = T |
| 26 | + |
| 27 | +(::Type{F})(x::Integer) where {F<:FixedRational} = unsafe_fixed_rational(x * denom(F), eltype(F), val_denom(F)) |
| 28 | +(::Type{F})(x::Rational) where {F<:FixedRational} = unsafe_fixed_rational(widemul(x.num, denom(F)) ÷ x.den, eltype(F), val_denom(F)) |
| 29 | + |
| 30 | +Base.:*(l::F, r::F) where {F<:FixedRational} = unsafe_fixed_rational(widemul(l.num, r.num) ÷ denom(F), eltype(F), val_denom(F)) |
| 31 | +Base.:+(l::F, r::F) where {F<:FixedRational} = unsafe_fixed_rational(l.num + r.num, eltype(F), val_denom(F)) |
| 32 | +Base.:-(l::F, r::F) where {F<:FixedRational} = unsafe_fixed_rational(l.num - r.num, eltype(F), val_denom(F)) |
| 33 | +Base.:-(x::F) where {F<:FixedRational} = unsafe_fixed_rational(-x.num, eltype(F), val_denom(F)) |
| 34 | +Base.inv(x::F) where {F<:FixedRational} = unsafe_fixed_rational(widemul(denom(F), denom(F)) ÷ x.num, eltype(F), val_denom(F)) |
| 35 | + |
| 36 | +Base.:(==)(x::F, y::F) where {F<:FixedRational} = x.num == y.num |
| 37 | +Base.iszero(x::FixedRational) = iszero(x.num) |
| 38 | +Base.isinteger(x::F) where {F<:FixedRational} = iszero(x.num % denom(F)) |
| 39 | +Base.convert(::Type{F}, x::Integer) where {F<:FixedRational} = unsafe_fixed_rational(x * denom(F), eltype(F), val_denom(F)) |
| 40 | +Base.convert(::Type{F}, x::Rational) where {F<:FixedRational} = F(x) |
| 41 | +Base.convert(::Type{Rational}, x::F) where {F<:FixedRational} = Rational{eltype(F)}(x.num, denom(F)) |
| 42 | +Base.convert(::Type{AF}, x::F) where {AF<:AbstractFloat,F<:FixedRational} = convert(AF, x.num) / convert(AF, denom(F)) |
| 43 | +Base.round(::Type{T}, x::F) where {T,F<:FixedRational} = div(convert(T, x.num), convert(T, denom(F)), RoundNearest) |
| 44 | +Base.promote(x, y::F) where {F<:FixedRational} = promote(x, convert(Rational, y)) |
| 45 | +Base.promote(x::F, y) where {F<:FixedRational} = promote(convert(Rational, x), y) |
| 46 | +Base.show(io::IO, x::F) where {F<:FixedRational} = show(io, convert(Rational, x)) |
| 47 | +Base.zero(::Type{F}) where {F<:FixedRational} = unsafe_fixed_rational(0, eltype(F), val_denom(F)) |
| 48 | + |
| 49 | +tryrationalize(::Type{F}, x::F) where {F<:FixedRational} = x |
| 50 | +tryrationalize(::Type{F}, x::Union{Rational,Integer}) where {F<:FixedRational} = convert(F, x) |
| 51 | +tryrationalize(::Type{F}, x) where {F<:FixedRational} = unsafe_fixed_rational(round(eltype(F), x * denom(F)), eltype(F), val_denom(F)) |
0 commit comments