Skip to content

Commit 55af8ad

Browse files
authored
Add constructors for CartesianIndices (#142)
* Add constructors for CartesianIndices. A combination of `AbstractUnitRange`s, `Colon` and `CartesianIndices` may be provided to the constructor such that the total number of ranges is equal to the dimension of the parent array. * Use `convert` instead of accessing indices, leads to the same lowered code but is future-proof
1 parent d58dda0 commit 55af8ad

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

src/OffsetArrays.jl

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ OffsetVector{T}(init::ArrayInitializer, inds::AbstractUnitRange) where {T} = Off
4747

4848
# OffsetMatrix constructors
4949
OffsetMatrix(A::AbstractMatrix, offset1, offset2) = OffsetArray(A, offset1, offset2)
50+
OffsetMatrix(A::AbstractMatrix, I::CartesianIndices{2}) = OffsetArray(A, I)
5051
OffsetMatrix{T}(init::ArrayInitializer, inds1::AbstractUnitRange, inds2::AbstractUnitRange) where {T} = OffsetArray{T}(init, inds1, inds2)
5152

5253
"""
@@ -82,12 +83,63 @@ uncolonindices(ax::Tuple, inds::Tuple) = (first(inds), uncolonindices(tail(ax),
8283
uncolonindices(ax::Tuple, inds::Tuple{Colon, Vararg{Any}}) = (first(ax), uncolonindices(tail(ax), tail(inds))...)
8384
uncolonindices(::Tuple{}, ::Tuple{}) = ()
8485

85-
function OffsetArray(A::AbstractArray{T,N}, inds::NTuple{N,Union{AbstractUnitRange, Colon}}) where {T,N}
86+
function OffsetArray(A::AbstractArray{T,N}, inds::NTuple{N,Union{AbstractUnitRange, CartesianIndices{1}, Colon}}) where {T,N}
8687
OffsetArray(A, uncolonindices(A, inds))
8788
end
88-
OffsetArray(A::AbstractArray{T,N}, inds::Vararg{Union{AbstractUnitRange, Colon},N}) where {T,N} =
89+
OffsetArray(A::AbstractArray{T,N}, inds::Vararg{Union{AbstractUnitRange, CartesianIndices{1}, Colon},N}) where {T,N} =
8990
OffsetArray(A, uncolonindices(A, inds))
9091

92+
# Specify offsets using CartesianIndices (issue #71)
93+
# Support a mix of AbstractUnitRanges and CartesianIndices{1}
94+
# Extract the range r from CartesianIndices((r,))
95+
function stripCartesianIndices(inds::Tuple{CartesianIndices{1},Vararg{Any}})
96+
I = first(inds)
97+
Ir = convert(Tuple{AbstractUnitRange{Int}}, I) |> first
98+
(Ir, stripCartesianIndices(tail(inds))...)
99+
end
100+
stripCartesianIndices(inds::Tuple)= (first(inds), stripCartesianIndices(tail(inds))...)
101+
stripCartesianIndices(::Tuple{}) = ()
102+
103+
OffsetArray(A::AbstractArray{<:Any,N}, inds::NTuple{N,Union{CartesianIndices{1}, AbstractUnitRange}}) where {N} =
104+
OffsetArray(A, stripCartesianIndices(inds))
105+
OffsetArray(A::AbstractArray{<:Any,N}, inds::Vararg{Union{CartesianIndices{1}, AbstractUnitRange},N}) where {N} =
106+
OffsetArray(A, inds)
107+
108+
# Support an arbitrary CartesianIndices alongside colons and ranges
109+
# The total number of indices should equal ndims(arr)
110+
# We split the CartesianIndices{N} into N CartesianIndices{1} indices to facilitate dispatch
111+
splitCartesianIndices(c::CartesianIndices{0}) = ()
112+
function splitCartesianIndices(c::CartesianIndices)
113+
c1, ct = Base.IteratorsMD.split(c, Val(1))
114+
(c1, splitCartesianIndices(ct)...)
115+
end
116+
function splitCartesianIndices(t::Tuple{CartesianIndices, Vararg{Any}})
117+
(splitCartesianIndices(first(t))..., splitCartesianIndices(tail(t))...)
118+
end
119+
function splitCartesianIndices(t::Tuple)
120+
(first(t), splitCartesianIndices(tail(t))...)
121+
end
122+
splitCartesianIndices(::Tuple{}) = ()
123+
124+
function OffsetArray(A::AbstractArray, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}}})
125+
OffsetArray(A, splitCartesianIndices(inds))
126+
end
127+
function OffsetArray(A::AbstractArray, inds::Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}})
128+
OffsetArray(A, inds)
129+
end
130+
131+
# Add methods to initialize OffsetArrays using CartesianIndices (issue #71)
132+
function OffsetArray{T,N}(init::ArrayInitializer, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}}}) where {T,N}
133+
indsN = stripCartesianIndices(splitCartesianIndices(inds))
134+
OffsetArray{T,N}(init, indsN)
135+
end
136+
function OffsetArray{T}(init::ArrayInitializer, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}}}) where {T}
137+
indsN = stripCartesianIndices(splitCartesianIndices(inds))
138+
OffsetArray{T}(init, indsN)
139+
end
140+
OffsetArray{T,N}(init::ArrayInitializer, inds::Vararg{Union{AbstractUnitRange, CartesianIndices}}) where {T,N} = OffsetArray{T,N}(init, inds)
141+
OffsetArray{T}(init::ArrayInitializer, inds::Vararg{Union{AbstractUnitRange, CartesianIndices}}) where {T} = OffsetArray{T}(init, inds)
142+
91143
# avoid a level of indirection when nesting OffsetArrays
92144
function OffsetArray(A::OffsetArray, offsets::NTuple{N,Int}) where {N}
93145
OffsetArray(parent(A), offsets .+ A.offsets)

test/runtests.jl

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ end
110110
@test OffsetVector(v, -2:2) == OffsetArray(v, -2:2)
111111
@test typeof(OffsetVector{Float64}(undef, -2:2)) == typeof(OffsetArray{Float64}(undef, -2:2))
112112

113+
# Issue #71
114+
ov = OffsetVector(v, -2:2)
115+
for T in [OffsetVector, OffsetArray]
116+
if VERSION >= v"1.1"
117+
@test ov == T(v, CartesianIndex(-2):CartesianIndex(2))
118+
end
119+
@test ov == T(v, CartesianIndices((-2:2,)))
120+
end
121+
113122
@test OffsetVector(v, :) == OffsetArray(v, (:,)) == OffsetArray(v, :) == OffsetArray(v, axes(v))
114123
@test axes(OffsetVector(v, :)) == axes(v)
115124

@@ -124,6 +133,18 @@ end
124133
@test OffsetMatrix(v, -2:2, -1:1) == OffsetArray(v, -2:2, -1:1)
125134
@test typeof(OffsetMatrix{Float64}(undef, -2:2, -1:1)) == typeof(OffsetArray{Float64}(undef, -2:2, -1:1))
126135

136+
# Issue #71
137+
m = OffsetMatrix(v, -2:2, -1:1)
138+
for T in [OffsetMatrix, OffsetArray]
139+
if VERSION >= v"1.1"
140+
@test m == T(v, CartesianIndex(-2, -1):CartesianIndex(2, 1))
141+
@test m == T(v, CartesianIndex(-2):CartesianIndex(2), CartesianIndex(-1):CartesianIndex(1))
142+
@test m == T(v, -2:2, CartesianIndex(-1):CartesianIndex(1))
143+
@test m == T(v, CartesianIndex(-2):CartesianIndex(2), -1:1)
144+
end
145+
@test m == T(v, CartesianIndices((-2:2, -1:1)))
146+
end
147+
127148
@test OffsetMatrix(v, :, :) == OffsetArray(v, (:, :)) == OffsetArray(v, :, :) == OffsetArray(v, axes(v))
128149
@test OffsetMatrix(v, :, 2:4) == OffsetArray(v, axes(v,1), 2:4)
129150
@test OffsetMatrix(v, 3:7, :) == OffsetArray(v, 3:7, axes(v,2))
@@ -134,6 +155,28 @@ end
134155
@test OffsetMatrix(w, :, 2:3) == OffsetArray(w, axes(w,1), 2:3)
135156
@test OffsetMatrix(w, 0:1, :) == OffsetArray(w, 0:1, axes(w,2))
136157
@test axes(OffsetArray(w, :, :)) == axes(w)
158+
159+
@test axes(OffsetMatrix(w, :, CartesianIndices((0:1,)))) == (3:4, 0:1)
160+
@test axes(OffsetMatrix(w, CartesianIndices((0:1,)), :)) == (0:1, 5:6)
161+
end
162+
163+
@testset "construct OffsetArray with CartesianIndices" begin
164+
a = rand(2, 2, 2)
165+
oa = OffsetArray(a, 0:1, 3:4, 2:3)
166+
@test OffsetArray(a, CartesianIndices(axes(oa))) == oa
167+
@test axes(OffsetArray(a, :, CartesianIndices((3:4, 2:3)))) == (1:2, 3:4, 2:3)
168+
@test axes(OffsetArray(a, 10:11, CartesianIndices((3:4, 2:3)) )) == (10:11, 3:4, 2:3)
169+
@test axes(OffsetArray(a, CartesianIndices((3:4, 2:3)), :)) == (3:4, 2:3, 1:2)
170+
@test axes(OffsetArray(a, CartesianIndices((3:4, 2:3)), 10:11)) == (3:4, 2:3, 10:11)
171+
@test axes(OffsetArray(a, :, :, CartesianIndices((3:4,)) )) == (1:2, 1:2, 3:4)
172+
@test axes(OffsetArray(a, 10:11, :, CartesianIndices((3:4,)) )) == (10:11, 1:2, 3:4)
173+
@test axes(OffsetArray(a, 10:11, 2:3, CartesianIndices((3:4,)) )) == (10:11, 2:3, 3:4)
174+
175+
# ignore empty CartesianIndices
176+
@test OffsetArray(a, CartesianIndices(()), 0:1, :, 2:3) == OffsetArray(a, 0:1, :, 2:3)
177+
@test OffsetArray(a, 0:1, CartesianIndices(()), :, 2:3) == OffsetArray(a, 0:1, :, 2:3)
178+
@test OffsetArray(a, 0:1, :, CartesianIndices(()), 2:3) == OffsetArray(a, 0:1, :, 2:3)
179+
@test OffsetArray(a, 0:1, :, 2:3, CartesianIndices(())) == OffsetArray(a, 0:1, :, 2:3)
137180
end
138181

139182
@testset "undef, missing, and nothing constructors" begin
@@ -151,6 +194,25 @@ end
151194
@test !isassigned(OffsetVector{Union{T,Vector{Int}}}(undef, -1:1), -1)
152195
@test OffsetVector{Union{T,Vector{Int}}}(t, -1:1)[-1] === t
153196
end
197+
198+
oa = OffsetArray{Float64,2}(undef, CartesianIndices((1:2, 3:4)))
199+
@test axes(oa) == (1:2, 3:4)
200+
@test eltype(oa) == Float64
201+
oa = OffsetArray{Float64,2}(undef, 1:2, CartesianIndices((3:4,)))
202+
@test axes(oa) == (1:2, 3:4)
203+
@test eltype(oa) == Float64
204+
oa = OffsetArray{Float64,2}(undef, (1:2, CartesianIndices((3:4,))))
205+
@test axes(oa) == (1:2, 3:4)
206+
@test eltype(oa) == Float64
207+
oa = OffsetArray{Float64}(undef, CartesianIndices((1:2, 3:4)))
208+
@test axes(oa) == (1:2, 3:4)
209+
@test eltype(oa) == Float64
210+
oa = OffsetArray{Float64}(undef, 1:2, CartesianIndices((3:4,)))
211+
@test axes(oa) == (1:2, 3:4)
212+
@test eltype(oa) == Float64
213+
oa = OffsetArray{Float64}(undef, (1:2, CartesianIndices((3:4,))))
214+
@test axes(oa) == (1:2, 3:4)
215+
@test eltype(oa) == Float64
154216
end
155217

156218
@testset "Offset range construction" begin

0 commit comments

Comments
 (0)