diff --git a/src/OffsetArrays.jl b/src/OffsetArrays.jl index 0e5c19ee..195697f3 100644 --- a/src/OffsetArrays.jl +++ b/src/OffsetArrays.jl @@ -47,6 +47,7 @@ OffsetVector{T}(init::ArrayInitializer, inds::AbstractUnitRange) where {T} = Off # OffsetMatrix constructors OffsetMatrix(A::AbstractMatrix, offset1, offset2) = OffsetArray(A, offset1, offset2) +OffsetMatrix(A::AbstractMatrix, I::CartesianIndices{2}) = OffsetArray(A, I) OffsetMatrix{T}(init::ArrayInitializer, inds1::AbstractUnitRange, inds2::AbstractUnitRange) where {T} = OffsetArray{T}(init, inds1, inds2) """ @@ -82,12 +83,63 @@ uncolonindices(ax::Tuple, inds::Tuple) = (first(inds), uncolonindices(tail(ax), uncolonindices(ax::Tuple, inds::Tuple{Colon, Vararg{Any}}) = (first(ax), uncolonindices(tail(ax), tail(inds))...) uncolonindices(::Tuple{}, ::Tuple{}) = () -function OffsetArray(A::AbstractArray{T,N}, inds::NTuple{N,Union{AbstractUnitRange, Colon}}) where {T,N} +function OffsetArray(A::AbstractArray{T,N}, inds::NTuple{N,Union{AbstractUnitRange, CartesianIndices{1}, Colon}}) where {T,N} OffsetArray(A, uncolonindices(A, inds)) end -OffsetArray(A::AbstractArray{T,N}, inds::Vararg{Union{AbstractUnitRange, Colon},N}) where {T,N} = +OffsetArray(A::AbstractArray{T,N}, inds::Vararg{Union{AbstractUnitRange, CartesianIndices{1}, Colon},N}) where {T,N} = OffsetArray(A, uncolonindices(A, inds)) +# Specify offsets using CartesianIndices (issue #71) +# Support a mix of AbstractUnitRanges and CartesianIndices{1} +# Extract the range r from CartesianIndices((r,)) +function stripCartesianIndices(inds::Tuple{CartesianIndices{1},Vararg{Any}}) + I = first(inds) + Ir = convert(Tuple{AbstractUnitRange{Int}}, I) |> first + (Ir, stripCartesianIndices(tail(inds))...) +end +stripCartesianIndices(inds::Tuple)= (first(inds), stripCartesianIndices(tail(inds))...) +stripCartesianIndices(::Tuple{}) = () + +OffsetArray(A::AbstractArray{<:Any,N}, inds::NTuple{N,Union{CartesianIndices{1}, AbstractUnitRange}}) where {N} = + OffsetArray(A, stripCartesianIndices(inds)) +OffsetArray(A::AbstractArray{<:Any,N}, inds::Vararg{Union{CartesianIndices{1}, AbstractUnitRange},N}) where {N} = + OffsetArray(A, inds) + +# Support an arbitrary CartesianIndices alongside colons and ranges +# The total number of indices should equal ndims(arr) +# We split the CartesianIndices{N} into N CartesianIndices{1} indices to facilitate dispatch +splitCartesianIndices(c::CartesianIndices{0}) = () +function splitCartesianIndices(c::CartesianIndices) + c1, ct = Base.IteratorsMD.split(c, Val(1)) + (c1, splitCartesianIndices(ct)...) +end +function splitCartesianIndices(t::Tuple{CartesianIndices, Vararg{Any}}) + (splitCartesianIndices(first(t))..., splitCartesianIndices(tail(t))...) +end +function splitCartesianIndices(t::Tuple) + (first(t), splitCartesianIndices(tail(t))...) +end +splitCartesianIndices(::Tuple{}) = () + +function OffsetArray(A::AbstractArray, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}}}) + OffsetArray(A, splitCartesianIndices(inds)) +end +function OffsetArray(A::AbstractArray, inds::Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}}) + OffsetArray(A, inds) +end + +# Add methods to initialize OffsetArrays using CartesianIndices (issue #71) +function OffsetArray{T,N}(init::ArrayInitializer, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}}}) where {T,N} + indsN = stripCartesianIndices(splitCartesianIndices(inds)) + OffsetArray{T,N}(init, indsN) +end +function OffsetArray{T}(init::ArrayInitializer, inds::Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}}}) where {T} + indsN = stripCartesianIndices(splitCartesianIndices(inds)) + OffsetArray{T}(init, indsN) +end +OffsetArray{T,N}(init::ArrayInitializer, inds::Vararg{Union{AbstractUnitRange, CartesianIndices}}) where {T,N} = OffsetArray{T,N}(init, inds) +OffsetArray{T}(init::ArrayInitializer, inds::Vararg{Union{AbstractUnitRange, CartesianIndices}}) where {T} = OffsetArray{T}(init, inds) + # avoid a level of indirection when nesting OffsetArrays function OffsetArray(A::OffsetArray, offsets::NTuple{N,Int}) where {N} OffsetArray(parent(A), offsets .+ A.offsets) diff --git a/test/runtests.jl b/test/runtests.jl index ebc8e216..d1cec2e2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -110,6 +110,15 @@ end @test OffsetVector(v, -2:2) == OffsetArray(v, -2:2) @test typeof(OffsetVector{Float64}(undef, -2:2)) == typeof(OffsetArray{Float64}(undef, -2:2)) + # Issue #71 + ov = OffsetVector(v, -2:2) + for T in [OffsetVector, OffsetArray] + if VERSION >= v"1.1" + @test ov == T(v, CartesianIndex(-2):CartesianIndex(2)) + end + @test ov == T(v, CartesianIndices((-2:2,))) + end + @test OffsetVector(v, :) == OffsetArray(v, (:,)) == OffsetArray(v, :) == OffsetArray(v, axes(v)) @test axes(OffsetVector(v, :)) == axes(v) @@ -124,6 +133,18 @@ end @test OffsetMatrix(v, -2:2, -1:1) == OffsetArray(v, -2:2, -1:1) @test typeof(OffsetMatrix{Float64}(undef, -2:2, -1:1)) == typeof(OffsetArray{Float64}(undef, -2:2, -1:1)) + # Issue #71 + m = OffsetMatrix(v, -2:2, -1:1) + for T in [OffsetMatrix, OffsetArray] + if VERSION >= v"1.1" + @test m == T(v, CartesianIndex(-2, -1):CartesianIndex(2, 1)) + @test m == T(v, CartesianIndex(-2):CartesianIndex(2), CartesianIndex(-1):CartesianIndex(1)) + @test m == T(v, -2:2, CartesianIndex(-1):CartesianIndex(1)) + @test m == T(v, CartesianIndex(-2):CartesianIndex(2), -1:1) + end + @test m == T(v, CartesianIndices((-2:2, -1:1))) + end + @test OffsetMatrix(v, :, :) == OffsetArray(v, (:, :)) == OffsetArray(v, :, :) == OffsetArray(v, axes(v)) @test OffsetMatrix(v, :, 2:4) == OffsetArray(v, axes(v,1), 2:4) @test OffsetMatrix(v, 3:7, :) == OffsetArray(v, 3:7, axes(v,2)) @@ -134,6 +155,28 @@ end @test OffsetMatrix(w, :, 2:3) == OffsetArray(w, axes(w,1), 2:3) @test OffsetMatrix(w, 0:1, :) == OffsetArray(w, 0:1, axes(w,2)) @test axes(OffsetArray(w, :, :)) == axes(w) + + @test axes(OffsetMatrix(w, :, CartesianIndices((0:1,)))) == (3:4, 0:1) + @test axes(OffsetMatrix(w, CartesianIndices((0:1,)), :)) == (0:1, 5:6) +end + +@testset "construct OffsetArray with CartesianIndices" begin + a = rand(2, 2, 2) + oa = OffsetArray(a, 0:1, 3:4, 2:3) + @test OffsetArray(a, CartesianIndices(axes(oa))) == oa + @test axes(OffsetArray(a, :, CartesianIndices((3:4, 2:3)))) == (1:2, 3:4, 2:3) + @test axes(OffsetArray(a, 10:11, CartesianIndices((3:4, 2:3)) )) == (10:11, 3:4, 2:3) + @test axes(OffsetArray(a, CartesianIndices((3:4, 2:3)), :)) == (3:4, 2:3, 1:2) + @test axes(OffsetArray(a, CartesianIndices((3:4, 2:3)), 10:11)) == (3:4, 2:3, 10:11) + @test axes(OffsetArray(a, :, :, CartesianIndices((3:4,)) )) == (1:2, 1:2, 3:4) + @test axes(OffsetArray(a, 10:11, :, CartesianIndices((3:4,)) )) == (10:11, 1:2, 3:4) + @test axes(OffsetArray(a, 10:11, 2:3, CartesianIndices((3:4,)) )) == (10:11, 2:3, 3:4) + + # ignore empty CartesianIndices + @test OffsetArray(a, CartesianIndices(()), 0:1, :, 2:3) == OffsetArray(a, 0:1, :, 2:3) + @test OffsetArray(a, 0:1, CartesianIndices(()), :, 2:3) == OffsetArray(a, 0:1, :, 2:3) + @test OffsetArray(a, 0:1, :, CartesianIndices(()), 2:3) == OffsetArray(a, 0:1, :, 2:3) + @test OffsetArray(a, 0:1, :, 2:3, CartesianIndices(())) == OffsetArray(a, 0:1, :, 2:3) end @testset "undef, missing, and nothing constructors" begin @@ -151,6 +194,25 @@ end @test !isassigned(OffsetVector{Union{T,Vector{Int}}}(undef, -1:1), -1) @test OffsetVector{Union{T,Vector{Int}}}(t, -1:1)[-1] === t end + + oa = OffsetArray{Float64,2}(undef, CartesianIndices((1:2, 3:4))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 + oa = OffsetArray{Float64,2}(undef, 1:2, CartesianIndices((3:4,))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 + oa = OffsetArray{Float64,2}(undef, (1:2, CartesianIndices((3:4,)))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 + oa = OffsetArray{Float64}(undef, CartesianIndices((1:2, 3:4))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 + oa = OffsetArray{Float64}(undef, 1:2, CartesianIndices((3:4,))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 + oa = OffsetArray{Float64}(undef, (1:2, CartesianIndices((3:4,)))) + @test axes(oa) == (1:2, 3:4) + @test eltype(oa) == Float64 end @testset "Offset range construction" begin