diff --git a/base/broadcast.jl b/base/broadcast.jl index 24d0424a89727..d5392d1ca3639 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -8,7 +8,8 @@ Module containing the broadcasting implementation. module Broadcast using .Base.Cartesian -using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, +using .Base: Indices, OneTo, AxesStartStyle, AxesStart1, AxesStartAny +using .Base: tail, to_shape, isoperator, promote_typejoin, require_one_based_indexing, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias import .Base: copy, copyto!, axes export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__ @@ -490,7 +491,11 @@ _bcsm(a::Number, b::Number) = a == b || b == 1 # (We may not want to define general promotion rules between, say, OneTo and Slice, but if # we get here we know the axes are at least consistent for the purposes of broadcasting) axistype(a::T, b::T) where T = a -axistype(a, b) = UnitRange{Int}(a) +axistype(a, b) = _axistype(AxesStartStyle(a), AxesStartStyle(b), a, b) +_axistype(::AxesStart1, ::AxesStart1, a, b) = OneTo{Int}(a) +_axistype(::AxesStartAny, ::AxesStart1, a, b) = a # if we get here, b has length 1 +_axistype(::AxesStart1, ::AxesStartAny, a, b) = error("mismatched axes, or specialize `Broadcast.axistype` for ", typeof(a), " and ", typeof(b), " axes") +_axistype(::AxesStartAny, ::AxesStartAny, a, b) = error("mismatched axes, or specialize `Broadcast.axistype` for ", typeof(a), " and ", typeof(b), " axes") ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape @@ -1002,15 +1007,20 @@ 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::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = + (require_one_based_indexing(r); range(-first(r), step=-step(r), length=length(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)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = + (require_one_based_indexing(r); range(x + first(r), length=length(r))) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = + (require_one_based_indexing(r); 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::AbstractRange, x::Number) = + (require_one_based_indexing(r); range(first(r) + x, step=step(r), length=length(r))) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::AbstractRange) = + (require_one_based_indexing(r); range(x + first(r), step=step(r), length=length(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 = @@ -1019,9 +1029,12 @@ 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::AbstractUnitRange, x::Number) = + (require_one_based_indexing(r); range(first(r)-x, length=length(r))) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = + (require_one_based_indexing(r); range(first(r)-x, step=step(r), length=length(r))) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = + (require_one_based_indexing(r); range(x-first(r), step=-step(r), length=length(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 = @@ -1030,22 +1043,26 @@ 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(*), x::Number, r::AbstractRange) = range(x*first(r), step=x*step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::AbstractRange) = + (require_one_based_indexing(r); range(x*first(r), step=x*step(r), length=length(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::StepRangeLen{T}) where {T} = StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::LinRange) = LinRange(x * r.start, x * r.stop, r.len) # separate in case of noncommutative multiplication -broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::AbstractRange, x::Number) = range(first(r)*x, step=step(r)*x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::AbstractRange, x::Number) = + (require_one_based_indexing(r); range(first(r)*x, step=step(r)*x, length=length(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::LinRange, x::Number) = LinRange(r.start * x, r.stop * x, r.len) -broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, step=step(r)/x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = + (require_one_based_indexing(r); range(first(r)/x, step=step(r)/x, length=length(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::LinRange, x::Number) = LinRange(r.start / x, r.stop / x, r.len) -broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::AbstractRange) = range(x\first(r), step=x\step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::AbstractRange) = + (require_one_based_indexing(r); range(x\first(r), step=x\step(r), length=length(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::StepRangeLen) = StepRangeLen(x\r.ref, x\r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::LinRange) = LinRange(x \ r.start, x \ r.stop, r.len) diff --git a/base/indices.jl b/base/indices.jl index 4f3e8f533fbf5..1cfea7250fb8a 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -316,6 +316,7 @@ struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end Slice(S::Slice) = S +AxesStartStyle(::Type{<:Slice}) = AxesStartAny() axes(S::Slice) = (IdentityUnitRange(S.indices),) unsafe_indices(S::Slice) = (IdentityUnitRange(S.indices),) axes1(S::Slice) = IdentityUnitRange(S.indices) @@ -333,6 +334,13 @@ getindex(S::Slice, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundsche getindex(S::Slice, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") iterate(S::Slice, s...) = iterate(S.indices, s...) +_convert(::AxesStart1, ::AxesStartAny, ::Type{T}, S::Slice) where {T<:AbstractRange} = + (require_one_based_indexing(S); T(S)) +function _convert(::AxesStartAny, ::AxesStart1, ::Type{T}, r::AbstractUnitRange) where {T<:Slice} + throwerr(r) = (@_noinline_meta; throw(ArgumentError("`convert($T, r)` requires a range with first element 1, got $(first(r))"))) + first(r) == 1 || throwerr(r) + return T(r) +end """ @@ -346,6 +354,7 @@ struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end IdentityUnitRange(S::IdentityUnitRange) = S +AxesStartStyle(::Type{<:IdentityUnitRange}) = AxesStartAny() # IdentityUnitRanges are offset and thus have offset axes, so they are their own axes... but # we need to strip the wholedim marker because we don't know how they'll be used axes(S::IdentityUnitRange) = (S,) @@ -365,6 +374,13 @@ getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@_inline_meta getindex(S::IdentityUnitRange, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) show(io::IO, r::IdentityUnitRange) = print(io, "Base.IdentityUnitRange(", r.indices, ")") iterate(S::IdentityUnitRange, s...) = iterate(S.indices, s...) +_convert(::AxesStart1, ::AxesStartAny, ::Type{T}, S::IdentityUnitRange) where {T<:AbstractRange} = + (require_one_based_indexing(S); T(S)) +function _convert(::AxesStartAny, ::AxesStart1, ::Type{T}, r::AbstractUnitRange) where {T<:IdentityUnitRange} + throwerr(r) = (@_noinline_meta; throw(ArgumentError("`convert($T, r)` requires a range with first element 1, got $(first(r))"))) + first(r) == 1 || throwerr(r) + return T(r) +end """ LinearIndices(A::AbstractArray) diff --git a/base/range.jl b/base/range.jl index e7607839a4785..d1f3ab19d833b 100644 --- a/base/range.jl +++ b/base/range.jl @@ -140,7 +140,20 @@ abstract type AbstractRange{T} <: AbstractArray{T,1} end RangeStepStyle(::Type{<:AbstractRange}) = RangeStepIrregular() RangeStepStyle(::Type{<:AbstractRange{<:Integer}}) = RangeStepRegular() -convert(::Type{T}, r::AbstractRange) where {T<:AbstractRange} = r isa T ? r : T(r) +AxesStartStyle(::Type{<:AbstractRange}) = AxesStart1() # opt-out of AxesStart1 for "weird" range types +AxesStartStyle(r::AbstractRange) = AxesStartStyle(typeof(r)) + +convert(::Type{AbstractRange}, r::AbstractRange) = r +convert(::Type{T}, r::T) where {T<:AbstractRange} = r +convert(::Type{T}, r::AbstractRange) where {T<:AbstractRange} = _convert(AxesStartStyle(T), AxesStartStyle(r), T, r) +_convert(::AxesStart1, ::AxesStart1, ::Type{T}, r::AbstractRange) where {T<:AbstractRange} = T(r) +_convert(::AxesStartStyle, ::AxesStartStyle, ::Type{T}, r::AbstractRange) where {T<:AbstractRange} = + throw(MethodError(convert, (T, r))) + +require_one_based_indexing(r::AbstractRange) = _require_one_based_indexing(AxesStartStyle(r), r) +_require_one_based_indexing(::AxesStartStyle, r) = + !has_offset_axes(r) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) +_require_one_based_indexing(::AxesStart1, r) = true ## ordinal ranges @@ -157,6 +170,8 @@ type can represent values smaller than `oneunit(Float64)`. """ abstract type OrdinalRange{T,S} <: AbstractRange{T} end +convert(::Type{OrdinalRange{T,S}}, r::OrdinalRange{T,S}) where {T,S} = r + """ AbstractUnitRange{T} <: OrdinalRange{T, T} @@ -165,6 +180,8 @@ Supertype for ranges with a step size of [`oneunit(T)`](@ref) with elements of t """ abstract type AbstractUnitRange{T} <: OrdinalRange{T,T} end +convert(::Type{AbstractUnitRange{T}}, r::AbstractUnitRange{T}) where {T} = r + """ StepRange{T, S} <: OrdinalRange{T, S} @@ -307,15 +324,15 @@ be 1. struct OneTo{T<:Integer} <: AbstractUnitRange{T} stop::T OneTo{T}(stop) where {T<:Integer} = new(max(zero(T), stop)) - function OneTo{T}(r::AbstractRange) where {T<:Integer} - throwstart(r) = (@_noinline_meta; throw(ArgumentError("first element must be 1, got $(first(r))"))) - throwstep(r) = (@_noinline_meta; throw(ArgumentError("step must be 1, got $(step(r))"))) - first(r) == 1 || throwstart(r) - step(r) == 1 || throwstep(r) - return new(max(zero(T), last(r))) - end end OneTo(stop::T) where {T<:Integer} = OneTo{T}(stop) +function OneTo{T}(r::AbstractRange) where {T<:Integer} + throwstart(r) = (@_noinline_meta; throw(ArgumentError("first element must be 1, got $(first(r))"))) + throwstep(r) = (@_noinline_meta; throw(ArgumentError("step must be 1, got $(step(r))"))) + first(r) == 1 || throwstart(r) + step(r) == 1 || throwstep(r) + return OneTo{T}(last(r)) +end OneTo(r::AbstractRange{T}) where {T<:Integer} = OneTo{T}(r) ## Step ranges parameterized by length @@ -713,10 +730,14 @@ show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ' show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") +range_axes_first_same(r, s) = _range_axes_first_same(AxesStartStyle(r), AxesStartStyle(s), r, s) +_range_axes_first_same(::AxesStart1, ::AxesStart1, r, s) = true +_range_axes_first_same(::AxesStartStyle, ::AxesStartStyle, r, s) = first(axes1(r)) == first(axes1(s)) + ==(r::T, s::T) where {T<:AbstractRange} = - (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) & range_axes_first_same(r, s) ==(r::OrdinalRange, s::OrdinalRange) = - (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) & range_axes_first_same(r, s) ==(r::T, s::T) where {T<:Union{StepRangeLen,LinRange}} = (first(r) == first(s)) & (length(r) == length(s)) & (last(r) == last(s)) ==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} = @@ -727,6 +748,7 @@ function ==(r::AbstractRange, s::AbstractRange) if lr != length(s) return false end + range_axes_first_same(r, s) || return false yr, ys = iterate(r), iterate(s) while yr !== nothing yr[1] == ys[1] || return false @@ -849,7 +871,7 @@ end ## linear operations on ranges ## --(r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +-(r::OrdinalRange) = (require_one_based_indexing(r); 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::LinRange) = LinRange(-r.start, -r.stop, length(r)) @@ -861,6 +883,9 @@ el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{S,n}}) el_same(::Type{T}, a::Type{<:AbstractArray{S,n}}, b::Type{<:AbstractArray{T,n}}) where {T,S,n} = b el_same(::Type, a, b) = promote_typejoin(a, b) +# promote_rule and more constructors +# Note: construction is distinct from conversion, convert should check require_one_based_indexing(r) +# but construction should not. promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = el_same(promote_type(T1,T2), a, b) UnitRange{T}(r::UnitRange{T}) where {T<:Real} = r @@ -944,7 +969,10 @@ end Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r) collect(r::AbstractRange) = vcat(r) -reverse(r::OrdinalRange) = (:)(last(r), -step(r), first(r)) +function reverse(r::OrdinalRange) + require_one_based_indexing(r) + (:)(last(r), -step(r), first(r)) +end function reverse(r::StepRangeLen) # If `r` is empty, `length(r) - r.offset + 1 will be nonpositive hence # invalid. As `reverse(r)` is also empty, any offset would work so we keep @@ -964,8 +992,11 @@ sort!(r::AbstractUnitRange) = r sort(r::AbstractRange) = issorted(r) ? r : reverse(r) -sortperm(r::AbstractUnitRange) = 1:length(r) -sortperm(r::AbstractRange) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) +sortperm(r::AbstractUnitRange) = (require_one_based_indexing(r); 1:length(r)) +function sortperm(r::AbstractRange) + require_one_based_indexing(r) + issorted(r) ? (1:1:length(r)) : (length(r):-1:1) +end function sum(r::AbstractRange{<:Real}) l = length(r) @@ -1004,6 +1035,7 @@ function _define_range_op(@nospecialize f) r1l = length(r1) (r1l == length(r2) || throw(DimensionMismatch("argument dimensions must match"))) + require_one_based_indexing(r1, r2) range($f(first(r1), first(r2)), step=$f(step(r1), step(r2)), length=r1l) end diff --git a/base/reducedim.jl b/base/reducedim.jl index 7b005aeff4f10..c595473415a84 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -4,7 +4,8 @@ # for reductions that expand 0 dims to 1 reduced_index(i::OneTo) = OneTo(1) -reduced_index(i::Union{Slice, IdentityUnitRange}) = first(i):first(i) +reduced_index(i::Slice) = Slice(first(i):first(i)) +reduced_index(i::IdentityUnitRange) = IdentityUnitRange(first(i):first(i)) reduced_index(i::AbstractUnitRange) = throw(ArgumentError( """ @@ -43,33 +44,24 @@ function reduced_indices0(inds::Indices{N}, d::Int) where N end end -function reduced_indices(inds::Indices{N}, region) where N - rinds = [inds...] +function check_reduced_region(region, N) for i in region isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) - d = Int(i) - if d < 1 - throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) - elseif d <= N - rinds[d] = reduced_index(rinds[d]) + if i < 1 + throw(ArgumentError("region dimension(s) must be ≥ 1, got $i")) end end - tuple(rinds...)::typeof(inds) + return nothing +end + +function reduced_indices(inds::Indices{N}, region) where N + check_reduced_region(region, N) + ntuple(i->in(i, region) ? reduced_index(inds[i]) : inds[i], Val(N))::typeof(inds) end function reduced_indices0(inds::Indices{N}, region) where N - rinds = [inds...] - for i in region - isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) - d = Int(i) - if d < 1 - throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) - elseif d <= N - rind = rinds[d] - rinds[d] = isempty(rind) ? rind : reduced_index(rind) - end - end - tuple(rinds...)::typeof(inds) + check_reduced_region(region, N) + ntuple(i->in(i, region) ? (r = inds[i]; isempty(r) ? r : reduced_index(r)) : inds[i], Val(N))::typeof(inds) end ###### Generic reduction functions ##### diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 4a156d11d1cf5..12b0868e056f6 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -108,7 +108,7 @@ function axes(a::ReinterpretArray{T,N,S} where {N}) where {T,S} paxs = axes(a.parent) f, l = first(paxs[1]), length(paxs[1]) size1 = div(l*sizeof(S), sizeof(T)) - tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...) + tuple(typeof(paxs[1])(f:f+size1-1), tail(paxs)...) end axes(a::ReinterpretArray{T,0}) where {T} = () diff --git a/base/traits.jl b/base/traits.jl index 3c6a8019483b7..de07d1413a436 100644 --- a/base/traits.jl +++ b/base/traits.jl @@ -57,3 +57,18 @@ struct RangeStepRegular <: RangeStepStyle end # range with regular step struct RangeStepIrregular <: RangeStepStyle end # range with rounding error RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) + +# trait that allows skipping of axes-checking on abstract range types (risks overflow on `length`) +""" + AxesStartStyle(instance) + AxesStartStyle(T::Type) + +Indicate the value that `axes(instance)` starts with. Containers that return `AxesStart1()` +must have `axes(instance)` start with 1 (e.g., `Base.OneTo` axes). Such containers may +bypass axes checks for certain operations (e.g., range comparisons to avoid risk of overflow). +`AxesStartAny()` indicates that one cannot count on the axes starting with 1, and that +an explicit check is required. +""" +abstract type AxesStartStyle end +struct AxesStart1 <: AxesStartStyle end +struct AxesStartAny <: AxesStartStyle end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index da449c7c67c76..42515d67cb994 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -922,6 +922,7 @@ end @test J1 === J2 end + CR = CartesianIndices((2:4, 1:5)) i = CartesianIndex(17,-2) @test CR .+ i === i .+ CR === CartesianIndices((19:21, -1:3)) @test CR .- i === CartesianIndices((-15:-13, 3:7)) diff --git a/test/arrayops.jl b/test/arrayops.jl index 4e989217cc5c3..28a8870daff75 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1792,7 +1792,8 @@ end @test CartesianIndices(CartesianIndex{3}(1,2,3)) == CartesianIndices((1, 2, 3)) @test Tuple{}(CartesianIndices{0,Tuple{}}(())) == () - R = CartesianIndices(map(Base.IdentityUnitRange, (2:5, 3:5))) + RR = map(Base.IdentityUnitRange, (2:5, 3:5)) + R = CartesianIndices(RR) @test eltype(R) <: CartesianIndex{2} @test eltype(typeof(R)) <: CartesianIndex{2} @test eltype(CartesianIndices{2}) <: CartesianIndex{2} @@ -1814,8 +1815,8 @@ end @test !in(CartesianIndex((3,6)), R) @test !in(CartesianIndex((6,5)), R) - @test @inferred(convert(NTuple{2,UnitRange}, R)) === (2:5, 3:5) - @test @inferred(convert(Tuple{Vararg{UnitRange}}, R)) === (2:5, 3:5) + @test @inferred(convert(NTuple{2,AbstractUnitRange}, R)) === RR + @test @inferred(convert(Tuple{Vararg{AbstractUnitRange}}, R)) === RR I = CartesianIndex(2,3) J = CartesianIndex(5,4) diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 820656faf4acc..9e03651be5f4b 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -9,473 +9,471 @@ using Statistics const OAs_name = join(fullname(OffsetArrays), ".") -let -# Basics -v0 = rand(4) -v = OffsetArray(v0, (-3,)) -h = OffsetArray([-1,1,-2,2,0], (-3,)) -@test axes(v) == (-2:1,) -@test size(v) == (4,) -@test size(v, 1) == 4 - -A0 = [1 3; 2 4] -A = OffsetArray(A0, (-1,2)) # IndexLinear -S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # IndexCartesian -@test axes(A) == axes(S) == (0:1, 3:4) -@test size(A) == (2,2) -@test size(A, 1) == 2 - -# Scalar indexing -@test A[0,3] == A[1] == A[0,3,1] == S[0,3] == S[1] == S[0,3,1] == 1 -@test A[1,3] == A[2] == A[1,3,1] == S[1,3] == S[2] == S[1,3,1] == 2 -@test A[0,4] == A[3] == A[0,4,1] == S[0,4] == S[3] == S[0,4,1] == 3 -@test A[1,4] == A[4] == A[1,4,1] == S[1,4] == S[4] == S[1,4,1] == 4 -@test_throws BoundsError A[1,1] -@test_throws BoundsError S[1,1] -@test_throws BoundsError A[0,3,2] -@test_throws BoundsError S[0,3,2] -# partial indexing -S3 = OffsetArray(view(reshape(Vector(1:4*3*1), 4, 3, 1), 1:3, 1:2, :), (-1,-2,1)) -@test S3[1,-1] == 2 -@test S3[1,0] == 6 -@test_throws BoundsError S3[1,1] -@test_throws BoundsError S3[1,-2] -S4 = OffsetArray(view(reshape(Vector(1:4*3*2), 4, 3, 2), 1:3, 1:2, :), (-1,-2,1)) -@test S4[1,-1,2] == 2 -@test S4[1,0,2] == 6 -@test_throws BoundsError S4[1,1,2] -@test_throws BoundsError S4[1,-2,2] - - -# Vector indexing -@test A[:, 3] == S[:, 3] == OffsetArray([1,2], (A.offsets[1],)) -@test A[:, 4] == S[:, 4] == OffsetArray([3,4], (A.offsets[1],)) -@test_throws BoundsError A[:, 1] -@test_throws BoundsError S[:, 1] -@test A[0, :] == S[0, :] == OffsetArray([1,3], (A.offsets[2],)) -@test A[1, :] == S[1, :] == OffsetArray([2,4], (A.offsets[2],)) -@test_throws BoundsError A[2, :] -@test_throws BoundsError S[2, :] -@test A[0:1, 3] == S[0:1, 3] == [1,2] -@test A[[1,0], 3] == S[[1,0], 3] == [2,1] -@test A[0, 3:4] == S[0, 3:4] == [1,3] -@test A[1, [4,3]] == S[1, [4,3]] == [4,2] -@test A[:, :] == S[:, :] == A - -A_3_3 = OffsetArray(Matrix{Int}(undef, 3,3), (-2,-1)) -A_3_3[:, :] = reshape(1:9, 3, 3) -for i = 1:9 @test A_3_3[i] == i end -A_3_3[-1:1, 0:2] = reshape(1:9, 3, 3) -for i = 1:9 @test A_3_3[i] == i end -A_3_3[:, :] = 1:9 -for i = 1:9 @test A_3_3[i] == i end -A_3_3[-1:1, 0:2] = 1:9 -for i = 1:9 @test A_3_3[i] == i end -A_3_3[:] = 1:9 -for i = 1:9 @test A_3_3[i] == i end -A_3_3[1:9] = 1:9 -for i = 1:9 @test A_3_3[i] == i end - -# CartesianIndexing -@test A[CartesianIndex((0,3))] == S[CartesianIndex((0,3))] == 1 -@test_throws BoundsError A[CartesianIndex(1,1)] -@test_throws BoundsError S[CartesianIndex(1,1)] -@test eachindex(A) == 1:4 -@test eachindex(S) == CartesianIndices(axes(S)) == CartesianIndices(map(Base.IdentityUnitRange, (0:1,3:4))) - -# LinearIndices -# issue 27986 -let a1 = [11,12,13], a2 = [1 2; 3 4] - b1 = OffsetArray(a1, (-3,)) - i1 = LinearIndices(b1) - @test i1[-2] == -2 - @test_throws BoundsError i1[-3] - @test_throws BoundsError i1[1] - @test i1[-2:end] === -2:0 - @test @inferred(i1[-2:0]) === -2:0 - @test_throws BoundsError i1[-3:end] - @test_throws BoundsError i1[-2:1] - b2 = OffsetArray(a2, (-3,5)) - i2 = LinearIndices(b2) - @test i2[3] == 3 - @test_throws BoundsError i2[0] - @test_throws BoundsError i2[5] - @test @inferred(i2[2:3]) === 2:3 - @test @inferred(i2[1:2:4]) === 1:2:3 - @test_throws BoundsError i2[1:5] - @test_throws BoundsError i2[1:2:5] -end +@testset "Basics" begin + v0 = rand(4) + v = OffsetArray(v0, (-3,)) + h = OffsetArray([-1,1,-2,2,0], (-3,)) + @test axes(v) == (Base.IdentityUnitRange(-2:1),) + @test size(v) == (4,) + @test size(v, 1) == 4 + + A0 = [1 3; 2 4] + A = OffsetArray(A0, (-1,2)) # IndexLinear + S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # IndexCartesian + @test axes(A) == axes(S) == map(Base.IdentityUnitRange, (0:1, 3:4)) + @test size(A) == (2,2) + @test size(A, 1) == 2 + + # Scalar indexing + @test A[0,3] == A[1] == A[0,3,1] == S[0,3] == S[1] == S[0,3,1] == 1 + @test A[1,3] == A[2] == A[1,3,1] == S[1,3] == S[2] == S[1,3,1] == 2 + @test A[0,4] == A[3] == A[0,4,1] == S[0,4] == S[3] == S[0,4,1] == 3 + @test A[1,4] == A[4] == A[1,4,1] == S[1,4] == S[4] == S[1,4,1] == 4 + @test_throws BoundsError A[1,1] + @test_throws BoundsError S[1,1] + @test_throws BoundsError A[0,3,2] + @test_throws BoundsError S[0,3,2] + # partial indexing + S3 = OffsetArray(view(reshape(Vector(1:4*3*1), 4, 3, 1), 1:3, 1:2, :), (-1,-2,1)) + @test S3[1,-1] == 2 + @test S3[1,0] == 6 + @test_throws BoundsError S3[1,1] + @test_throws BoundsError S3[1,-2] + S4 = OffsetArray(view(reshape(Vector(1:4*3*2), 4, 3, 2), 1:3, 1:2, :), (-1,-2,1)) + @test S4[1,-1,2] == 2 + @test S4[1,0,2] == 6 + @test_throws BoundsError S4[1,1,2] + @test_throws BoundsError S4[1,-2,2] + + + # Vector indexing + @test A[:, 3] == S[:, 3] == OffsetArray([1,2], (A.offsets[1],)) + @test A[:, 4] == S[:, 4] == OffsetArray([3,4], (A.offsets[1],)) + @test_throws BoundsError A[:, 1] + @test_throws BoundsError S[:, 1] + @test A[0, :] == S[0, :] == OffsetArray([1,3], (A.offsets[2],)) + @test A[1, :] == S[1, :] == OffsetArray([2,4], (A.offsets[2],)) + @test_throws BoundsError A[2, :] + @test_throws BoundsError S[2, :] + @test A[0:1, 3] == S[0:1, 3] == [1,2] + @test A[[1,0], 3] == S[[1,0], 3] == [2,1] + @test A[0, 3:4] == S[0, 3:4] == [1,3] + @test A[1, [4,3]] == S[1, [4,3]] == [4,2] + @test A[:, :] == S[:, :] == A + + A_3_3 = OffsetArray(Matrix{Int}(undef, 3,3), (-2,-1)) + A_3_3[:, :] = reshape(1:9, 3, 3) + for i = 1:9 @test A_3_3[i] == i end + A_3_3[-1:1, 0:2] = reshape(1:9, 3, 3) + for i = 1:9 @test A_3_3[i] == i end + A_3_3[:, :] = 1:9 + for i = 1:9 @test A_3_3[i] == i end + A_3_3[-1:1, 0:2] = 1:9 + for i = 1:9 @test A_3_3[i] == i end + A_3_3[:] = 1:9 + for i = 1:9 @test A_3_3[i] == i end + A_3_3[1:9] = 1:9 + for i = 1:9 @test A_3_3[i] == i end + + # CartesianIndexing + @test A[CartesianIndex((0,3))] == S[CartesianIndex((0,3))] == 1 + @test_throws BoundsError A[CartesianIndex(1,1)] + @test_throws BoundsError S[CartesianIndex(1,1)] + @test eachindex(A) == 1:4 + @test eachindex(S) == CartesianIndices(axes(S)) == CartesianIndices(map(Base.IdentityUnitRange, (0:1,3:4))) + + # LinearIndices + # issue 27986 + let a1 = [11,12,13], a2 = [1 2; 3 4] + b1 = OffsetArray(a1, (-3,)) + i1 = LinearIndices(b1) + @test i1[-2] == -2 + @test_throws BoundsError i1[-3] + @test_throws BoundsError i1[1] + @test i1[-2:end] === -2:0 + @test @inferred(i1[-2:0]) === -2:0 + @test_throws BoundsError i1[-3:end] + @test_throws BoundsError i1[-2:1] + b2 = OffsetArray(a2, (-3,5)) + i2 = LinearIndices(b2) + @test i2[3] == 3 + @test_throws BoundsError i2[0] + @test_throws BoundsError i2[5] + @test @inferred(i2[2:3]) === 2:3 + @test @inferred(i2[1:2:4]) === 1:2:3 + @test_throws BoundsError i2[1:5] + @test_throws BoundsError i2[1:2:5] + end -# logical indexing -@test A[A .> 2] == [3,4] -@test_throws BoundsError h[trues(2)] -@test_throws BoundsError h[trues(5)] -@test h[OffsetArray(trues(5), (-3,))] == parent(h) -@test h[OffsetArray([true,false,false,true,true], (-3,))] == parent(h)[[1,4,5]] -@test A[OffsetArray([true false; false true], A.offsets)] == [1,4] -@test A[OffsetArray([true true; false true], A.offsets)] == [1,3,4] -@test_throws BoundsError A[[true true; false true]] - -# view -S = view(A, :, 3) -@test S == OffsetArray([1,2], (A.offsets[1],)) -@test S[0] == 1 -@test S[1] == 2 -@test_throws BoundsError S[2] -@test axes(S) === (Base.IdentityUnitRange(0:1),) -S = view(A, 0, :) -@test S == OffsetArray([1,3], (A.offsets[2],)) -@test S[3] == 1 -@test S[4] == 3 -@test_throws BoundsError S[1] -@test axes(S) === (Base.IdentityUnitRange(3:4),) -S = view(A, 0:0, 4) -@test S == [3] -@test S[1] == 3 -@test_throws BoundsError S[0] -@test axes(S) === (Base.OneTo(1),) -S = view(A, 1, 3:4) -@test S == [2,4] -@test S[1] == 2 -@test S[2] == 4 -@test_throws BoundsError S[3] -@test axes(S) === (Base.OneTo(2),) -S = view(A, :, :) -@test S == A -@test S[0,3] == S[1] == 1 -@test S[1,3] == S[2] == 2 -@test S[0,4] == S[3] == 3 -@test S[1,4] == S[4] == 4 -@test_throws BoundsError S[1,1] -@test axes(S) === Base.IdentityUnitRange.((0:1, 3:4)) -# https://github.com/JuliaArrays/OffsetArrays.jl/issues/27 -g = OffsetArray(Vector(-2:3), (-3,)) -gv = view(g, -1:2) -@test axes(gv, 1) === Base.OneTo(4) -@test collect(gv) == -1:2 -gv = view(g, OffsetArray(-1:2, (-2,))) -@test axes(gv, 1) === Base.IdentityUnitRange(-1:2) -@test collect(gv) == -1:2 -gv = view(g, OffsetArray(-1:2, (-1,))) -@test axes(gv, 1) === Base.IdentityUnitRange(0:3) -@test collect(gv) == -1:2 - -# iteration -for (a,d) in zip(A, A0) - @test a == d -end + # logical indexing + @test A[A .> 2] == [3,4] + @test_throws BoundsError h[trues(2)] + @test_throws BoundsError h[trues(5)] + @test h[OffsetArray(trues(5), (-3,))] == parent(h) + @test h[OffsetArray([true,false,false,true,true], (-3,))] == parent(h)[[1,4,5]] + @test A[OffsetArray([true false; false true], A.offsets)] == [1,4] + @test A[OffsetArray([true true; false true], A.offsets)] == [1,3,4] + @test_throws BoundsError A[[true true; false true]] + + # view + S = view(A, :, 3) + @test S == OffsetArray([1,2], (A.offsets[1],)) + @test S[0] == 1 + @test S[1] == 2 + @test_throws BoundsError S[2] + @test axes(S) === (Base.IdentityUnitRange(0:1),) + S = view(A, 0, :) + @test S == OffsetArray([1,3], (A.offsets[2],)) + @test S[3] == 1 + @test S[4] == 3 + @test_throws BoundsError S[1] + @test axes(S) === (Base.IdentityUnitRange(3:4),) + S = view(A, 0:0, 4) + @test S == [3] + @test S[1] == 3 + @test_throws BoundsError S[0] + @test axes(S) === (Base.OneTo(1),) + S = view(A, 1, 3:4) + @test S == [2,4] + @test S[1] == 2 + @test S[2] == 4 + @test_throws BoundsError S[3] + @test axes(S) === (Base.OneTo(2),) + S = view(A, :, :) + @test S == A + @test S[0,3] == S[1] == 1 + @test S[1,3] == S[2] == 2 + @test S[0,4] == S[3] == 3 + @test S[1,4] == S[4] == 4 + @test_throws BoundsError S[1,1] + @test axes(S) === Base.IdentityUnitRange.((0:1, 3:4)) + # https://github.com/JuliaArrays/OffsetArrays.jl/issues/27 + g = OffsetArray(Vector(-2:3), (-3,)) + gv = view(g, -1:2) + @test axes(gv, 1) === Base.OneTo(4) + @test collect(gv) == -1:2 + gv = view(g, OffsetArray(-1:2, (-2,))) + @test axes(gv, 1) === Base.IdentityUnitRange(-1:2) + @test collect(gv) == -1:2 + gv = view(g, OffsetArray(-1:2, (-1,))) + @test axes(gv, 1) === Base.IdentityUnitRange(0:3) + @test collect(gv) == -1:2 + + # iteration + for (a,d) in zip(A, A0) + @test a == d + end -# show -io = IOBuffer() -show(io, v) -str = String(take!(io)) -show(io, v0) -@test str == String(take!(io)) -show(io, A) -str = String(take!(io)) -@test str == "[1 3; 2 4]" -show(io, S) -str = String(take!(io)) -@test str == "[1 3; 2 4]" -show(io, MIME("text/plain"), A) -strs = split(strip(String(take!(io))), '\n') -@test strs[2] == " 1 3" -@test strs[3] == " 2 4" -v = OffsetArray(rand(3), (-2,)) -show(io, v) -str = String(take!(io)) -show(io, parent(v)) -@test str == String(take!(io)) -smry = summary(v) -@test occursin("OffsetArray{Float64,1", smry) -@test occursin("with indices -1:1", smry) -function cmp_showf(printfunc, io, A; options = ()) - ioc = IOContext(io, :limit => true, :compact => true, options...) - printfunc(ioc, A) - str1 = String(take!(io)) - printfunc(ioc, parent(A)) - str2 = String(take!(io)) - @test str1 == str2 -end -cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,5), (10,-9))) # rows&cols fit -cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,5), (10,-9))) # columns fit -cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,10^3), (10,-9))) # rows fit -cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,10^3), (10,-9))) # neither fits -cmp_showf(Base.print_matrix, io, OffsetArray(reshape(range(-0.212121212121, stop=2/11, length=3*29), 3, 29), (-2, -15)); options=(:displaysize=>(53,210),)) -targets1 = ["0-dimensional $OAs_name.OffsetArray{Float64,0,Array{Float64,0}}:\n1.0", - "1-element $OAs_name.OffsetArray{Float64,1,Array{Float64,1}} with indices 2:2:\n 1.0", - "1×1 $OAs_name.OffsetArray{Float64,2,Array{Float64,2}} with indices 2:2×3:3:\n 1.0", - "1×1×1 $OAs_name.OffsetArray{Float64,3,Array{Float64,3}} with indices 2:2×3:3×4:4:\n[:, :, 4] =\n 1.0", - "1×1×1×1 $OAs_name.OffsetArray{Float64,4,Array{Float64,4}} with indices 2:2×3:3×4:4×5:5:\n[:, :, 4, 5] =\n 1.0"] -targets2 = ["(1.0, 1.0)", - "([1.0], [1.0])", - "([1.0], [1.0])", - "([1.0], [1.0])", - "([1.0], [1.0])"] -@testset "printing of OffsetArray with n=$n" for n = 0:4 - a = OffsetArray(fill(1.,ntuple(d->1,n)), ntuple(identity,n)) - show(IOContext(io, :limit => true), MIME("text/plain"), a) - @test String(take!(io)) == targets1[n+1] - show(IOContext(io, :limit => true), MIME("text/plain"), (a,a)) - @test String(take!(io)) == targets2[n+1] -end -P = OffsetArray(rand(8,8), (1,1)) -PV = view(P, 2:3, :) -@test endswith(summary(PV), "with indices Base.OneTo(2)×2:9") - -# Similar -B = similar(A, Float32) -@test isa(B, OffsetArray{Float32,2}) -@test axes(B) === axes(A) -B = similar(A, (3,4)) -@test isa(B, Array{Int,2}) -@test size(B) == (3,4) -@test axes(B) === (Base.OneTo(3), Base.OneTo(4)) -B = similar(A, (-3:3,1:4)) -@test isa(B, OffsetArray{Int,2}) -@test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4)) -B = similar(parent(A), (-3:3,1:4)) -@test isa(B, OffsetArray{Int,2}) -@test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4)) - -# Indexing with OffsetArray indices -i1 = OffsetArray([2,1], (-5,)) -i1 = OffsetArray([2,1], -5) -b = A0[i1, 1] -@test axes(b) === (Base.IdentityUnitRange(-4:-3),) -@test b[-4] == 2 -@test b[-3] == 1 -b = A0[1,i1] -@test axes(b) === (Base.IdentityUnitRange(-4:-3),) -@test b[-4] == 3 -@test b[-3] == 1 -v = view(A0, i1, 1) -@test axes(v) === (Base.IdentityUnitRange(-4:-3),) -v = view(A0, 1:1, i1) -@test axes(v) === (Base.OneTo(1), Base.IdentityUnitRange(-4:-3)) - -# copyto! and fill! -a = OffsetArray{Int}(undef, (-3:-1,)) -fill!(a, -1) -copyto!(a, (1,2)) # non-array iterables -@test a[-3] == 1 -@test a[-2] == 2 -@test a[-1] == -1 -fill!(a, -1) -copyto!(a, -2, (1,2)) -@test a[-3] == -1 -@test a[-2] == 1 -@test a[-1] == 2 -@test_throws BoundsError copyto!(a, 1, (1,2)) -fill!(a, -1) -copyto!(a, -2, (1,2,3), 2) -@test a[-3] == -1 -@test a[-2] == 2 -@test a[-1] == 3 -@test_throws BoundsError copyto!(a, -2, (1,2,3), 1) -fill!(a, -1) -copyto!(a, -2, (1,2,3), 1, 2) -@test a[-3] == -1 -@test a[-2] == 1 -@test a[-1] == 2 - -b = 1:2 # copy between AbstractArrays -bo = OffsetArray(1:2, (-3,)) -@test_throws BoundsError copyto!(a, b) -fill!(a, -1) -copyto!(a, bo) -@test a[-3] == -1 -@test a[-2] == 1 -@test a[-1] == 2 -fill!(a, -1) -copyto!(a, -2, bo) -@test a[-3] == -1 -@test a[-2] == 1 -@test a[-1] == 2 -@test_throws BoundsError copyto!(a, -4, bo) -@test_throws BoundsError copyto!(a, -1, bo) -fill!(a, -1) -copyto!(a, -3, b, 2) -@test a[-3] == 2 -@test a[-2] == a[-1] == -1 -@test_throws BoundsError copyto!(a, -3, b, 1, 4) -am = OffsetArray{Int}(undef, (1:1, 7:9)) # for testing linear indexing -fill!(am, -1) -copyto!(am, b) -@test am[1] == 1 -@test am[2] == 2 -@test am[3] == -1 -@test am[1,7] == 1 -@test am[1,8] == 2 -@test am[1,9] == -1 - -# map -dest = similar(am) -map!(+, dest, am, am) -@test dest[1,7] == 2 -@test dest[1,8] == 4 -@test dest[1,9] == -2 - -am = map(identity, a) -@test isa(am, OffsetArray) -@test am == a - -# dropdims -a0 = rand(1,1,8,8,1) -a = OffsetArray(a0, (-1,2,3,4,5)) -@test @inferred(dropdims(a, dims=1)) == @inferred(dropdims(a, dims=(1,))) == OffsetArray(reshape(a, (1,8,8,1)), (2,3,4,5)) -@test @inferred(dropdims(a, dims=5)) == @inferred(dropdims(a, dims=(5,))) == OffsetArray(reshape(a, (1,1,8,8)), (-1,2,3,4)) -@test @inferred(dropdims(a, dims=(1,5))) == dropdims(a, dims=(5,1)) == OffsetArray(reshape(a, (1,8,8)), (2,3,4)) -@test @inferred(dropdims(a, dims=(1,2,5))) == dropdims(a, dims=(5,2,1)) == OffsetArray(reshape(a, (8,8)), (3,4)) -@test_throws ArgumentError dropdims(a, dims=0) -@test_throws ArgumentError dropdims(a, dims=(1,1)) -@test_throws ArgumentError dropdims(a, dims=(1,2,1)) -@test_throws ArgumentError dropdims(a, dims=(1,1,2)) -@test_throws ArgumentError dropdims(a, dims=3) -@test_throws ArgumentError dropdims(a, dims=4) -@test_throws ArgumentError dropdims(a, dims=6) - -# other functions -v = OffsetArray(v0, (-3,)) -@test lastindex(v) == 1 -@test v ≈ v -@test axes(v') === (Base.OneTo(1),Base.IdentityUnitRange(-2:1)) -@test parent(v) == collect(v) -rv = reverse(v) -@test axes(rv) == axes(v) -@test rv[1] == v[-2] -@test rv[0] == v[-1] -@test rv[-1] == v[0] -@test rv[-2] == v[1] -cv = copy(v) -@test reverse!(cv) == rv - -A = OffsetArray(rand(4,4), (-3,5)) -@test lastindex(A) == 16 -@test lastindex(A, 1) == 1 -@test lastindex(A, 2) == 9 -@test A ≈ A -@test axes(A') === Base.IdentityUnitRange.((6:9, -2:1)) -@test parent(copy(A')) == copy(parent(A)') -@test collect(A) == parent(A) -@test maximum(A) == maximum(parent(A)) -@test minimum(A) == minimum(parent(A)) -@test extrema(A) == extrema(parent(A)) -@test maximum(A, dims=1) == OffsetArray(maximum(parent(A), dims=1), A.offsets) -@test maximum(A, dims=2) == OffsetArray(maximum(parent(A), dims=2), A.offsets) -@test maximum(A, dims=1:2) == OffsetArray(maximum(parent(A), dims=1:2), A.offsets) -C = similar(A) -cumsum!(C, A, dims=1) -@test parent(C) == cumsum(parent(A), dims=1) -@test parent(cumsum(A, dims=1)) == cumsum(parent(A), dims=1) -cumsum!(C, A, dims=2) -@test parent(C) == cumsum(parent(A), dims=2) -R = similar(A, (1:1, 6:9)) -maximum!(R, A) -@test parent(R) == maximum(parent(A), dims=1) -R = similar(A, (-2:1, 1:1)) -maximum!(R, A) -@test parent(R) == maximum(parent(A), dims=2) -amin, iamin = findmin(A) -pmin, ipmin = findmin(parent(A)) -@test amin == pmin -@test A[iamin] == amin -@test amin == parent(A)[ipmin] -amax, iamax = findmax(A) -pmax, ipmax = findmax(parent(A)) -@test amax == pmax -@test A[iamax] == amax -@test amax == parent(A)[ipmax] -z = OffsetArray([0 0; 2 0; 0 0; 0 0], (-3,-1)) -I = findall(!iszero, z) -@test I == [CartesianIndex(-1, 0)] -@test findall(!iszero,h) == [-2:1;] -@test findall(x->x>0, h) == [-1,1] -@test findall(x->x<0, h) == [-2,0] -@test findall(x->x==0, h) == [2] -@test mean(A_3_3) == median(A_3_3) == 5 -@test mean(x->2x, A_3_3) == 10 -@test mean(A_3_3, dims=1) == median(A_3_3, dims=1) == OffsetArray([2 5 8], A_3_3.offsets) -@test mean(A_3_3, dims=2) == median(A_3_3, dims=2) == OffsetArray(reshape([4,5,6],(3,1)), A_3_3.offsets) -@test var(A_3_3) == 7.5 -@test std(A_3_3, dims=1) == OffsetArray([1 1 1], A_3_3.offsets) -@test std(A_3_3, dims=2) == OffsetArray(reshape([3,3,3], (3,1)), A_3_3.offsets) -@test sum(OffsetArray(fill(1,3000), -1000)) == 3000 - -@test norm(v) ≈ norm(parent(v)) -@test norm(A) ≈ norm(parent(A)) -@test dot(v, v) ≈ dot(v0, v0) - -# Prior to its removal from Base, cumsum_kbn was used here. To achieve the same level of -# accuracy in the tests, we need to use BigFloats with enlarged precision. -@testset "high-precision array reduction" begin - setprecision(BigFloat, 500) do - v = OffsetArray(BigFloat[1,1e100,1,-1e100], (-3,)) .* 1000 - v2 = OffsetArray(BigFloat[1,-1e100,1,1e100], ( 5,)) .* 1000 - @test isa(v, OffsetArray) - cv = OffsetArray(BigFloat[1, 1e100, 1e100,2], (-3,)) .* 1000 - cv2 = OffsetArray(BigFloat[1,-1e100,-1e100,2], ( 5,)) .* 1000 - @test cumsum(v) ≈ cv - @test cumsum(v2) ≈ cv2 - @test sum(v) ≈ sum(parent(v)) + # show + io = IOBuffer() + show(io, v) + str = String(take!(io)) + show(io, v0) + @test str == String(take!(io)) + show(io, A) + str = String(take!(io)) + @test str == "[1 3; 2 4]" + show(io, S) + str = String(take!(io)) + @test str == "[1 3; 2 4]" + show(io, MIME("text/plain"), A) + strs = split(strip(String(take!(io))), '\n') + @test strs[2] == " 1 3" + @test strs[3] == " 2 4" + v = OffsetArray(rand(3), (-2,)) + show(io, v) + str = String(take!(io)) + show(io, parent(v)) + @test str == String(take!(io)) + smry = summary(v) + @test occursin("OffsetArray{Float64,1", smry) + @test occursin("with indices -1:1", smry) + function cmp_showf(printfunc, io, A; options = ()) + ioc = IOContext(io, :limit => true, :compact => true, options...) + printfunc(ioc, A) + str1 = String(take!(io)) + printfunc(ioc, parent(A)) + str2 = String(take!(io)) + @test str1 == str2 + end + cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,5), (10,-9))) # rows&cols fit + cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,5), (10,-9))) # columns fit + cmp_showf(Base.print_matrix, io, OffsetArray(rand(5,10^3), (10,-9))) # rows fit + cmp_showf(Base.print_matrix, io, OffsetArray(rand(10^3,10^3), (10,-9))) # neither fits + cmp_showf(Base.print_matrix, io, OffsetArray(reshape(range(-0.212121212121, stop=2/11, length=3*29), 3, 29), (-2, -15)); options=(:displaysize=>(53,210),)) + targets1 = ["0-dimensional $OAs_name.OffsetArray{Float64,0,Array{Float64,0}}:\n1.0", + "1-element $OAs_name.OffsetArray{Float64,1,Array{Float64,1}} with indices 2:2:\n 1.0", + "1×1 $OAs_name.OffsetArray{Float64,2,Array{Float64,2}} with indices 2:2×3:3:\n 1.0", + "1×1×1 $OAs_name.OffsetArray{Float64,3,Array{Float64,3}} with indices 2:2×3:3×4:4:\n[:, :, 4] =\n 1.0", + "1×1×1×1 $OAs_name.OffsetArray{Float64,4,Array{Float64,4}} with indices 2:2×3:3×4:4×5:5:\n[:, :, 4, 5] =\n 1.0"] + targets2 = ["(1.0, 1.0)", + "([1.0], [1.0])", + "([1.0], [1.0])", + "([1.0], [1.0])", + "([1.0], [1.0])"] + @testset "printing of OffsetArray with n=$n" for n = 0:4 + a = OffsetArray(fill(1.,ntuple(d->1,n)), ntuple(identity,n)) + show(IOContext(io, :limit => true), MIME("text/plain"), a) + @test String(take!(io)) == targets1[n+1] + show(IOContext(io, :limit => true), MIME("text/plain"), (a,a)) + @test String(take!(io)) == targets2[n+1] + end + P = OffsetArray(rand(8,8), (1,1)) + PV = view(P, 2:3, :) + @test endswith(summary(PV), "with indices Base.OneTo(2)×2:9") + + # Similar + B = similar(A, Float32) + @test isa(B, OffsetArray{Float32,2}) + @test axes(B) === axes(A) + B = similar(A, (3,4)) + @test isa(B, Array{Int,2}) + @test size(B) == (3,4) + @test axes(B) === (Base.OneTo(3), Base.OneTo(4)) + B = similar(A, (-3:3,1:4)) + @test isa(B, OffsetArray{Int,2}) + @test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4)) + B = similar(parent(A), (-3:3,1:4)) + @test isa(B, OffsetArray{Int,2}) + @test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4)) + + # Indexing with OffsetArray indices + i1 = OffsetArray([2,1], (-5,)) + i1 = OffsetArray([2,1], -5) + b = A0[i1, 1] + @test axes(b) === (Base.IdentityUnitRange(-4:-3),) + @test b[-4] == 2 + @test b[-3] == 1 + b = A0[1,i1] + @test axes(b) === (Base.IdentityUnitRange(-4:-3),) + @test b[-4] == 3 + @test b[-3] == 1 + v = view(A0, i1, 1) + @test axes(v) === (Base.IdentityUnitRange(-4:-3),) + v = view(A0, 1:1, i1) + @test axes(v) === (Base.OneTo(1), Base.IdentityUnitRange(-4:-3)) + + # copyto! and fill! + a = OffsetArray{Int}(undef, (-3:-1,)) + fill!(a, -1) + copyto!(a, (1,2)) # non-array iterables + @test a[-3] == 1 + @test a[-2] == 2 + @test a[-1] == -1 + fill!(a, -1) + copyto!(a, -2, (1,2)) + @test a[-3] == -1 + @test a[-2] == 1 + @test a[-1] == 2 + @test_throws BoundsError copyto!(a, 1, (1,2)) + fill!(a, -1) + copyto!(a, -2, (1,2,3), 2) + @test a[-3] == -1 + @test a[-2] == 2 + @test a[-1] == 3 + @test_throws BoundsError copyto!(a, -2, (1,2,3), 1) + fill!(a, -1) + copyto!(a, -2, (1,2,3), 1, 2) + @test a[-3] == -1 + @test a[-2] == 1 + @test a[-1] == 2 + + b = 1:2 # copy between AbstractArrays + bo = OffsetArray(1:2, (-3,)) + @test_throws BoundsError copyto!(a, b) + fill!(a, -1) + copyto!(a, bo) + @test a[-3] == -1 + @test a[-2] == 1 + @test a[-1] == 2 + fill!(a, -1) + copyto!(a, -2, bo) + @test a[-3] == -1 + @test a[-2] == 1 + @test a[-1] == 2 + @test_throws BoundsError copyto!(a, -4, bo) + @test_throws BoundsError copyto!(a, -1, bo) + fill!(a, -1) + copyto!(a, -3, b, 2) + @test a[-3] == 2 + @test a[-2] == a[-1] == -1 + @test_throws BoundsError copyto!(a, -3, b, 1, 4) + am = OffsetArray{Int}(undef, (1:1, 7:9)) # for testing linear indexing + fill!(am, -1) + copyto!(am, b) + @test am[1] == 1 + @test am[2] == 2 + @test am[3] == -1 + @test am[1,7] == 1 + @test am[1,8] == 2 + @test am[1,9] == -1 + + # map + dest = similar(am) + map!(+, dest, am, am) + @test dest[1,7] == 2 + @test dest[1,8] == 4 + @test dest[1,9] == -2 + + am = map(identity, a) + @test isa(am, OffsetArray) + @test am == a + + # dropdims + a0 = rand(1,1,8,8,1) + a = OffsetArray(a0, (-1,2,3,4,5)) + @test @inferred(dropdims(a, dims=1)) == @inferred(dropdims(a, dims=(1,))) == OffsetArray(reshape(a, (1,8,8,1)), (2,3,4,5)) + @test @inferred(dropdims(a, dims=5)) == @inferred(dropdims(a, dims=(5,))) == OffsetArray(reshape(a, (1,1,8,8)), (-1,2,3,4)) + @test @inferred(dropdims(a, dims=(1,5))) == dropdims(a, dims=(5,1)) == OffsetArray(reshape(a, (1,8,8)), (2,3,4)) + @test @inferred(dropdims(a, dims=(1,2,5))) == dropdims(a, dims=(5,2,1)) == OffsetArray(reshape(a, (8,8)), (3,4)) + @test_throws ArgumentError dropdims(a, dims=0) + @test_throws ArgumentError dropdims(a, dims=(1,1)) + @test_throws ArgumentError dropdims(a, dims=(1,2,1)) + @test_throws ArgumentError dropdims(a, dims=(1,1,2)) + @test_throws ArgumentError dropdims(a, dims=3) + @test_throws ArgumentError dropdims(a, dims=4) + @test_throws ArgumentError dropdims(a, dims=6) + + # other functions + v = OffsetArray(v0, (-3,)) + @test lastindex(v) == 1 + @test v ≈ v + @test axes(v') === (Base.OneTo(1),Base.IdentityUnitRange(-2:1)) + @test parent(v) == collect(v) + rv = reverse(v) + @test axes(rv) == axes(v) + @test rv[1] == v[-2] + @test rv[0] == v[-1] + @test rv[-1] == v[0] + @test rv[-2] == v[1] + cv = copy(v) + @test reverse!(cv) == rv + + A = OffsetArray(rand(4,4), (-3,5)) + @test lastindex(A) == 16 + @test lastindex(A, 1) == 1 + @test lastindex(A, 2) == 9 + @test A ≈ A + @test axes(A') === Base.IdentityUnitRange.((6:9, -2:1)) + @test parent(copy(A')) == copy(parent(A)') + @test collect(A) == parent(A) + @test maximum(A) == maximum(parent(A)) + @test minimum(A) == minimum(parent(A)) + @test extrema(A) == extrema(parent(A)) + @test maximum(A, dims=1) == OffsetArray(maximum(parent(A), dims=1), A.offsets) + @test maximum(A, dims=2) == OffsetArray(maximum(parent(A), dims=2), A.offsets) + @test maximum(A, dims=1:2) == OffsetArray(maximum(parent(A), dims=1:2), A.offsets) + C = similar(A) + cumsum!(C, A, dims=1) + @test parent(C) == cumsum(parent(A), dims=1) + @test parent(cumsum(A, dims=1)) == cumsum(parent(A), dims=1) + cumsum!(C, A, dims=2) + @test parent(C) == cumsum(parent(A), dims=2) + R = similar(A, (1:1, 6:9)) + maximum!(R, A) + @test parent(R) == maximum(parent(A), dims=1) + R = similar(A, (-2:1, 1:1)) + maximum!(R, A) + @test parent(R) == maximum(parent(A), dims=2) + amin, iamin = findmin(A) + pmin, ipmin = findmin(parent(A)) + @test amin == pmin + @test A[iamin] == amin + @test amin == parent(A)[ipmin] + amax, iamax = findmax(A) + pmax, ipmax = findmax(parent(A)) + @test amax == pmax + @test A[iamax] == amax + @test amax == parent(A)[ipmax] + z = OffsetArray([0 0; 2 0; 0 0; 0 0], (-3,-1)) + I = findall(!iszero, z) + @test I == [CartesianIndex(-1, 0)] + @test findall(!iszero,h) == [-2:1;] + @test findall(x->x>0, h) == [-1,1] + @test findall(x->x<0, h) == [-2,0] + @test findall(x->x==0, h) == [2] + @test mean(A_3_3) == median(A_3_3) == 5 + @test mean(x->2x, A_3_3) == 10 + @test mean(A_3_3, dims=1) == median(A_3_3, dims=1) == OffsetArray([2 5 8], A_3_3.offsets) + @test mean(A_3_3, dims=2) == median(A_3_3, dims=2) == OffsetArray(reshape([4,5,6],(3,1)), A_3_3.offsets) + @test var(A_3_3) == 7.5 + @test std(A_3_3, dims=1) == OffsetArray([1 1 1], A_3_3.offsets) + @test std(A_3_3, dims=2) == OffsetArray(reshape([3,3,3], (3,1)), A_3_3.offsets) + @test sum(OffsetArray(fill(1,3000), -1000)) == 3000 + + @test norm(v) ≈ norm(parent(v)) + @test norm(A) ≈ norm(parent(A)) + @test dot(v, v) ≈ dot(v0, v0) + + # Prior to its removal from Base, cumsum_kbn was used here. To achieve the same level of + # accuracy in the tests, we need to use BigFloats with enlarged precision. + @testset "high-precision array reduction" begin + setprecision(BigFloat, 500) do + v = OffsetArray(BigFloat[1,1e100,1,-1e100], (-3,)) .* 1000 + v2 = OffsetArray(BigFloat[1,-1e100,1,1e100], ( 5,)) .* 1000 + @test isa(v, OffsetArray) + cv = OffsetArray(BigFloat[1, 1e100, 1e100,2], (-3,)) .* 1000 + cv2 = OffsetArray(BigFloat[1,-1e100,-1e100,2], ( 5,)) .* 1000 + @test cumsum(v) ≈ cv + @test cumsum(v2) ≈ cv2 + @test sum(v) ≈ sum(parent(v)) + end end -end -io = IOBuffer() -writedlm(io, A) -seek(io, 0) -@test readdlm(io, eltype(A)) == parent(A) - -amin, amax = extrema(parent(A)) -@test clamp.(A, (amax+amin)/2, amax).parent == clamp.(parent(A), (amax+amin)/2, amax) - -@test unique(A, dims=1) == OffsetArray(parent(A), 0, first(axes(A, 2)) - 1) -@test unique(A, dims=2) == OffsetArray(parent(A), first(axes(A, 1)) - 1, 0) -v = OffsetArray(rand(8), (-2,)) -@test sort(v) == OffsetArray(sort(parent(v)), v.offsets) -@test sortslices(A, dims=1) == OffsetArray(sortslices(parent(A), dims=1), A.offsets) -@test sortslices(A, dims=2) == OffsetArray(sortslices(parent(A), dims=2), A.offsets) -@test sort(A, dims=1) == OffsetArray(sort(parent(A), dims=1), A.offsets) -@test sort(A, dims=2) == OffsetArray(sort(parent(A), dims=2), A.offsets) - -@test mapslices(sort, A, dims=1) == OffsetArray(mapslices(sort, parent(A), dims=1), A.offsets) -@test mapslices(sort, A, dims=2) == OffsetArray(mapslices(sort, parent(A), dims=2), A.offsets) - -@test rotl90(A) == OffsetArray(rotl90(parent(A)), A.offsets[[2,1]]) -@test rotr90(A) == OffsetArray(rotr90(parent(A)), A.offsets[[2,1]]) -@test reverse(A, dims=1) == OffsetArray(reverse(parent(A), dims=1), A.offsets) -@test reverse(A, dims=2) == OffsetArray(reverse(parent(A), dims=2), A.offsets) - -@test A .+ 1 == OffsetArray(parent(A) .+ 1, A.offsets) -@test 2*A == OffsetArray(2*parent(A), A.offsets) -@test A+A == OffsetArray(parent(A)+parent(A), A.offsets) -@test A.*A == OffsetArray(parent(A).*parent(A), A.offsets) - -@test circshift(A, (-1,2)) == OffsetArray(circshift(parent(A), (-1,2)), A.offsets) - -src = reshape(Vector(1:16), (4,4)) -dest = OffsetArray(Matrix{Int}(undef, 4,4), (-1,1)) -circcopy!(dest, src) -@test parent(dest) == [8 12 16 4; 5 9 13 1; 6 10 14 2; 7 11 15 3] -@test dest[1:3,2:4] == src[1:3,2:4] - -# reshape -A = OffsetArray(rand(4,4), (-3,5)) -@test vec(A) == reshape(A, :) == reshape(A, 16) == reshape(A, Val(1)) == A[:] == vec(A.parent) -A = OffsetArray(view(rand(4,4), 1:4, 4:-1:1), (-3,5)) -@test vec(A) == reshape(A, :) == reshape(A, 16) == reshape(A, Val(1)) == A[:] == vec(A.parent) - -# broadcast -a = [1] -b = OffsetArray(a, (0,)) -@test @inferred(a .+ b) == [2] -a = OffsetArray([1, -2, 1], (-2,)) -@test a .* a' == OffsetArray([ 1 -2 1; - -2 4 -2; - 1 -2 1], (-2,-2)) - -end # let + io = IOBuffer() + writedlm(io, A) + seek(io, 0) + @test readdlm(io, eltype(A)) == parent(A) + + amin, amax = extrema(parent(A)) + @test clamp.(A, (amax+amin)/2, amax).parent == clamp.(parent(A), (amax+amin)/2, amax) + + @test unique(A, dims=1) == OffsetArray(parent(A), 0, first(axes(A, 2)) - 1) + @test unique(A, dims=2) == OffsetArray(parent(A), first(axes(A, 1)) - 1, 0) + v = OffsetArray(rand(8), (-2,)) + @test sort(v) == OffsetArray(sort(parent(v)), v.offsets) + @test sortslices(A, dims=1) == OffsetArray(sortslices(parent(A), dims=1), A.offsets) + @test sortslices(A, dims=2) == OffsetArray(sortslices(parent(A), dims=2), A.offsets) + @test sort(A, dims=1) == OffsetArray(sort(parent(A), dims=1), A.offsets) + @test sort(A, dims=2) == OffsetArray(sort(parent(A), dims=2), A.offsets) + + @test mapslices(sort, A, dims=1) == OffsetArray(mapslices(sort, parent(A), dims=1), A.offsets) + @test mapslices(sort, A, dims=2) == OffsetArray(mapslices(sort, parent(A), dims=2), A.offsets) + + @test rotl90(A) == OffsetArray(rotl90(parent(A)), A.offsets[[2,1]]) + @test rotr90(A) == OffsetArray(rotr90(parent(A)), A.offsets[[2,1]]) + @test reverse(A, dims=1) == OffsetArray(reverse(parent(A), dims=1), A.offsets) + @test reverse(A, dims=2) == OffsetArray(reverse(parent(A), dims=2), A.offsets) + + @test A .+ 1 == OffsetArray(parent(A) .+ 1, A.offsets) + @test 2*A == OffsetArray(2*parent(A), A.offsets) + @test A+A == OffsetArray(parent(A)+parent(A), A.offsets) + @test A.*A == OffsetArray(parent(A).*parent(A), A.offsets) + + @test circshift(A, (-1,2)) == OffsetArray(circshift(parent(A), (-1,2)), A.offsets) + + src = reshape(Vector(1:16), (4,4)) + dest = OffsetArray(Matrix{Int}(undef, 4,4), (-1,1)) + circcopy!(dest, src) + @test parent(dest) == [8 12 16 4; 5 9 13 1; 6 10 14 2; 7 11 15 3] + @test dest[1:3,2:4] == src[1:3,2:4] + + # reshape + A = OffsetArray(rand(4,4), (-3,5)) + @test vec(A) == reshape(A, :) == reshape(A, 16) == reshape(A, Val(1)) == A[:] == vec(A.parent) + A = OffsetArray(view(rand(4,4), 1:4, 4:-1:1), (-3,5)) + @test vec(A) == reshape(A, :) == reshape(A, 16) == reshape(A, Val(1)) == A[:] == vec(A.parent) + + # broadcast + a = [1] + b = OffsetArray(a, (0,)) + @test @inferred(a .+ b) == [2] + a = OffsetArray([1, -2, 1], (-2,)) + @test a .* a' == OffsetArray([ 1 -2 1; + -2 4 -2; + 1 -2 1], (-2,-2)) +end # Check that similar throws a MethodError rather than a # StackOverflowError if no appropriate method has been defined diff --git a/test/ranges.jl b/test/ranges.jl index d2092d53bb839..ab974603882eb 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1458,3 +1458,49 @@ end Base.TwicePrecision(-1.0, -0.0), 0) @test reverse(reverse(1.0:0.0)) === 1.0:0.0 end + +@testset "Fallbacks for IdentityUnitRange" begin + r = Base.IdentityUnitRange(-2:2) + argerr = ArgumentError("offset arrays are not supported but got an array with index other than 1") + @test r != -2:2 + @test r != -2:1:2 + @test r == r + @test r != Base.IdentityUnitRange(-1:2) + @test +r === r + @test UnitRange{Int}(r) === UnitRange(r) === -2:2 + @test StepRange{Int,Int}(r) === StepRange(r) === -2:1:2 + @test StepRangeLen{Int,Int,Int}(r) === StepRangeLen(r) === StepRangeLen(-2, 1, length(r)) + @test LinRange{Int}(r) === LinRange(r) === LinRange{Int}(-2, 2, length(r)) + @test_throws argerr convert(UnitRange{Int}, r) + @test_throws argerr convert(UnitRange, r) + @test_throws argerr convert(StepRange{Int,Int}, r) + @test_throws argerr convert(StepRange, r) + @test_throws argerr convert(StepRangeLen{Int,Int,Int}, r) + @test_throws argerr convert(StepRangeLen, r) + @test_throws argerr convert(LinRange{Int}, r) + @test_throws argerr convert(LinRange, r) + @test_throws argerr -r + @test_throws argerr .-r + @test_throws argerr r .+ 1 + @test_throws argerr 1 .+ r + @test_throws argerr r .+ im + @test_throws argerr im .+ r + @test_throws argerr r .- 1 + @test_throws argerr 1 .- r + @test_throws argerr 2 * r + @test_throws argerr r * 2 + @test_throws argerr 2 .* r + @test_throws argerr r .* 2 + @test_throws argerr r / 2 + @test_throws argerr r ./ 2 + @test_throws argerr 2 \ r + @test_throws argerr 2 .\ r + @test_throws argerr r + r + @test_throws argerr r - r + @test_throws argerr r .+ r + @test_throws argerr r .- r + @test_throws MethodError r .* r + @test_throws DimensionMismatch r .* (-2:2) + @test_throws argerr reverse(r) + @test_throws argerr sortperm(r) +end