diff --git a/src/CircularArrays.jl b/src/CircularArrays.jl index f1495ff..66a4a63 100644 --- a/src/CircularArrays.jl +++ b/src/CircularArrays.jl @@ -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 @@ -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) @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index acb10af..5eadee8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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] @@ -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) @@ -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)