Skip to content

Commit

Permalink
promote ranges to the largest of the the start, step, or length (as a…
Browse files Browse the repository at this point in the history
…pplicable)

Fixes #35711
Fixes #10554
  • Loading branch information
vtjnash committed Nov 12, 2021
1 parent 5e2894b commit f59082e
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 65 deletions.
76 changes: 43 additions & 33 deletions base/range.jl
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

(:)(a::Real, b::Real) = (:)(promote(a,b)...)
(:)(a::Real, b::Real) = (:)(promote(a, b)...)

(:)(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop)

(:)(start::T, stop::T) where {T} = (:)(start, oftype(stop-start, 1), stop)
(:)(start::T, stop::T) where {T} = (:)(start, oneunit(stop >= start ? stop - start : start - stop), stop)

# promote start and stop, leaving step alone
(:)(start::A, step, stop::C) where {A<:Real,C<:Real} =
(:)(convert(promote_type(A,C),start), step, convert(promote_type(A,C),stop))
(:)(start::A, step, stop::C) where {A<:Real, C<:Real} =
(:)(convert(promote_type(A, C), start), step, convert(promote_type(A, C), stop))

# AbstractFloat specializations
(:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, T(1), b)
(:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, oneunit(T), b)

(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a,b,c)...)
(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...)
(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...)
(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a, b, c)...)
(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...)
(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...)

(:)(start::T, step::T, stop::T) where {T<:AbstractFloat} =
_colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop)
Expand Down Expand Up @@ -168,15 +168,15 @@ range_stop(stop) = range_start_stop(oneunit(stop), stop)
range_stop(stop::Integer) = range_length(stop)

# Stop and length as the only argument
range_stop_length(a::Real, len::Integer) = UnitRange{typeof(a)}(oftype(a, a-len+1), a)
range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oftype(a, 1), a, len)
range_stop_length(a, len::Integer) = range_step_stop_length(oftype(a-a, 1), a, len)
range_stop_length(a::Real, len::Integer) = UnitRange(promote(a - (len - oneunit(len)), a)...)
range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oneunit(a), a, len)
range_stop_length(a, len::Integer) = range_step_stop_length(oneunit(a - a), a, len)

range_step_stop_length(step, stop, length) = reverse(range_start_step_length(stop, -step, length))

range_start_length(a::Real, len::Integer) = UnitRange{typeof(a)}(a, oftype(a, a+len-1))
range_start_length(a::AbstractFloat, len::Integer) = range_start_step_length(a, oftype(a, 1), len)
range_start_length(a, len::Integer) = range_start_step_length(a, oftype(a-a, 1), len)
range_start_length(a::Real, len::Integer) = UnitRange(promote(a, a + (len - oneunit(len)))...)
range_start_length(a::AbstractFloat, len::Integer) = range_start_step_length(a, oneunit(a), len)
range_start_length(a, len::Integer) = range_start_step_length(a, oneunit(a - a), len)

range_start_stop(start, stop) = start:stop

Expand All @@ -200,10 +200,15 @@ function range_start_step_length(a::T, step, len::Integer) where {T}
_rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len)
end

_rangestyle(::Ordered, ::ArithmeticWraps, a::T, step::S, len::Integer) where {T,S} =
StepRange{typeof(a+zero(step)),S}(a, step, a+step*(len-1))
_rangestyle(::Any, ::Any, a::T, step::S, len::Integer) where {T,S} =
StepRangeLen{typeof(a+zero(step)),T,S}(a, step, len)
function _rangestyle(::Ordered, ::ArithmeticWraps, a, step, len::Integer)
stop = a + step * (len - oneunit(len))
T = typeof(stop)
return StepRange{T,typeof(step)}(convert(T, a), step, stop)
end
function _rangestyle(::Any, ::Any, a::T, step, len::Integer) where {T}
stop = a + step * len - step
return StepRangeLen{typeof(stop),T,typeof(step)}(a, step, len)
end

range_start_step_stop(start, step, stop) = start:step:stop

Expand Down Expand Up @@ -397,7 +402,7 @@ unitrange_last(start::T, stop::T) where {T<:Integer} =
stop >= start ? stop : convert(T,start-oneunit(start-stop))
unitrange_last(start::T, stop::T) where {T} =
stop >= start ? convert(T,start+floor(stop-start)) :
convert(T,start-oneunit(stop-start))
convert(T,start-oneunit(start-stop))

unitrange(x) = UnitRange(x)

Expand Down Expand Up @@ -556,7 +561,7 @@ function LinRange{T}(start, stop, len::Integer) where T
end

function LinRange(start, stop, len::Integer)
T = typeof((stop-start)/len)
T = typeof((len >= 0 ? stop - start : start - stop) / oneunit(len))
LinRange{T}(start, stop, len)
end

Expand Down Expand Up @@ -962,29 +967,32 @@ function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integ
@boundscheck checkbounds(r, s)

if T === Bool
range(first(s) ? first(r) : last(r), length = Integer(last(s)))
return range(first(s) ? first(r) : last(r), length = last(s))
else
f = first(r)
st = oftype(f, f + first(s)-firstindex(r))
return range(st, length=length(s))
start = oftype(f, f + first(s)-firstindex(r))
stop = oftype(f, start + (length(s) - 1))
return range(start, stop)
end
end

function getindex(r::OneTo{T}, s::OneTo) where T
@inline
@boundscheck checkbounds(r, s)
OneTo(T(s.stop))
return OneTo(T(s.stop))
end

function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer}
@inline
@boundscheck checkbounds(r, s)

if T === Bool
range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Integer(last(s)))
return range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = last(s))
else
st = oftype(first(r), first(r) + s.start-firstindex(r))
return range(st, step=step(s), length=length(s))
f = first(r)
start = oftype(f, f + s.start-firstindex(r))
stop = oftype(f, start + (length(s) - 1) * step(s))
return range(start, stop; step=step(s))
end
end

Expand All @@ -994,19 +1002,21 @@ function getindex(r::StepRange, s::AbstractRange{T}) where {T<:Integer}

if T === Bool
if length(s) == 0
return range(first(r), step=step(r), length=0)
start, len = first(r), 0
elseif length(s) == 1
if first(s)
return range(first(r), step=step(r), length=1)
start, len = first(r), 1
else
return range(first(r), step=step(r), length=0)
start, len = first(r), 0
end
else # length(s) == 2
return range(last(r), step=step(r), length=1)
start, len = last(r), 1
end
return range(start, step=step(r); length=len)
else
st = oftype(r.start, r.start + (first(s)-1)*step(r))
return range(st, step=step(r)*step(s), length=length(s))
f = r.start
start = oftype(f, f + (first(s)-1)*step(r))
return range(start; step=step(r)*step(s), length=length(s))
end
end

Expand Down
89 changes: 57 additions & 32 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1894,41 +1894,50 @@ end
end

@testset "eltype of range(::Integer; step::Rational, length) (#37295)" begin
@test range(1, step=1//2, length=3) == [1//1, 3//2, 2//1]
@test eltype(range(1, step=1//2, length=3)) === Rational{Int}
@test typeof(step(range(1, step=1//2, length=3))) === Rational{Int}

@test range(1//1, step=2, length=3) == [1, 3, 5]
@test eltype(range(1//1, step=2, length=3)) === Rational{Int}
@test typeof(step(range(1//1, step=2, length=3))) === Int

@test range(Int16(1), step=Rational{Int8}(1,2), length=3) == [1//1, 3//2, 2//1]
@test eltype(range(Int16(1), step=Rational{Int8}(1,2), length=3)) === Rational{Int16}
@test typeof(step(range(Int16(1), step=Rational{Int8}(1,2), length=3))) === Rational{Int8}

@test range(Rational{Int8}(1), step=Int16(2), length=3) == [1, 3, 5]
@test eltype(range(Rational{Int8}(1), step=Int16(2), length=3)) === Rational{Int16}
@test typeof(step(range(Rational{Int8}(1), step=Int16(2), length=3))) === Int16

@test range('a', step=2, length=3) == ['a', 'c', 'e']
@test eltype(range('a', step=2, length=3)) === Char
@test typeof(step(range('a', step=2, length=3))) === Int

@test isempty(range(typemax(Int)//1, step=1, length=0))
@test eltype(range(typemax(Int)//1, step=1, length=0)) === Rational{Int}
@test typeof(step(range(typemax(Int)//1, step=1, length=0))) === Int
r = range(1, step=1//2, length=3)
@test r == [1//1, 3//2, 2//1]
@test eltype(r) === Rational{Int}
@test typeof(step(r)) === Rational{Int}

r = range(1//1, step=2, length=3)
@test r == [1, 3, 5]
@test eltype(r) === Rational{Int}
@test typeof(step(r)) === Int

r = range(Int16(1), step=Rational{Int8}(1,2), length=Int16(3))
@test r == [1//1, 3//2, 2//1]
@test eltype(r) === Rational{Int16}
@test typeof(step(r)) === Rational{Int8}

r = range(Rational{Int8}(1), step=Int16(2), length=Int8(3))
@test r == [1, 3, 5]
@test eltype(r) === Rational{Int16}
@test typeof(step(r)) === Int16

r = range('a', step=2, length=3)
@test r == ['a', 'c', 'e']
@test eltype(r) === Char
@test typeof(step(r)) === Int

r = range(typemax(Int)//1, step=1, length=0)
@test isempty(r)
@test eltype(r) === Rational{Int}
@test typeof(step(r)) === Int

@test isempty(range(typemin(Int), step=-1//1, length=0))
@test eltype(range(typemin(Int), step=-1//1, length=0)) === Rational{Int}
@test typeof(step(range(typemin(Int), step=-1//1, length=0))) === Rational{Int}
r = range(typemin(Int), step=-1//1, length=0)
@test isempty(r)
@test eltype(r) === Rational{Int}
@test typeof(step(r)) === Rational{Int}

@test StepRangeLen(Int8(1), Int8(2), 3) == Int8[1, 3, 5]
@test eltype(StepRangeLen(Int8(1), Int8(2), 3)) === Int8
@test typeof(step(StepRangeLen(Int8(1), Int8(2), 3))) === Int8
r = StepRangeLen(Int8(1), Int8(2), 3)
@test r == Int8[1, 3, 5]
@test eltype(r) === Int8
@test typeof(step(r)) === Int8

@test StepRangeLen(Int8(1), Int8(2), 3, 2) == Int8[-1, 1, 3]
@test eltype(StepRangeLen(Int8(1), Int8(2), 3, 2)) === Int8
@test typeof(step(StepRangeLen(Int8(1), Int8(2), 3, 2))) === Int8
r = StepRangeLen(Int8(1), Int8(2), 3, 2)
@test r == Int8[-1, 1, 3]
@test eltype(r) === Int8
@test typeof(step(r)) === Int8
end

@testset "LinRange eltype for element types that wrap integers" begin
Expand Down Expand Up @@ -2226,3 +2235,19 @@ let r = Ptr{Cvoid}(20):-UInt(2):Ptr{Cvoid}(10)
@test step(r) === -UInt(2)
@test last(r) === Ptr{Cvoid}(10)
end

# test behavior of wrap-around and promotion of empty ranges (#35711)
@test length(range(0, length=UInt(0))) === UInt(0)
@test !isempty(range(0, length=UInt(0)))
@test length(range(typemax(Int), length=UInt(0))) === UInt(0)
@test isempty(range(typemax(Int), length=UInt(0)))
@test length(range(0, length=UInt(0), step=UInt(2))) == typemin(Int) % UInt
@test !isempty(range(0, length=UInt(0), step=UInt(2)))
@test length(range(typemax(Int), length=UInt(0), step=UInt(2))) === UInt(0)
@test isempty(range(typemax(Int), length=UInt(0), step=UInt(2)))
@test length(range(typemax(Int), length=UInt(0), step=2)) === UInt(0)
@test isempty(range(typemax(Int), length=UInt(0), step=2))
@test length(range(typemax(Int), length=0, step=UInt(2))) === UInt(0)
@test isempty(range(typemax(Int), length=0, step=UInt(2)))

@test length(range(1, length=typemax(Int128))) === typemax(Int128)

0 comments on commit f59082e

Please sign in to comment.