Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
72 changes: 61 additions & 11 deletions src/OffsetArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

"""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -247,21 +299,19 @@ indexlength(r::AbstractRange) = length(r)
indexlength(i::Integer) = i
indexlength(i::Colon) = Colon()

# These functions keep the summary compact
function Base.inds2string(inds::Tuple{Vararg{Union{IdOffsetRange, IdentityUnitRange{<:IdOffsetRange}}}})
Base.inds2string(map(UnitRange, inds))
end
Base.showindices(io::IO, ind1::IdOffsetRange, inds::IdOffsetRange...) = Base.showindices(io, map(UnitRange, (ind1, inds...))...)

function Base.showarg(io::IO, a::OffsetArray, toplevel)
print(io, "OffsetArray(")
Base.showarg(io, parent(a), false)
if ndims(a) > 0
print(io, ", ")
printindices(io, axes(a)...)
end
Base.showindices(io, axes(a)...)
print(io, ')')
toplevel && print(io, " with eltype ", eltype(a))
end
printindices(io::IO, ind1, inds...) =
(print(io, _unslice(ind1), ", "); printindices(io, inds...))
printindices(io::IO, ind1) = print(io, _unslice(ind1))
_unslice(x) = x
_unslice(x::IdentityUnitRange) = x.indices

function Base.replace_in_print_matrix(A::OffsetArray{<:Any,2}, i::Integer, j::Integer, s::AbstractString)
J = map(parentindex, axes(A), (i,j))
Expand Down
2 changes: 1 addition & 1 deletion src/axes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Broadcast.broadcasted(::Base.Broadcast.DefaultArrayStyle{1}, ::typeof(+), r::IdO
Broadcast.broadcasted(::Base.Broadcast.DefaultArrayStyle{1}, ::typeof(+), x::Integer, r::IdOffsetRange{T}) where T =
IdOffsetRange{T}(x .+ r.parent, r.offset)

Base.show(io::IO, r::IdOffsetRange) = print(io, first(r), ':', last(r))
Base.show(io::IO, r::IdOffsetRange) = print(io, "OffsetArrays.IdOffsetRange(",first(r), ':', last(r),")")

# Optimizations
@inline Base.checkindex(::Type{Bool}, inds::IdOffsetRange, i::Real) = Base.checkindex(Bool, inds.parent, i - inds.offset)
Expand Down
81 changes: 80 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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))
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -440,15 +502,32 @@ end
a = OffsetArray(reshape([1]))
@test summary(a) == "0-dimensional OffsetArray(::$(typeof(parent(a)))) with eltype $(Int)"

a = OffsetArray([1 2; 3 4], -1:0, 5:6)
io = IOBuffer()
show(io, axes(a, 1))
@test String(take!(io)) == "OffsetArrays.IdOffsetRange(-1:0)"
show(io, axes(a, 2))
@test String(take!(io)) == "OffsetArrays.IdOffsetRange(5:6)"

@test Base.inds2string(axes(a)) == Base.inds2string(map(UnitRange, axes(a)))

show(io, OffsetArray(3:5, 0:2))
@test String(take!(io)) == "3:5 with indices 0:2"

d = Diagonal([1,2,3])
Base.print_array(io, d)
s1 = String(take!(io))
Base.print_array(io, OffsetArray(d, -1:1, 3:5))
od = OffsetArray(d, -1:1, 3:5)
Base.print_array(io, od)
s2 = String(take!(io))
@test s1 == s2

@test Base.replace_in_print_matrix(od, -1, 3, " ") == Base.replace_in_print_matrix(d, 1, 1, " ")
@test Base.replace_in_print_matrix(od, -1, 4, " ") == Base.replace_in_print_matrix(d, 1, 2, " ")

v = rand(3)
ov = OffsetArray(v, (-2,))
@test Base.replace_in_print_matrix(ov, -1, 1, " ") == Base.replace_in_print_matrix(v, 1, 1, " ")
end

@testset "readdlm/writedlm" begin
Expand Down