diff --git a/base/broadcast.jl b/base/broadcast.jl index c6651e28489a3..b34a73041708b 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -1121,19 +1121,20 @@ end ## scalar-range broadcast operations ## # DefaultArrayStyle and \ are not available at the time of range.jl -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange) = r +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), -last(r), step=-step(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange) = LinRange(-r.start, -r.stop, length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, length=length(r)) # For #18336 we need to prevent promotion of the step type: broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange, x::Number) = range(first(r) + x, step=step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::AbstractRange) = range(x + first(r), step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange, x::Real) = range(first(r) + x, last(r) + x, step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::Real) = range(x + first(r), x + last(r), step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, last(r) + x) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), x + last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)+x)}(r.ref + x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::StepRangeLen{T}) where T = @@ -1142,9 +1143,11 @@ broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange, x::Number) = LinRa broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::LinRange) = LinRange(x + r.start, x + r.stop, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r1::AbstractRange, r2::AbstractRange) = r1 + r2 -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Number) = range(first(r)-x, length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r)-x, step=step(r), length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r) - x, step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x - first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange, x::Real) = range(first(r) - x, last(r) - x, step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Real, r::OrdinalRange) = range(x - first(r), x - last(r), step=-step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Real) = range(first(r) - x, last(r) - x) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)-x)}(r.ref - x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::StepRangeLen{T}) where T = diff --git a/base/range.jl b/base/range.jl index ee3c86c3f103f..b6a8e333cf52e 100644 --- a/base/range.jl +++ b/base/range.jl @@ -24,9 +24,9 @@ _colon(::Ordered, ::Any, start::T, step, stop::T) where {T} = StepRange(start, step, stop) # for T<:Union{Float16,Float32,Float64} see twiceprecision.jl _colon(::Ordered, ::ArithmeticRounds, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) + StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) _colon(::Any, ::Any, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) + StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) """ (:)(start, [step], stop) @@ -415,10 +415,11 @@ oneto(r) = OneTo(r) ## Step ranges parameterized by length """ - StepRangeLen{T,R,S}(ref::R, step::S, len, [offset=1]) where {T,R,S} - StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} + StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} + StepRangeLen{T,R,S}( ref::R, step::S, len, [offset=1]) where {T,R,S} + StepRangeLen{T,R,S,L}(ref::R, step::S, len, [offset=1]) where {T,R,S,L} -A range `r` where `r[i]` produces values of type `T` (in the second +A range `r` where `r[i]` produces values of type `T` (in the first form, `T` is deduced automatically), parameterized by a `ref`erence value, a `step`, and the `len`gth. By default `ref` is the starting value `r[1]`, but alternatively you can supply it as the value of @@ -426,40 +427,45 @@ value `r[1]`, but alternatively you can supply it as the value of with `TwicePrecision` this can be used to implement ranges that are free of roundoff error. """ -struct StepRangeLen{T,R,S} <: AbstractRange{T} +struct StepRangeLen{T,R,S,L<:Integer} <: AbstractRange{T} ref::R # reference value (might be smallest-magnitude value in the range) step::S # step value - len::Int # length of the range - offset::Int # the index of ref + len::L # length of the range + offset::L # the index of ref - function StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} + function StepRangeLen{T,R,S,L}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S,L} if T <: Integer && !isinteger(ref + step) throw(ArgumentError("StepRangeLen{<:Integer} cannot have non-integer step")) end - len >= 0 || throw(ArgumentError("length cannot be negative, got $len")) - 1 <= offset <= max(1,len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) - new(ref, step, len, offset) + len = convert(L, len) + len >= zero(len) || throw(ArgumentError("length cannot be negative, got $len")) + offset = convert(L, offset) + L1 = oneunit(typeof(len)) + L1 <= offset <= max(L1, len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) + return new(ref, step, len, offset) end end +StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = + StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = - StepRangeLen{typeof(ref+zero(step)),R,S}(ref, step, len, offset) + StepRangeLen{typeof(ref+zero(step)),R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = - StepRangeLen{T,R,S}(ref, step, len, offset) + StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) ## range with computed step """ - LinRange{T} + LinRange{T,L} A range with `len` linearly spaced elements between its `start` and `stop`. The size of the spacing is controlled by `len`, which must -be an `Int`. +be an `Integer`. # Examples ```jldoctest julia> LinRange(1.5, 5.5, 9) -9-element LinRange{Float64}: +9-element LinRange{Float64, Int64}: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5 ``` @@ -483,26 +489,35 @@ julia> collect(LinRange(-0.1, 0.3, 5)) 0.3 ``` """ -struct LinRange{T} <: AbstractRange{T} +struct LinRange{T,L<:Integer} <: AbstractRange{T} start::T stop::T - len::Int - lendiv::Int + len::L + lendiv::L - function LinRange{T}(start,stop,len) where T + function LinRange{T,L}(start::T, stop::T, len::L) where {T,L<:Integer} len >= 0 || throw(ArgumentError("range($start, stop=$stop, length=$len): negative length")) - if len == 1 + onelen = oneunit(typeof(len)) + if len == onelen start == stop || throw(ArgumentError("range($start, stop=$stop, length=$len): endpoints differ")) - return new(start, stop, 1, 1) + return new(start, stop, len, len) end - lendiv = max(len-1, 1) + lendiv = max(len - onelen, onelen) if T <: Integer && !iszero(mod(stop-start, lendiv)) throw(ArgumentError("LinRange{<:Integer} cannot have non-integer step")) end - new(start,stop,len,lendiv) + return new(start, stop, len, lendiv) end end +function LinRange{T,L}(start, stop, len::Integer) where {T,L} + LinRange{T,L}(convert(T, start), convert(T, stop), convert(L, len)) +end + +function LinRange{T}(start, stop, len::Integer) where T + LinRange{T,promote_type(Int,typeof(len))}(start, stop, len) +end + function LinRange(start, stop, len::Integer) T = typeof((stop-start)/len) LinRange{T}(start, stop, len) @@ -621,6 +636,7 @@ step(r::StepRangeLen) = r.step step(r::StepRangeLen{T}) where {T<:AbstractFloat} = T(r.step) step(r::LinRange) = (last(r)-first(r))/r.lendiv +# high-precision step step_hp(r::StepRangeLen) = r.step step_hp(r::AbstractRange) = step(r) @@ -648,7 +664,7 @@ function checked_length(r::OrdinalRange{T}) where T diff = checked_sub(stop, start) end a = Integer(div(diff, s)) - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end function checked_length(r::AbstractUnitRange{T}) where T @@ -657,7 +673,7 @@ function checked_length(r::AbstractUnitRange{T}) where T return Integer(first(r) - first(r)) end a = Integer(checked_add(checked_sub(last(r), first(r)))) - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end function length(r::OrdinalRange{T}) where T @@ -675,14 +691,14 @@ function length(r::OrdinalRange{T}) where T diff = stop - start end a = Integer(div(diff, s)) - return a + one(a) + return a + oneunit(a) end function length(r::AbstractUnitRange{T}) where T @_inline_meta a = Integer(last(r) - first(r)) # even when isempty, by construction (with overflow) - return a + one(a) + return a + oneunit(a) end length(r::OneTo) = Integer(r.stop - zero(r.stop)) @@ -710,7 +726,7 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} else a = div(unsigned(diff), s) % typeof(diff) end - return Integer(a) + one(a) + return Integer(a) + oneunit(a) end function checked_length(r::OrdinalRange{T}) where T<:bigints s = step(r) @@ -729,7 +745,7 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} else a = div(checked_sub(start, stop), -s) end - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end end @@ -803,10 +819,11 @@ copy(r::AbstractRange) = r ## iteration -function iterate(r::Union{LinRange,StepRangeLen}, i::Int=1) +function iterate(r::Union{StepRangeLen,LinRange}, i::Integer=zero(length(r))) @_inline_meta + i += oneunit(i) length(r) < i && return nothing - unsafe_getindex(r, i), i + 1 + unsafe_getindex(r, i), i end iterate(r::OrdinalRange) = isempty(r) ? nothing : (first(r), first(r)) @@ -897,7 +914,7 @@ 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 = Int(last(s))) + range(first(s) ? first(r) : last(r), length = Integer(last(s))) else f = first(r) st = oftype(f, f + first(s)-firstindex(r)) @@ -916,7 +933,7 @@ function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer} @boundscheck checkbounds(r, s) if T === Bool - range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Int(last(s))) + range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Integer(last(s))) else st = oftype(first(r), first(r) + s.start-firstindex(r)) return range(st, step=step(s), length=length(s)) @@ -949,24 +966,29 @@ function getindex(r::StepRangeLen{T}, s::OrdinalRange{S}) where {T, S<:Integer} @_inline_meta @boundscheck checkbounds(r, s) + len = length(s) + sstep = step_hp(s) + rstep = step_hp(r) + L = typeof(len) if S === Bool - if length(s) == 0 - return StepRangeLen{T}(first(r), step(r), 0, 1) - elseif length(s) == 1 + rstep *= one(sstep) + if len == 0 + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) + elseif len == 1 if first(s) - return StepRangeLen{T}(first(r), step(r), 1, 1) + return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else - return StepRangeLen{T}(first(r), step(r), 0, 1) + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end - else # length(s) == 2 - return StepRangeLen{T}(last(r), step(r), 1, 1) + else # len == 2 + return StepRangeLen{T}(last(r), rstep, oneunit(L), oneunit(L)) end else # Find closest approach to offset by s ind = LinearIndices(s) - offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) - ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) - return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) + offset = L(max(min(1 + round(L, (r.offset - first(s))/sstep), last(ind)), first(ind))) + ref = _getindex_hiprec(r, first(s) + (offset-1)*sstep) + return StepRangeLen{T}(ref, rstep*sstep, len, offset) end end @@ -974,22 +996,24 @@ function getindex(r::LinRange{T}, s::OrdinalRange{S}) where {T, S<:Integer} @_inline_meta @boundscheck checkbounds(r, s) + len = length(s) + L = typeof(len) if S === Bool - if length(s) == 0 - return LinRange(first(r), first(r), 0) - elseif length(s) == 1 + if len == 0 + return LinRange{T}(first(r), first(r), len) + elseif len == 1 if first(s) - return LinRange(first(r), first(r), 1) + return LinRange{T}(first(r), first(r), len) else - return LinRange(first(r), first(r), 0) + return LinRange{T}(first(r), first(r), zero(L)) end else # length(s) == 2 - return LinRange(last(r), last(r), 1) + return LinRange{T}(last(r), last(r), oneunit(L)) end else vfirst = unsafe_getindex(r, first(s)) vlast = unsafe_getindex(r, last(s)) - return LinRange{T}(vfirst, vlast, length(s)) + return LinRange{T}(vfirst, vlast, len) end end @@ -1158,8 +1182,8 @@ issubset(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = ## linear operations on ranges ## -(r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) --(r::StepRangeLen{T,R,S}) where {T,R,S} = - StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset) +-(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(-r.ref, -r.step, r.len, r.offset) function -(r::LinRange) start = -r.start LinRange{typeof(start)}(start, -r.stop, length(r)) @@ -1173,12 +1197,12 @@ el_same(::Type{T}, a::Type{<:AbstractArray{S,n}}, b::Type{<:AbstractArray{T,n}}) el_same(::Type, a, b) = promote_typejoin(a, b) promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = - el_same(promote_type(T1,T2), a, b) + el_same(promote_type(T1, T2), a, b) UnitRange{T}(r::UnitRange{T}) where {T<:Real} = r UnitRange{T}(r::UnitRange) where {T<:Real} = UnitRange{T}(r.start, r.stop) promote_rule(a::Type{OneTo{T1}}, b::Type{OneTo{T2}}) where {T1,T2} = - el_same(promote_type(T1,T2), a, b) + el_same(promote_type(T1, T2), a, b) OneTo{T}(r::OneTo{T}) where {T<:Integer} = r OneTo{T}(r::OneTo) where {T<:Integer} = OneTo{T}(r.stop) @@ -1196,11 +1220,11 @@ OrdinalRange{T1, T2}(r::AbstractUnitRange{T1}) where {T1, T2<:Integer} = r OrdinalRange{T1, T2}(r::UnitRange) where {T1, T2<:Integer} = UnitRange{T1}(r) OrdinalRange{T1, T2}(r::OneTo) where {T1, T2<:Integer} = OneTo{T1}(r) -promote_rule(::Type{StepRange{T1a,T1b}}, ::Type{StepRange{T2a,T2b}}) where {T1a,T1b,T2a,T2b} = - el_same(promote_type(T1a,T2a), - # el_same only operates on array element type, so just promote second type parameter - StepRange{T1a, promote_type(T1b,T2b)}, - StepRange{T2a, promote_type(T1b,T2b)}) +function promote_rule(::Type{StepRange{T1a,T1b}}, ::Type{StepRange{T2a,T2b}}) where {T1a,T1b,T2a,T2b} + Tb = promote_type(T1b, T2b) + # el_same only operates on array element type, so just promote second type parameter + el_same(promote_type(T1a, T2a), StepRange{T1a,Tb}, StepRange{T2a,Tb}) +end StepRange{T1,T2}(r::StepRange{T1,T2}) where {T1,T2} = r promote_rule(a::Type{StepRange{T1a,T1b}}, ::Type{UR}) where {T1a,T1b,UR<:AbstractUnitRange} = @@ -1211,35 +1235,38 @@ StepRange(r::AbstractUnitRange{T}) where {T} = StepRange{T,T}(first(r), step(r), last(r)) (StepRange{T1,T2} where T1)(r::AbstractRange) where {T2} = StepRange{eltype(r),T2}(r) -promote_rule(::Type{StepRangeLen{T1,R1,S1}},::Type{StepRangeLen{T2,R2,S2}}) where {T1,T2,R1,R2,S1,S2} = - el_same(promote_type(T1,T2), - StepRangeLen{T1,promote_type(R1,R2),promote_type(S1,S2)}, - StepRangeLen{T2,promote_type(R1,R2),promote_type(S1,S2)}) -StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T,R,S} = r -StepRangeLen{T,R,S}(r::StepRangeLen) where {T,R,S} = - StepRangeLen{T,R,S}(convert(R, r.ref), convert(S, r.step), length(r), r.offset) +function promote_rule(::Type{StepRangeLen{T1,R1,S1,L1}},::Type{StepRangeLen{T2,R2,S2,L2}}) where {T1,T2,R1,R2,S1,S2,L1,L2} + R, S, L = promote_type(R1, R2), promote_type(S1, S2), promote_type(L1, L2) + el_same(promote_type(T1, T2), StepRangeLen{T1,R,S,L}, StepRangeLen{T2,R,S,L}) +end +StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = r +StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(convert(R, r.ref), convert(S, r.step), convert(L, r.len), convert(L, r.offset)) StepRangeLen{T}(r::StepRangeLen) where {T} = - StepRangeLen(convert(T, r.ref), convert(T, r.step), length(r), r.offset) + StepRangeLen(convert(T, r.ref), convert(T, r.step), r.len, r.offset) -promote_rule(a::Type{StepRangeLen{T,R,S}}, ::Type{OR}) where {T,R,S,OR<:AbstractRange} = - promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR)}) -StepRangeLen{T,R,S}(r::AbstractRange) where {T,R,S} = - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +promote_rule(a::Type{StepRangeLen{T,R,S,L}}, ::Type{OR}) where {T,R,S,L,OR<:AbstractRange} = + promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR), Int}) +StepRangeLen{T,R,S,L}(r::AbstractRange) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), length(r)) StepRangeLen{T}(r::AbstractRange) where {T} = StepRangeLen(T(first(r)), T(step(r)), length(r)) StepRangeLen(r::AbstractRange) = StepRangeLen{eltype(r)}(r) -promote_rule(a::Type{LinRange{T1}}, b::Type{LinRange{T2}}) where {T1,T2} = - el_same(promote_type(T1,T2), a, b) -LinRange{T}(r::LinRange{T}) where {T} = r -LinRange{T}(r::AbstractRange) where {T} = LinRange{T}(first(r), last(r), length(r)) +function promote_rule(a::Type{LinRange{T1,L1}}, b::Type{LinRange{T2,L2}}) where {T1,T2,L1,L2} + L = promote_type(L1, L2) + el_same(promote_type(T1, T2), LinRange{T1,L}, LinRange{T2,L}) +end +LinRange{T,L}(r::LinRange{T,L}) where {T,L} = r +LinRange{T,L}(r::AbstractRange) where {T,L} = LinRange{T,L}(first(r), last(r), length(r)) +LinRange{T}(r::AbstractRange) where {T} = LinRange{T,typeof(length(r))}(first(r), last(r), length(r)) LinRange(r::AbstractRange{T}) where {T} = LinRange{T}(r) -promote_rule(a::Type{LinRange{T}}, ::Type{OR}) where {T,OR<:OrdinalRange} = - promote_rule(a, LinRange{eltype(OR)}) +promote_rule(a::Type{LinRange{T,L}}, ::Type{OR}) where {T,L,OR<:OrdinalRange} = + promote_rule(a, LinRange{eltype(OR),L}) -promote_rule(::Type{LinRange{L}}, b::Type{StepRangeLen{T,R,S}}) where {L,T,R,S} = - promote_rule(StepRangeLen{L,L,L}, b) +promote_rule(::Type{LinRange{A,L}}, b::Type{StepRangeLen{T2,R2,S2,L2}}) where {A,L,T2,R2,S2,L2} = + promote_rule(StepRangeLen{A,A,A,L}, b) ## concatenation ## @@ -1266,9 +1293,9 @@ function _reverse(r::StepRangeLen, ::Colon) # invalid. As `reverse(r)` is also empty, any offset would work so we keep # `r.offset` offset = isempty(r) ? r.offset : length(r)-r.offset+1 - StepRangeLen(r.ref, -r.step, length(r), offset) + return typeof(r)(r.ref, -r.step, length(r), offset) end -_reverse(r::LinRange{T}, ::Colon) where {T} = LinRange{T}(r.stop, r.start, length(r)) +_reverse(r::LinRange{T}, ::Colon) where {T} = typeof(r)(r.stop, r.start, length(r)) ## sorting ## diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 55cdc59371674..a7ff116b56b36 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -194,6 +194,10 @@ function TwicePrecision{T}(x) where {T} TwicePrecision{T}(xT, T(Δx)) end +function TwicePrecision{T}(x::TwicePrecision) where {T} + TwicePrecision{T}(x.hi, x.lo) +end + TwicePrecision{T}(i::Integer) where {T<:AbstractFloat} = TwicePrecision{T}(canonicalize2(splitprec(T, i)...)...) @@ -207,7 +211,7 @@ end function TwicePrecision{T}(nd::Tuple{Any,Any}) where {T} n, d = nd - TwicePrecision{T}(n) / d + TwicePrecision{T}(TwicePrecision{T}(n) / d) end function TwicePrecision{T}(nd::Tuple{I,I}, nb::Integer) where {T,I} @@ -329,13 +333,13 @@ function steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref), - TwicePrecision{Float64}(step, nb), Int(len), offset) + TwicePrecision{Float64}(step, nb), len, offset) end function steprangelen_hp(::Type{T}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} - StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], Int(len), offset) + StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], len, offset) end # AbstractFloat constructors (can supply a single number or a 2-tuple @@ -347,14 +351,13 @@ function steprangelen_hp(::Type{Float64}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref...), - twiceprecision(TwicePrecision{Float64}(step...), nb), Int(len), offset) + twiceprecision(TwicePrecision{Float64}(step...), nb), len, offset) end function steprangelen_hp(::Type{T}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} - StepRangeLen{T}(asF64(ref), - asF64(step), Int(len), offset) + StepRangeLen{T}(asF64(ref), asF64(step), len, offset) end @@ -365,30 +368,33 @@ StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, # Construct range for rational start=start_n/den, step=step_n/den function floatrange(::Type{T}, start_n::Integer, step_n::Integer, len::Integer, den::Integer) where T + len = len + 0 # promote with Int if len < 2 || step_n == 0 - return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) + return steprangelen_hp(T, (start_n, den), (step_n, den), 0, len, oneunit(len)) end # index of smallest-magnitude value - imin = clamp(round(Int, -start_n/step_n+1), 1, Int(len)) + L = typeof(len) + imin = clamp(round(typeof(len), -start_n/step_n+1), oneunit(L), len) # Compute smallest-magnitude element to 2x precision ref_n = start_n+(imin-1)*step_n # this shouldn't overflow, so don't check nb = nbitslen(T, len, imin) - steprangelen_hp(T, (ref_n, den), (step_n, den), nb, Int(len), imin) + steprangelen_hp(T, (ref_n, den), (step_n, den), nb, len, imin) end function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::AbstractFloat) + len = len + 0 # promote with Int T = promote_type(typeof(a), typeof(st), typeof(divisor)) m = maxintfloat(T, Int) if abs(a) <= m && abs(st) <= m && abs(divisor) <= m ia, ist, idivisor = round(Int, a), round(Int, st), round(Int, divisor) if ia == a && ist == st && idivisor == divisor # We can return the high-precision range - return floatrange(T, ia, ist, Int(len), idivisor) + return floatrange(T, ia, ist, len, idivisor) end end # Fallback (misses the opportunity to set offset different from 1, # but otherwise this is still high-precision) - steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), Int(len), 1) + steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), len, oneunit(len)) end function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} @@ -407,7 +413,7 @@ function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} rem(den, start_d) == 0 && rem(den, step_d) == 0 # check lcm overflow start_n = round(Int, start*den) step_n = round(Int, step*den) - len = max(0, div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d)) + len = max(0, Int(div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d))) # Integer ops could overflow, so check that this makes sense if isbetween(start, start + (len-1)*step, stop + step/2) && !isbetween(start, start + len*step, stop) @@ -418,6 +424,7 @@ function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} end end # Fallback, taking start and step literally + # n.b. we use Int as the default length type for IEEEFloats lf = (stop-start)/step if lf < 0 len = 0 @@ -436,6 +443,7 @@ step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T<:AbstractF step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T} = T(r.step) function range_start_step_length(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64} + len = len + 0 # promote with Int start_n, start_d = rat(a) step_n, step_d = rat(st) if start_d != 0 && step_d != 0 && @@ -474,31 +482,38 @@ end function getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, s::OrdinalRange{S}) where {T, S<:Integer} @boundscheck checkbounds(r, s) + len = length(s) + L = typeof(len) + sstep = step_hp(s) + rstep = step_hp(r) if S === Bool - if length(s) == 0 - return StepRangeLen(r.ref, r.step, 0, 1) - elseif length(s) == 1 + #rstep *= one(sstep) + if len == 0 + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) + elseif len == 1 if first(s) - return StepRangeLen(r.ref, r.step, 1, 1) + return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else - return StepRangeLen(r.ref, r.step, 0, 1) + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end - else # length(s) == 2 - return StepRangeLen(r[2], step(r), 1, 1) + else # len == 2 + return StepRangeLen{T}(last(r), step(r), oneunit(L), oneunit(L)) end else - soffset = 1 + round(Int, (r.offset - first(s))/step(s)) - soffset = clamp(soffset, 1, length(s)) - ioffset = first(s) + (soffset-1)*step(s) - if step(s) == 1 || length(s) < 2 - newstep = r.step + soffset = round(L, (r.offset - first(s))/sstep + 1) + soffset = clamp(soffset, oneunit(L), len) + ioffset = L(first(s) + (soffset - oneunit(L)) * sstep) + if sstep == 1 || len < 2 + newstep = rstep #* one(sstep) else - newstep = twiceprecision(r.step*step(s), nbitslen(T, length(s), soffset)) + newstep = rstep * sstep + newstep = twiceprecision(newstep, nbitslen(T, len, soffset)) end + soffset = max(oneunit(L), soffset) if ioffset == r.offset - return StepRangeLen(r.ref, newstep, length(s), max(1,soffset)) + return StepRangeLen{T}(r.ref, newstep, len, soffset) else - return StepRangeLen(r.ref + (ioffset-r.offset)*r.step, newstep, length(s), max(1,soffset)) + return StepRangeLen{T}(r.ref + (ioffset-r.offset)*rstep, newstep, len, soffset) end end end @@ -509,30 +524,30 @@ end /(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = StepRangeLen(r.ref/x, twiceprecision(r.step/x, nbitslen(r)), length(r), r.offset) -StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = r +StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = r -StepRangeLen{T,R,S}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = - _convertSRL(StepRangeLen{T,R,S}, r) +StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = + _convertSRL(StepRangeLen{T,R,S,L}, r) StepRangeLen{Float64}(r::StepRangeLen) = - _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::StepRangeLen) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,Float64,Float64}, r) + _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) StepRangeLen{Float64}(r::AbstractRange) = - _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::AbstractRange) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,Float64,Float64}, r) + _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} - StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{<:Integer}) where {T,R,S,L} + StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{<:Integer}) where {T,R,S} - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{<:Integer}) where {T,R,S,L} + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} # if start and step have a rational approximation in the old type, # then we transfer that rational approximation to the new type f, s = first(r), step(r) @@ -546,17 +561,17 @@ function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T, rem(den, start_d) == 0 && rem(den, step_d) == 0 start_n = round(Int, f*den) step_n = round(Int, s*den) - return floatrange(T, start_n, step_n, length(r), den) + return floatrange(T, start_n, step_n, L(length(r)), den) end end - __convertSRL(StepRangeLen{T,R,S}, r) + return __convertSRL(StepRangeLen{T,R,S,L}, r) end -function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{U}) where {T,R,S,U} - StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{U}) where {T,R,S,L,U} + StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end -function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end function sum(r::StepRangeLen) @@ -567,7 +582,7 @@ function sum(r::StepRangeLen) np, nn = l - r.offset, r.offset - 1 # positive, negative # To prevent overflow in sum(1:n), multiply its factors by the step sp, sn = sumpair(np), sumpair(nn) - W = widen(Int) + W = widen(typeof(l)) Δn = W(sp[1]) * W(sp[2]) - W(sn[1]) * W(sn[2]) s = r.step * Δn # Add in contributions of ref @@ -603,19 +618,20 @@ function +(r1::StepRangeLen{T,R}, r2::StepRangeLen{T,R}) where T where R<:TwiceP imid = r1.offset ref = r1.ref + r2.ref else - imid = round(Int, (r1.offset+r2.offset)/2) + imid = round(typeof(len), (r1.offset+r2.offset)/2) ref1mid = _getindex_hiprec(r1, imid) ref2mid = _getindex_hiprec(r2, imid) ref = ref1mid + ref2mid end step = twiceprecision(r1.step + r2.step, nbitslen(T, len, imid)) - StepRangeLen{T,typeof(ref),typeof(step)}(ref, step, len, imid) + StepRangeLen{T,typeof(ref),typeof(step),typeof(len)}(ref, step, len, imid) end ## LinRange # For Float16, Float32, and Float64, this returns a StepRangeLen function range_start_stop_length(start::T, stop::T, len::Integer) where {T<:IEEEFloat} + len = len + 0 # promote with Int len < 2 && return _linspace1(T, start, stop, len) if start == stop return steprangelen_hp(T, start, zero(T), 0, len, 1) @@ -638,32 +654,35 @@ function range_start_stop_length(start::T, stop::T, len::Integer) where {T<:IEEE end function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} + len = len + 0 # promote with Int (isfinite(start) && isfinite(stop)) || throw(ArgumentError("start and stop must be finite, got $start and $stop")) # Find the index that returns the smallest-magnitude element Δ, Δfac = stop-start, 1 if !isfinite(Δ) # handle overflow for large endpoints - Δ, Δfac = stop/len - start/len, Int(len) + Δ, Δfac = stop/len - start/len, len end tmin = -(start/Δ)/Δfac # t such that (1-t)*start + t*stop == 0 - imin = round(Int, tmin*(len-1)+1) # index approximately corresponding to t + L = typeof(len) + lenn1 = len - oneunit(L) + imin = round(L, tmin*lenn1 + 1) # index approximately corresponding to t if 1 < imin < len # The smallest-magnitude element is in the interior - t = (imin-1)/(len-1) + t = (imin - 1)/lenn1 ref = T((1-t)*start + t*stop) step = imin-1 < len-imin ? (ref-start)/(imin-1) : (stop-ref)/(len-imin) elseif imin <= 1 - imin = 1 + imin = oneunit(L) ref = start - step = (Δ/(len-1))*Δfac + step = (Δ/(lenn1))*Δfac else - imin = Int(len) + imin = len ref = stop - step = (Δ/(len-1))*Δfac + step = (Δ/(lenn1))*Δfac end if len == 2 && !isfinite(step) # For very large endpoints where step overflows, exploit the # split-representation to handle the overflow - return steprangelen_hp(T, start, (-start, stop), 0, 2, 1) + return steprangelen_hp(T, start, (-start, stop), 0, len, oneunit(L)) end # 2x calculations to get high precision endpoint matching while also # preventing overflow in ref_hi+(i-offset)*step_hi @@ -676,23 +695,28 @@ function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo step_lo = (b - a)/(len - 1) ref_lo = a - (1 - imin)*step_lo - steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, Int(len), imin) + steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, len, imin) end # range for rational numbers, start = start_n/den, stop = stop_n/den # Note this returns a StepRangeLen _linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where {T<:IEEEFloat} = _linspace(T, start, stop, len, one(start)) function _linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T<:IEEEFloat + len = len + 0 # promote with Int len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) - start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len, 1) + L = typeof(len) + start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len, oneunit(L)) tmin = -start_n/(Float64(stop_n) - Float64(start_n)) - imin = round(Int, tmin*(len-1)+1) - imin = clamp(imin, 1, Int(len)) - ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n - ref_denom = Int128(len-1) * den + imin = round(typeof(len), tmin*(len-1)+1) + imin = clamp(imin, oneunit(L), len) + W = widen(L) + start_n = W(start_n) + stop_n = W(stop_n) + ref_num = W(len-imin) * start_n + W(imin-1) * stop_n + ref_denom = W(len-1) * den ref = (ref_num, ref_denom) - step_full = (Int128(stop_n) - Int128(start_n), ref_denom) - steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), Int(len), imin) + step_full = (stop_n - start_n, ref_denom) + steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), len, imin) end # For len < 2 @@ -704,7 +728,7 @@ function _linspace1(::Type{T}, start, stop, len::Integer) where T<:IEEEFloat # The output type must be consistent with steprangelen_hp if T<:Union{Float32,Float16} return StepRangeLen{T}(Float64(start), Float64(start) - Float64(stop), len, 1) - else + else # T == Float64 return StepRangeLen(TwicePrecision(start, zero(T)), TwicePrecision(start, -stop), len, 1) end end @@ -713,8 +737,8 @@ end ### Numeric utilities -# Approximate x with a rational representation. Guaranteed to return, -# but not guaranteed to return a precise answer. +# Approximate x with a rational representation as a pair of Int values. +# Guaranteed to return, but not guaranteed to return a precise answer. # https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations function rat(x) y = x @@ -722,7 +746,7 @@ function rat(x) b = c = 0 m = maxintfloat(narrow(typeof(x)), Int) while abs(y) <= m - f = trunc(Int,y) + f = trunc(Int, y) y -= f a, c = f*a + c, a b, d = f*b + d, b diff --git a/test/ranges.jl b/test/ranges.jl index ad8554d14ea4a..d4ff83b81fe2d 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -652,8 +652,8 @@ end @test broadcast(-, T(1):2:6, 0.3) === T(1)-0.3:2:5-0.3 is_unsigned = T <: Unsigned is_unsigned && @test length(broadcast(-, T(1):3, 2)) === length(T(1)-2:T(3)-2) - @test broadcast(-, T(1):3) == -T(1):-1:-T(3) broken=is_unsigned - @test broadcast(-, 2, T(1):3) == T(1):-1:-T(1) broken=is_unsigned + @test broadcast(-, T(1):3) == -T(1):-T(1):-T(3) + @test broadcast(-, 2, T(1):3) == T(1):-T(1):-T(1) end @testset "operations between ranges and arrays" for T in (Int, UInt, Int128) @test all(([T(1):5;] + (T(5):-1:1)) .=== T(6)) @@ -1108,10 +1108,11 @@ end # repr/show should display the range nicely # to test print_range in range.jl replrepr(x) = repr("text/plain", x; context=IOContext(stdout, :limit=>true, :displaysize=>(24, 80))) + nb = Sys.WORD_SIZE @test replrepr(1:4) == "1:4" @test repr("text/plain", 1:4) == "1:4" @test repr("text/plain", range(1, stop=5, length=7)) == "1.0:0.6666666666666666:5.0" - @test repr("text/plain", LinRange{Float64}(1,5,7)) == "7-element LinRange{Float64}:\n 1.0,1.66667,2.33333,3.0,3.66667,4.33333,5.0" + @test repr("text/plain", LinRange{Float64}(1,5,7)) == "7-element LinRange{Float64, Int$nb}:\n 1.0,1.66667,2.33333,3.0,3.66667,4.33333,5.0" @test repr(range(1, stop=5, length=7)) == "1.0:0.6666666666666666:5.0" @test repr(LinRange{Float64}(1,5,7)) == "range(1.0, stop=5.0, length=7)" @test replrepr(0:100.) == "0.0:1.0:100.0" @@ -1119,7 +1120,7 @@ end # only examines spacing of the left and right edges of the range, sufficient # to cover the designated screen size. @test replrepr(range(0, stop=100, length=10000)) == "0.0:0.010001000100010001:100.0" - @test replrepr(LinRange{Float64}(0,100, 10000)) == "10000-element LinRange{Float64}:\n 0.0,0.010001,0.020002,0.030003,0.040004,…,99.95,99.96,99.97,99.98,99.99,100.0" + @test replrepr(LinRange{Float64}(0,100, 10000)) == "10000-element LinRange{Float64, Int$nb}:\n 0.0,0.010001,0.020002,0.030003,0.040004,…,99.95,99.96,99.97,99.98,99.99,100.0" @test sprint(show, UnitRange(1, 2)) == "1:2" @test sprint(show, StepRange(1, 2, 5)) == "1:2:5" @@ -2076,3 +2077,7 @@ end @test r[0:2] == r[0]:r[2] @test r[0:1:2] == r[0]:1:r[2] end + +@test length(range(1, 100, length=big(100)^100)) == big(100)^100 +@test length(range(big(1), big(100)^100, length=big(100)^100)) == big(100)^100 +@test length(0 * (1:big(100)^100)) == big(100)^100