Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detangle Slice and fix mixed-dimension in-place reductions #28941

Merged
merged 4 commits into from
Dec 1, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Introduce a whole new IdentityUnitRange
that we will encourage offset array implementations to use instead of Base.Slice
  • Loading branch information
mbauman committed Sep 12, 2018
commit 7702adc01645b24c50d40ba632619b2e45d47336
6 changes: 3 additions & 3 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ checkindex(::Type{Bool}, inds::AbstractUnitRange, i) =
throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))"))
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds))
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::WholeSlice) = true
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true
function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange)
@_propagate_inbounds_meta
isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r)))
Expand Down Expand Up @@ -1739,7 +1739,7 @@ end

nextL(L, l::Integer) = L*l
nextL(L, r::AbstractUnitRange) = L*unsafe_length(r)
nextL(L, r::WholeSlice) = L*unsafe_length(r.indices)
nextL(L, r::Slice) = L*unsafe_length(r.indices)
offsetin(i, l::Integer) = i-1
offsetin(i, r::AbstractUnitRange) = i-first(r)

Expand Down Expand Up @@ -1901,7 +1901,7 @@ function mapslices(f, A::AbstractArray; dims)
idx = Any[first(ind) for ind in axes(A)]
itershape = tuple(dimsA[otherdims]...)
for d in dims
idx[d] = WholeSlice(axes(A, d))
idx[d] = Slice(axes(A, d))
end

# Apply the function to the first slice in order to determine the next steps
Expand Down
2 changes: 1 addition & 1 deletion base/abstractarraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ julia> selectdim(A, 2, 3)
7
```
"""
@inline selectdim(A::AbstractArray, d::Integer, i) = _selectdim(A, d, i, setindex(map(WholeSlice, axes(A)), i, d))
@inline selectdim(A::AbstractArray, d::Integer, i) = _selectdim(A, d, i, setindex(map(Slice, axes(A)), i, d))
@noinline function _selectdim(A, d, i, idxs)
d >= 1 || throw(ArgumentError("dimension must be ≥ 1"))
nd = ndims(A)
Expand Down
53 changes: 39 additions & 14 deletions base/indices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -298,29 +298,22 @@ _maybetail(t::Tuple) = tail(t)
"""
Slice(indices)

Represent an AbstractUnitRange of indices as a vector of the indices themselves.
Represent an AbstractUnitRange of indices as a vector of the indices themselves,
with special handling to signal they represent a complete slice of a dimension (:).

Upon calling `to_indices`, Colons are converted to Slice objects to represent
the indices over which the Colon spans. Slice objects are themselves unit
ranges with the same indices as those they wrap. This means that indexing into
Slice objects with an integer always returns that exact integer, and they
iterate over all the wrapped indices, even supporting offset indices.
"""
struct Slice{T<:AbstractUnitRange, wholedim} <: AbstractUnitRange{Int}
struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int}
indices::T
end
Slice(r::AbstractUnitRange) = Slice{typeof(r), false}(r)
Slice(S::Slice) = Slice{typeof(S.indices), false}(S.indices)
Slice(S::Slice{<:Any, false}) = S
const WholeSlice{T} = Slice{T, true}
WholeSlice(r::AbstractUnitRange) = Slice{typeof(r), true}(r)
WholeSlice(S::Slice) = Slice{typeof(S.indices), true}(S.indices)
WholeSlice(S::WholeSlice) = S
# Slices 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::Slice) = (Slice(S),)
unsafe_indices(S::Slice) = (Slice(S),)
axes1(S::Slice) = Slice(S)
Slice(S::Slice) = S
axes(S::Slice) = (IdentityUnitRange(S.indices),)
unsafe_indices(S::Slice) = (IdentityUnitRange(S.indices),)
axes1(S::Slice) = IdentityUnitRange(S.indices)
axes(S::Slice{<:OneTo}) = (S.indices,)
unsafe_indices(S::Slice{<:OneTo}) = (S.indices,)
axes1(S::Slice{<:OneTo}) = S.indices
Expand All @@ -336,6 +329,38 @@ getindex(S::Slice, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck check
show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")")
iterate(S::Slice, s...) = iterate(S.indices, s...)


"""
IdentityUnitRange(range::AbstractUnitRange)

Represent an AbstractUnitRange `range` as an offset vector such that `range[i] == i`.

`IdentityUnitRange`s are frequently used as axes for offset arrays.
"""
struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int}
indices::T
end
IdentityUnitRange(S::IdentityUnitRange) = S
# 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,)
unsafe_indices(S::IdentityUnitRange) = (S,)
axes1(S::IdentityUnitRange) = S
axes(S::IdentityUnitRange{<:OneTo}) = (S.indices,)
unsafe_indices(S::IdentityUnitRange{<:OneTo}) = (S.indices,)
axes1(S::IdentityUnitRange{<:OneTo}) = S.indices

first(S::IdentityUnitRange) = first(S.indices)
last(S::IdentityUnitRange) = last(S.indices)
size(S::IdentityUnitRange) = (length(S.indices),)
length(S::IdentityUnitRange) = length(S.indices)
unsafe_length(S::IdentityUnitRange) = unsafe_length(S.indices)
getindex(S::IdentityUnitRange, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
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...)

"""
LinearIndices(A::AbstractArray)

Expand Down
4 changes: 2 additions & 2 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,8 @@ _maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i)
(uncolon(inds, I), to_indices(A, _maybetail(inds), tail(I))...)

const CI0 = Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}}
uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = WholeSlice(OneTo(1))
uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = WholeSlice(inds[1])
uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = Slice(OneTo(1))
uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1])

### From abstractarray.jl: Internal multidimensional indexing definitions ###
getindex(x::Number, i::CartesianIndex{0}) = x
Expand Down
2 changes: 1 addition & 1 deletion base/reducedim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# for reductions that expand 0 dims to 1
reduced_index(i::OneTo) = OneTo(1)
reduced_index(i::Slice) = first(i):first(i)
reduced_index(i::Union{Slice, IdentityUnitRange}) = first(i):first(i)
reduced_index(i::AbstractUnitRange) =
throw(ArgumentError(
"""
Expand Down
4 changes: 2 additions & 2 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1818,7 +1818,7 @@ dims2string(d) = isempty(d) ? "0-dimensional" :

inds2string(inds) = join(map(_indsstring,inds), '×')
_indsstring(i) = string(i)
_indsstring(i::Slice) = string(i.indices)
_indsstring(i::Union{IdentityUnitRange, Slice}) = string(i.indices)

# anything array-like gets summarized e.g. 10-element Array{Int64,1}
summary(io::IO, a::AbstractArray) = summary(io, a, axes(a))
Expand Down Expand Up @@ -1893,7 +1893,7 @@ function showarg(io::IO, v::SubArray, toplevel)
print(io, ')')
toplevel && print(io, " with eltype ", eltype(v))
end
showindices(io, ::Slice, inds...) =
showindices(io, ::Union{Slice,IdentityUnitRange}, inds...) =
(print(io, ", :"); showindices(io, inds...))
showindices(io, ind1, inds...) =
(print(io, ", ", ind1); showindices(io, inds...))
Expand Down
1 change: 0 additions & 1 deletion base/sort.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import .Base:
sort!,
issorted,
sortperm,
Slice,
to_indices

export # also exported by Base
Expand Down
18 changes: 9 additions & 9 deletions base/subarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ viewindexing() = IndexLinear()
# Leading scalar indices simply increase the stride
viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I)))
# Slices may begin a section which may be followed by any number of Slices
viewindexing(I::Tuple{WholeSlice, WholeSlice, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I)))
# A UnitRange can follow WholeSlices, but only if all other indices are scalar
viewindexing(I::Tuple{WholeSlice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
viewindexing(I::Tuple{WholeSlice, WholeSlice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate
viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I)))
# A UnitRange can follow Slices, but only if all other indices are scalar
viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate
# In general, ranges are only fast if all other indices are scalar
viewindexing(I::Tuple{AbstractRange, Vararg{ScalarIndex}}) = IndexLinear()
# All other index combinations are slow
Expand Down Expand Up @@ -190,7 +190,7 @@ reindex(V, idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) =
(@_propagate_inbounds_meta; (idxs[1], reindex(V, tail(idxs), subidxs)...))

# Slices simply pass their subindices straight through
reindex(V, idxs::Tuple{WholeSlice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
reindex(V, idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
(@_propagate_inbounds_meta; (subidxs[1], reindex(V, tail(idxs), tail(subidxs))...))

# Re-index into parent vectors with one subindex
Expand Down Expand Up @@ -229,7 +229,7 @@ function getindex(V::FastSubArray, i::Int)
r
end
# We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange
FastContiguousSubArray{T,N,P,I<:Tuple{Union{WholeSlice, AbstractUnitRange}, Vararg{Any}}} = SubArray{T,N,P,I,true}
FastContiguousSubArray{T,N,P,I<:Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}}} = SubArray{T,N,P,I,true}
function getindex(V::FastContiguousSubArray, i::Int)
@_inline_meta
@boundscheck checkbounds(V, i)
Expand Down Expand Up @@ -266,7 +266,7 @@ strides(V::SubArray) = substrides(V.parent, V.indices)
substrides(parent, I::Tuple) = substrides(parent, strides(parent), I)
substrides(parent, strds::Tuple{}, ::Tuple{}) = ()
substrides(parent, strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(parent, tail(strds), tail(I))...,)
substrides(parent, strds::NTuple{N,Int}, I::Tuple{WholeSlice, Vararg{Any}}) where N = (first(strds), substrides(parent, tail(strds), tail(I))...)
substrides(parent, strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(parent, tail(strds), tail(I))...)
substrides(parent, strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(parent, tail(strds), tail(I))...)
substrides(parent, strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))"))

Expand All @@ -278,7 +278,7 @@ compute_stride1(s, inds, I::Tuple{}) = s
compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) =
(@_inline_meta; compute_stride1(s*unsafe_length(inds[1]), tail(inds), tail(I)))
compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1])
compute_stride1(s, inds, I::Tuple{WholeSlice, Vararg{Any}}) = s
compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s
compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))"))

elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P)
Expand All @@ -305,7 +305,7 @@ compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange
# linear indexing always starts with 1.
compute_offset1(parent, stride1::Integer, I::Tuple) =
(@_inline_meta; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I))
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) =
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Union{Slice, IdentityUnitRange}}, I::Tuple) =
(@_inline_meta; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case
compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) =
(@_inline_meta; compute_linindex(parent, I) - stride1) # linear indexing starts with 1
Expand Down
2 changes: 1 addition & 1 deletion stdlib/LinearAlgebra/test/adjtrans.jl
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ using .Main.OffsetArrays

@testset "offset axes" begin
s = Base.Slice(-3:3)'
@test axes(s) === (Base.OneTo(1), Base.Slice(-3:3))
@test axes(s) === (Base.OneTo(1), Base.IdentityUnitRange(-3:3))
@test collect(LinearIndices(s)) == reshape(1:7, 1, 7)
@test collect(CartesianIndices(s)) == reshape([CartesianIndex(1,i) for i = -3:3], 1, 7)
@test s[1] == -3
Expand Down
2 changes: 1 addition & 1 deletion stdlib/SparseArrays/src/sparsematrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ size(S::SparseMatrixCSC) = (S.m, S.n)
# underlying SparseMatrixCSC
const SparseMatrixCSCView{Tv,Ti} =
SubArray{Tv,2,SparseMatrixCSC{Tv,Ti},
Tuple{Base.Slice{Base.OneTo{Int},true},I}} where {I<:AbstractUnitRange}
Tuple{Base.Slice{Base.OneTo{Int}},I}} where {I<:AbstractUnitRange}
const SparseMatrixCSCUnion{Tv,Ti} = Union{SparseMatrixCSC{Tv,Ti}, SparseMatrixCSCView{Tv,Ti}}

getcolptr(S::SparseMatrixCSC) = S.colptr
Expand Down
2 changes: 1 addition & 1 deletion stdlib/SparseArrays/src/sparsevector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ SparseVector(n::Integer, nzind::Vector{Ti}, nzval::Vector{Tv}) where {Tv,Ti} =

# Define an alias for a view of a whole column of a SparseMatrixCSC. Many methods can be written for the
# union of such a view and a SparseVector so we define an alias for such a union as well
const SparseColumnView{T} = SubArray{T,1,<:SparseMatrixCSC,Tuple{Base.Slice{Base.OneTo{Int},true},Int},false}
const SparseColumnView{T} = SubArray{T,1,<:SparseMatrixCSC,Tuple{Base.Slice{Base.OneTo{Int}},Int},false}
const SparseVectorUnion{T} = Union{SparseVector{T}, SparseColumnView{T}}

### Basic properties
Expand Down
2 changes: 1 addition & 1 deletion test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1729,7 +1729,7 @@ end
@test a[1,2] == 7
@test 2*CartesianIndex{3}(1,2,3) == CartesianIndex{3}(2,4,6)

R = CartesianIndices(map(Base.Slice, (2:5, 3:5)))
R = CartesianIndices(map(Base.IdentityUnitRange, (2:5, 3:5)))
@test eltype(R) <: CartesianIndex{2}
@test eltype(typeof(R)) <: CartesianIndex{2}
@test eltype(CartesianIndices{2}) <: CartesianIndex{2}
Expand Down
28 changes: 14 additions & 14 deletions test/offsetarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ for i = 1:9 @test A_3_3[i] == i end
@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.Slice, (0:1,3:4)))
@test eachindex(S) == CartesianIndices(axes(S)) == CartesianIndices(map(Base.IdentityUnitRange, (0:1,3:4)))

# LinearIndices
# issue 27986
Expand Down Expand Up @@ -122,13 +122,13 @@ S = view(A, :, 3)
@test S[0] == 1
@test S[1] == 2
@test_throws BoundsError S[2]
@test axes(S) === (Base.Slice(0:1),)
@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.Slice(3:4),)
@test axes(S) === (Base.IdentityUnitRange(3:4),)
S = view(A, 0:0, 4)
@test S == [3]
@test S[1] == 3
Expand All @@ -147,17 +147,17 @@ S = view(A, :, :)
@test S[0,4] == S[3] == 3
@test S[1,4] == S[4] == 4
@test_throws BoundsError S[1,1]
@test axes(S) === Base.Slice.((0:1, 3:4))
@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.Slice(-1: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.Slice(0:3)
@test axes(gv, 1) === Base.IdentityUnitRange(0:3)
@test collect(gv) == -1:2

# iteration
Expand Down Expand Up @@ -234,26 +234,26 @@ B = similar(A, (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.Slice.((-3:3, 1:4))
@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.Slice.((-3:3, 1:4))
@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.Slice(-4:-3),)
@test axes(b) === (Base.IdentityUnitRange(-4:-3),)
@test b[-4] == 2
@test b[-3] == 1
b = A0[1,i1]
@test axes(b) === (Base.Slice(-4:-3),)
@test axes(b) === (Base.IdentityUnitRange(-4:-3),)
@test b[-4] == 3
@test b[-3] == 1
v = view(A0, i1, 1)
@test axes(v) === (Base.Slice(-4:-3),)
@test axes(v) === (Base.IdentityUnitRange(-4:-3),)
v = view(A0, 1:1, i1)
@test axes(v) === (Base.OneTo(1), Base.Slice(-4:-3))
@test axes(v) === (Base.OneTo(1), Base.IdentityUnitRange(-4:-3))

# copyto! and fill!
a = OffsetArray{Int}(undef, (-3:-1,))
Expand Down Expand Up @@ -340,7 +340,7 @@ a = OffsetArray(a0, (-1,2,3,4,5))
v = OffsetArray(v0, (-3,))
@test lastindex(v) == 1
@test v ≈ v
@test axes(v') === (Base.OneTo(1),Base.Slice(-2:1))
@test axes(v') === (Base.OneTo(1),Base.IdentityUnitRange(-2:1))
@test parent(v) == collect(v)
rv = reverse(v)
@test axes(rv) == axes(v)
Expand All @@ -356,7 +356,7 @@ A = OffsetArray(rand(4,4), (-3,5))
@test lastindex(A, 1) == 1
@test lastindex(A, 2) == 9
@test A ≈ A
@test axes(A') === Base.Slice.((6:9, -2:1))
@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))
Expand Down
4 changes: 2 additions & 2 deletions test/reinterpretarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ let a = [0.1 0.2; 0.3 0.4], at = reshape([(i,i+1) for i = 1:2:8], 2, 2)
@test r[1,2] === reinterpret(Int64, v[1,2])
@test r[0,3] === reinterpret(Int64, v[0,3])
@test r[1,3] === reinterpret(Int64, v[1,3])
@test_throws ArgumentError("cannot reinterpret a `Float64` array to `UInt32` when the first axis is Base.Slice(0:1). Try reshaping first.") reinterpret(UInt32, v)
@test_throws ArgumentError("cannot reinterpret a `Float64` array to `UInt32` when the first axis is Base.IdentityUnitRange(0:1). Try reshaping first.") reinterpret(UInt32, v)
v = OffsetArray(a, (0, 1))
r = reinterpret(UInt32, v)
axsv = axes(v)
Expand All @@ -155,7 +155,7 @@ let a = [0.1 0.2; 0.3 0.4], at = reshape([(i,i+1) for i = 1:2:8], 2, 2)
offsetvt = (-2, 4)
vt = OffsetArray(at, offsetvt)
istr = string(Int)
@test_throws ArgumentError("cannot reinterpret a `Tuple{$istr,$istr}` array to `$istr` when the first axis is Base.Slice(-1:0). Try reshaping first.") reinterpret(Int, vt)
@test_throws ArgumentError("cannot reinterpret a `Tuple{$istr,$istr}` array to `$istr` when the first axis is Base.IdentityUnitRange(-1:0). Try reshaping first.") reinterpret(Int, vt)
vt = reshape(vt, 1:1, axes(vt)...)
r = reinterpret(Int, vt)
@test r == OffsetArray(reshape(1:8, 2, 2, 2), (0, offsetvt...))
Expand Down
8 changes: 4 additions & 4 deletions test/testhelpers/OffsetArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ Base.eachindex(::IndexLinear, A::OffsetVector) = axes(A, 1)
# Implementations of indices and axes1. Since bounds-checking is
# performance-critical and relies on indices, these are usually worth
# optimizing thoroughly.
@inline Base.axes(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? Base.Slice(axes(parent(A))[d] .+ A.offsets[d]) : Base.Slice(1:1)
@inline Base.axes(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? Base.IdentityUnitRange(axes(parent(A))[d] .+ A.offsets[d]) : Base.IdentityUnitRange(1:1)
@inline Base.axes(A::OffsetArray) = _indices(axes(parent(A)), A.offsets) # would rather use ntuple, but see #15276
@inline _indices(inds, offsets) = (Base.Slice(inds[1] .+ offsets[1]), _indices(tail(inds), tail(offsets))...)
@inline _indices(inds, offsets) = (Base.IdentityUnitRange(inds[1] .+ offsets[1]), _indices(tail(inds), tail(offsets))...)
_indices(::Tuple{}, ::Tuple{}) = ()
Base.axes1(A::OffsetArray{T,0}) where {T} = Base.Slice(1:1) # we only need to specialize this one
Base.axes1(A::OffsetArray{T,0}) where {T} = Base.IdentityUnitRange(1:1) # we only need to specialize this one

const OffsetAxis = Union{Integer, UnitRange, Base.Slice{<:UnitRange}, Base.OneTo}
const OffsetAxis = Union{Integer, UnitRange, Base.IdentityUnitRange{<:UnitRange}, Base.OneTo}
function Base.similar(A::OffsetArray, T::Type, dims::Dims)
B = similar(parent(A), T, dims)
end
Expand Down