Skip to content

Commit

Permalink
Merge pull request #10 from mcabbott/getindex
Browse files Browse the repository at this point in the history
  • Loading branch information
Vexatos authored Jan 3, 2021
2 parents fe49430 + a4d79af commit 12b733b
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 9 deletions.
35 changes: 26 additions & 9 deletions src/CircularArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export CircularArray, CircularVector
`N`-dimensional array backed by an `AbstractArray{T, N}` of type `A` with fixed size and circular indexing.
array[index] == array[mod1(index, size)]
array[index...] == array[mod1.(index, size)...]
"""
struct CircularArray{T, N, A} <: AbstractArray{T, N}
data::A
Expand All @@ -23,24 +23,33 @@ end
One-dimensional array backed by an `AbstractArray{T, 1}` of type `A` with fixed size and circular indexing.
Alias for [`CircularArray{T,1,A}`](@ref).
array[index] == array[mod1(index, size)]
array[index] == array[mod1(index, length)]
"""
const CircularVector{T} = CircularArray{T, 1}

@inline clamp_bounds(arr::CircularArray, I::Tuple{Vararg{Int}})::AbstractArray{Int, 1} = map(Base.splat(mod), zip(I, axes(arr.data)))

CircularArray(data::AbstractArray{T,N}) where {T,N} = CircularArray{T,N}(data)
CircularArray{T}(data::AbstractArray{T,N}) where {T,N} = CircularArray{T,N}(data)
CircularArray(def::T, size) where T = CircularArray(fill(def, size))

@inline Base.getindex(arr::CircularArray, i::Int) = @inbounds getindex(arr.data, mod(i, Base.axes1(arr.data)))
@inline Base.setindex!(arr::CircularArray, v, i::Int) = @inbounds setindex!(arr.data, v, mod(i, Base.axes1(arr.data)))
@inline Base.getindex(arr::CircularArray, I::Vararg{Int}) = @inbounds getindex(arr.data, clamp_bounds(arr, I)...)
@inline Base.setindex!(arr::CircularArray, v, I::Vararg{Int}) = @inbounds setindex!(arr.data, v, clamp_bounds(arr, I)...)
@inline Base.getindex(arr::CircularArray, i::Int) =
@inbounds getindex(arr.data, mod1(i, length(arr.data)))
@inline Base.getindex(arr::CircularArray{T,N}, I::Vararg{<:Int,N}) where {T,N} =
@inbounds getindex(arr.data, map(mod, I, axes(arr.data))...)

@inline Base.setindex!(arr::CircularArray, v, i::Int) =
@inbounds setindex!(arr.data, v, mod1(i, length(arr.data)))
@inline Base.setindex!(arr::CircularArray{T,N}, v, I::Vararg{<:Int,N}) where {T,N} =
@inbounds setindex!(arr.data, v, map(mod, I, axes(arr.data))...)

@inline Base.size(arr::CircularArray) = size(arr.data)
@inline Base.axes(arr::CircularArray) = axes(arr.data)
Base.parent(arr::CircularArray) = arr.data

@inline Base.checkbounds(::CircularArray, _...) = nothing
@inline function Base.checkbounds(arr::CircularArray, I...)
J = Base.to_indices(arr, I)
length(J) == 1 || length(J) >= ndims(arr) || throw(BoundsError(arr, I))
nothing
end

@inline _similar(arr::CircularArray, ::Type{T}, dims) where T = CircularArray(similar(arr.data,T,dims))
@inline Base.similar(arr::CircularArray, ::Type{T}, dims::Tuple{Base.DimOrInd, Vararg{Base.DimOrInd}}) where T = _similar(arr,T,dims)
Expand All @@ -50,6 +59,14 @@ CircularArray(def::T, size) where T = CircularArray(fill(def, size))
CircularVector(data::AbstractArray{T, 1}) where T = CircularVector{T}(data)
CircularVector(def::T, size::Int) where T = CircularVector{T}(fill(def, size))

Base.IndexStyle(::Type{CircularArray{T,N,A}}) where {T,N,A} = IndexCartesian()
Base.IndexStyle(::Type{<:CircularVector}) = IndexLinear()

function Base.showarg(io::IO, arr::CircularArray, toplevel)
print(io, ndims(arr) == 1 ? "CircularVector(" : "CircularArray(")
Base.showarg(io, parent(arr), false)
print(io, ')')
# toplevel && print(io, " with eltype ", eltype(arr))
end

end
37 changes: 37 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ end
@test !isa(v1, AbstractVector{String})
@test v1[2] == v1[2 + length(v1)]

@test IndexStyle(v1) == IndexLinear()
@test v1[0] == data[end]
@test v1[-4:10] == [data; data; data]
@test v1[-3:1][-1] == data[end]
Expand All @@ -65,11 +66,25 @@ end
a1 = CircularArray(b_arr)
@test size(a1) == (3, 4)
@test a1[2, 3] == 14
@test a1[2, Int32(3)] == 14
a1[2, 3] = 17
@test a1[2, 3] == 17
@test a1[-1, 7] == 17
@test a1[CartesianIndex(-1, 7)] == 17
@test a1[-1:5, 4:10][1, 4] == 17
@test a1[:, -1:-1][2, 1] == 17
a1[CartesianIndex(-2, 7)] = 99
@test a1[1, 3] == 99

@test IndexStyle(a1) == IndexCartesian()
@test a1[3] == a1[3,1]
@test a1[Int32(4)] == a1[1,2]
@test a1[-1] == a1[length(a1)-1]

@test a1[2, 3, 1] == 17 # trailing index
@test a1[2, 3, 99] == 17
@test a1[2, 3, :] == [17]

@test !isa(a1, CircularVector)
@test !isa(a1, AbstractVector)
@test isa(a1, AbstractArray)
Expand All @@ -80,6 +95,28 @@ end
@test isa(a2, CircularArray{Int, 2})
end

@testset "3-array" begin
t3 = collect(reshape('a':'x', 2,3,4))
c3 = CircularArray(t3)

@test c3[1,3,3] == c3[3,3,3] == c3[3,3,7] == c3[3,3,7,1]

c3[3,3,7] = 'Z'
@test t3[1,3,3] == 'Z'

@test c3[3, CartesianIndex(3,7)] == 'Z'
c3[Int32(3), CartesianIndex(3,7)] = 'ζ'
@test t3[1,3,3] == 'ζ'

@test vec(c3[:, [CartesianIndex()], 1, 5]) == vec(t3[:, 1, 1])

@test IndexStyle(c3) == IndexCartesian()
@test c3[-1] == t3[length(t3)-1]

@test_throws BoundsError c3[2,3] # too few indices
@test_throws BoundsError c3[CartesianIndex(2,3)]
end

@testset "offset indices" begin
i = OffsetArray(1:5,-3)
a = CircularArray(i)
Expand Down

0 comments on commit 12b733b

Please sign in to comment.