Skip to content

Commit

Permalink
Improve support for nonstandard ranges.
Browse files Browse the repository at this point in the history
Make it so that an Integer subtype, say Position <: Integer, for which a difference is of a
different type, say Displacement <: Integer, is properly handled in UnitRange, OneTo, and StepRange.
  • Loading branch information
tkoolen committed May 29, 2018
1 parent 8e6cfff commit a573b17
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 3 deletions.
6 changes: 3 additions & 3 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ julia> step(range(2.5, stop=10.9, length=85))
```
"""
step(r::StepRange) = r.step
step(r::AbstractUnitRange{T}) where{T} = oneunit(T)
step(r::AbstractUnitRange{T}) where{T} = oneunit(T) - zero(T)
step(r::StepRangeLen{T}) where {T} = T(r.step)
step(r::LinRange) = (last(r)-first(r))/r.lendiv

Expand All @@ -388,8 +388,8 @@ function unsafe_length(r::StepRange)
isempty(r) ? zero(n) : n
end
length(r::StepRange) = unsafe_length(r)
unsafe_length(r::AbstractUnitRange) = Integer(last(r) - first(r) + 1)
unsafe_length(r::OneTo) = r.stop
unsafe_length(r::AbstractUnitRange) = Integer(last(r) - first(r) + step(r))
unsafe_length(r::OneTo) = Integer(r.stop - zero(r.stop))
length(r::AbstractUnitRange) = unsafe_length(r)
length(r::OneTo) = unsafe_length(r)
length(r::StepRangeLen) = r.len
Expand Down
59 changes: 59 additions & 0 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1276,3 +1276,62 @@ end
@test step(x) == 0.0
@test x isa StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}
end

module NonStandardIntegerRangeTest

using Test

struct Position <: Integer
val::Int
end
Position(x::Position) = x # to resolve ambiguity with boot.jl:728

struct Displacement <: Integer
val::Int
end
Displacement(x::Displacement) = x # to resolve ambiguity with boot.jl:728

Base.:-(x::Displacement) = Displacement(-x.val)
Base.:-(x::Position, y::Position) = Displacement(x.val - y.val)
Base.:-(x::Position, y::Displacement) = Position(x.val - y.val)
Base.:-(x::Displacement, y::Displacement) = Displacement(x.val - y.val)
Base.:+(x::Position, y::Displacement) = Position(x.val + y.val)
Base.:+(x::Displacement, y::Displacement) = Displacement(x.val + y.val)
Base.:(<=)(x::Position, y::Position) = x.val <= y.val
Base.:(<)(x::Position, y::Position) = x.val < y.val
Base.:(<)(x::Displacement, y::Displacement) = x.val < y.val

# for StepRange computation:
Base.Unsigned(x::Displacement) = Unsigned(x.val)
Base.rem(x::Displacement, y::Displacement) = Displacement(rem(x.val, y.val))
Base.div(x::Displacement, y::Displacement) = Displacement(div(x.val, y.val))

# required for collect (summing lengths); alternatively, should unsafe_length return Int by default?
Base.promote_rule(::Type{Displacement}, ::Type{Int}) = Int
Base.convert(::Type{Int}, x::Displacement) = x.val

@testset "Ranges with nonstandard Integers" begin
for (start, stop) in [(2, 4), (3, 3), (3, -2)]
@test collect(Position(start) : Position(stop)) == Position.(start : stop)
end

for start in [3, 0, -2]
@test collect(Base.OneTo(Position(start))) == Position.(Base.OneTo(start))
end

for step in [1, 2, 3]
for start in [-1, 0, 2]
for stop in [start, start - 1, start + 2 * step, start + 2 * step + 1]
r1 = StepRange(Position(start), Displacement(step), Position(stop))
@test collect(r1) == Position.(start : step : stop)

@test_broken begin
r2 = Position(start) : Displacement(step) : Position(stop)
r1 === r2
end
end
end
end
end

end # module NonStandardIntegerRangeTest

0 comments on commit a573b17

Please sign in to comment.