From cdd85278abafc9ccc8615a62107cf3097a31f574 Mon Sep 17 00:00:00 2001 From: AFeuerpfeil Date: Tue, 31 Mar 2026 17:15:05 -0400 Subject: [PATCH 1/2] fix: replace ArrayStyle with PeriodicArrayStyle for correct broadcast dispatch The old `ArrayStyle{PeriodicArray{T,N,A,F}}` approach broke on scalar-first broadcasts and mixed-type PA operations because `bc.args[1].map` assumed a specific arg layout and `ArrayStyle` erased the array type info needed for style promotion. Introducing `PeriodicArrayStyle{N} <: AbstractArrayStyle{N}` with explicit promotion rules and a helper `_find_pa` to recover the map from any broadcast tree fixes these cases. Bump version to 1.1.1. Co-Authored-By: Claude Sonnet 4.6 --- Project.toml | 2 +- src/PeriodicArrays.jl | 33 +++++++++++++++++++++----------- test/test_basics.jl | 15 ++++++++++++++- test/test_nontrivial_boundary.jl | 11 ++++++++++- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index abbdebb..aa5fad9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PeriodicArrays" uuid = "343d6138-6384-4525-8bee-38906309ab36" authors = ["Andreas Feuerpfeil "] -version = "1.1.0" +version = "1.1.1" [compat] julia = "1.10" diff --git a/src/PeriodicArrays.jl b/src/PeriodicArrays.jl index 14891d0..41b5710 100644 --- a/src/PeriodicArrays.jl +++ b/src/PeriodicArrays.jl @@ -181,18 +181,29 @@ end return _similar(arr, T, dims) end -@inline function Broadcast.BroadcastStyle( - ::Type{PeriodicArray{T, N, A, F}} - ) where {T, N, A, F} - return Broadcast.ArrayStyle{PeriodicArray{T, N, A, F}}() -end +struct PeriodicArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end +PeriodicArrayStyle{N}(::Val{M}) where {N,M} = PeriodicArrayStyle{M}() + +Broadcast.BroadcastStyle(::Type{<:PeriodicArray{T,N}}) where {T,N} = PeriodicArrayStyle{N}() +Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::PeriodicArrayStyle{N}) where {M,N} = PeriodicArrayStyle{max(M,N)}() +Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::Broadcast.DefaultArrayStyle{N}) where {M,N} = PeriodicArrayStyle{max(M,N)}() +Broadcast.BroadcastStyle(::Broadcast.DefaultArrayStyle{N}, ::PeriodicArrayStyle{M}) where {N,M} = PeriodicArrayStyle{max(N,M)}() + +_find_pa(bc::Broadcast.Broadcasted) = _find_pa(bc.args...) +_find_pa(a::Broadcast.Extruded, rest...) = _find_pa(a.x, rest...) +_find_pa() = nothing +_find_pa(a::PeriodicArray, rest...) = a +_find_pa(a::Broadcast.Broadcasted, rest...) = + let r = _find_pa(a) + r !== nothing ? r : _find_pa(rest...) + end +_find_pa(::Any, rest...) = _find_pa(rest...) + @inline function Base.similar( - bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{PeriodicArray{T, N, A, F}}}, ::Type{ElType} - ) where {T, N, A, F, ElType} - return PeriodicArray( - similar(convert(Broadcast.Broadcasted{typeof(Broadcast.BroadcastStyle(A))}, bc), ElType), - bc.args[1].map - ) + bc::Broadcast.Broadcasted{PeriodicArrayStyle{N}}, ::Type{ElType} + ) where {N, ElType} + pa = _find_pa(bc) + return PeriodicArray(similar(Array{ElType, N}, axes(bc)), pa.map) end @inline Base.dataids(arr::PeriodicArray) = Base.dataids(parent(arr)) diff --git a/test/test_basics.jl b/test/test_basics.jl index eb449e7..3179c7c 100644 --- a/test/test_basics.jl +++ b/test/test_basics.jl @@ -206,8 +206,21 @@ end @test v4 == PeriodicArray([2, 3, 4, 5]) v5 = v4 .> 3 - @test v5 isa PeriodicVector{Bool, BitVector} + @test v5 isa PeriodicVector{Bool, Vector{Bool}} @test v5 == PeriodicArray([0, 0, 1, 1]) + + # scalar-first broadcast (previously: FieldError on `map`) + v6 = @inferred(1 .+ PeriodicArray([1, 2, 3, 4])) + @test v6 isa PeriodicVector{Int64} + @test v6 == PeriodicArray([2, 3, 4, 5]) + + # two PeriodicArrays with different element types (previously: fell back to plain Array) + v7 = @inferred(PeriodicArray([1, 2, 3]) .+ PeriodicArray([1.0, 2.0, 3.0])) + @test v7 isa PeriodicVector{Float64} + + # PeriodicArray broadcast with plain array + v8 = @inferred(PeriodicArray([1, 2, 3]) .+ [10, 20, 30]) + @test v8 isa PeriodicVector{Int64} end end diff --git a/test/test_nontrivial_boundary.jl b/test/test_nontrivial_boundary.jl index c0b5bc7..4f5d4b5 100644 --- a/test/test_nontrivial_boundary.jl +++ b/test/test_nontrivial_boundary.jl @@ -203,8 +203,17 @@ for f in translation_functions @test v4 == PeriodicArray([2, 3, 4, 5], f) v5 = v4 .> 3 - @test v5 isa PeriodicVector{Bool, BitVector} + @test v5 isa PeriodicVector{Bool, Vector{Bool}} @test v5 == PeriodicArray([0, 0, 1, 1], f) + + # scalar-first broadcast (previously: FieldError on `map`) + v6 = @inferred(1 .+ PeriodicArray([1, 2, 3, 4], f)) + @test v6 isa PeriodicVector{Int64} + @test v6 == PeriodicArray([2, 3, 4, 5], f) + + # PeriodicArray broadcast with plain array + v7 = @inferred(PeriodicArray([1, 2, 3], f) .+ [10, 20, 30]) + @test v7 isa PeriodicVector{Int64} end end From 6fca715eb367b96be25282305fd335d68b468aaf Mon Sep 17 00:00:00 2001 From: AFeuerpfeil Date: Tue, 31 Mar 2026 17:35:18 -0400 Subject: [PATCH 2/2] format --- benchmark/benchmarks.jl | 2 +- docs/files/README.jl | 8 +-- docs/make.jl | 5 +- src/PeriodicArrays.jl | 59 ++++++++-------- test/runtests.jl | 2 +- test/test_basics.jl | 116 ++++++++++++++++--------------- test/test_nontrivial_boundary.jl | 114 +++++++++++++++--------------- 7 files changed, 155 insertions(+), 151 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 25f237b..4f83c40 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -4,4 +4,4 @@ using PeriodicArrays SUITE = BenchmarkGroup() SUITE["rand"] = @benchmarkable rand(10) -# Write your benchmarks here. \ No newline at end of file +# Write your benchmarks here. diff --git a/docs/files/README.jl b/docs/files/README.jl index 7ab4808..c3ed258 100644 --- a/docs/files/README.jl +++ b/docs/files/README.jl @@ -1,11 +1,11 @@ # # PeriodicArrays.jl -# `PeriodicArrays.jl` adds the `PeriodicArray` type which can be backed by any `AbstractArray`. The idea of this package is based on [`CircularArrays.jl`](https://github.com/Vexatos/CircularArrays.jl) and extends its functionality to support user-defined translation rules for periodic indexing. -# A `PeriodicArray{T,N,A,F}` is an `AbstractArray{T,N}` backed by a data array of type `A<:AbstractArray{T,N}` and a map `f` of type `F`. +# `PeriodicArrays.jl` adds the `PeriodicArray` type which can be backed by any `AbstractArray`. The idea of this package is based on [`CircularArrays.jl`](https://github.com/Vexatos/CircularArrays.jl) and extends its functionality to support user-defined translation rules for periodic indexing. +# A `PeriodicArray{T,N,A,F}` is an `AbstractArray{T,N}` backed by a data array of type `A<:AbstractArray{T,N}` and a map `f` of type `F`. # The map defines how data in out-of-bounds indices is translated to valid indices in the data array. -# `f` can be any callable object (e.g. a function or a struct), which defines -# ```julia +# `f` can be any callable object (e.g. a function or a struct), which defines +# ```julia # f(x, shift::Vararg{Int,N}) # ``` # where `x` is an element of the array and shift encodes the unit cell, in which we index. diff --git a/docs/make.jl b/docs/make.jl index 428f5c4..140f04f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -14,8 +14,9 @@ makedocs(; format = Documenter.HTML(; canonical = "https://manybodylab.github.io/PeriodicArrays.jl", edit_link = "main", - assets = [#"assets/logo.png", - "assets/extras.css"], + assets = [#"assets/logo.png", + "assets/extras.css", + ], ), pages = ["Home" => "index.md", "Reference" => "reference.md"], ) diff --git a/src/PeriodicArrays.jl b/src/PeriodicArrays.jl index 41b5710..e5240dd 100644 --- a/src/PeriodicArrays.jl +++ b/src/PeriodicArrays.jl @@ -16,12 +16,12 @@ and periodic indexing as defined by `map`. array[index...] == map(array[mod1.(index, size)...], fld.(index .- 1, size)...) """ -struct PeriodicArray{T, N, A <: AbstractArray{T, N}, F} <: AbstractArray{T,N} +struct PeriodicArray{T, N, A <: AbstractArray{T, N}, F} <: AbstractArray{T, N} data::A map::F - PeriodicArray{T}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T,N,A,F}(data, map) - PeriodicArray{T,N}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T,N,A,F}(data, map) - PeriodicArray{T,N,A}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T,N,A,F}(data, map) + PeriodicArray{T}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T, N, A, F}(data, map) + PeriodicArray{T, N}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T, N, A, F}(data, map) + PeriodicArray{T, N, A}(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = new{T, N, A, F}(data, map) end """ @@ -30,7 +30,7 @@ end Create a `PeriodicArray` backed by `data`. `map` is optional and defaults to the identity map. """ -PeriodicArray(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = PeriodicArray{T,N}(data, map) +PeriodicArray(data::A, map::F = identity_map) where {A <: AbstractArray{T, N}, F} where {T, N} = PeriodicArray{T, N}(data, map) PeriodicArray(arr::PeriodicArray, map::F = identity_map) where {F} = arr @@ -41,7 +41,7 @@ PeriodicArray(arr::PeriodicArray, map::F = identity_map) where {F} = arr Create a `PeriodicArray` of size `size` filled with value `def`. `map` is optional and defaults to the identity map. """ -PeriodicArray(def::T, size, map::F = identity_map) where {T,F} = PeriodicArray(fill(def, size), map) +PeriodicArray(def::T, size, map::F = identity_map) where {T, F} = PeriodicArray(fill(def, size), map) """ PeriodicVector{T, A, F} <: AbstractVector{T} @@ -61,7 +61,7 @@ Alias for [`PeriodicArray{T, 2, A, F}`](@ref). """ const PeriodicMatrix{T} = PeriodicArray{T, 2} -# Define constructors for PeriodicVector and PeriodicMatrix +# Define constructors for PeriodicVector and PeriodicMatrix PeriodicVector(args...) = PeriodicArray(args...) PeriodicMatrix(args...) = PeriodicArray(args...) @@ -97,13 +97,13 @@ end # Special case for trivial map (identical to CelledArrays.jl) @inline function Base.getindex( arr::PeriodicArray{T, N, A, _identity_map_type}, i::Int - ) where {A<:AbstractArray{T, N}} where {T, N} + ) where {A <: AbstractArray{T, N}} where {T, N} return @inbounds getindex(parent(arr), mod(i, eachindex(IndexLinear(), parent(arr)))) end @inline function Base.setindex!( arr::PeriodicArray{T, N, A, _identity_map_type}, v, i::Int ) where {A <: AbstractArray{T, N}} where {T, N} - @inbounds setindex!(parent(arr), v, mod(i, eachindex(IndexLinear(), parent(arr)))) + return @inbounds setindex!(parent(arr), v, mod(i, eachindex(IndexLinear(), parent(arr)))) end @inline function Base.getindex( @@ -112,14 +112,14 @@ end i_base, i_shift = cell_position(arr, I...) @inbounds v = getindex(parent(arr), i_base...) - all(iszero, i_shift) && return v + all(iszero, i_shift) && return v return arr.map(v, i_shift...) end @inline function Base.setindex!( arr::PeriodicArray{T, N, A, F}, v, I::Vararg{Int, N} - ) where {T,N,A,F} + ) where {T, N, A, F} i_base, i_shift = inverse_cell_position(arr, I...) - + all(iszero, i_shift) && return @inbounds setindex!(parent(arr), v, i_base...) return @inbounds setindex!(parent(arr), arr.map(v, i_shift...), i_base...) end @@ -154,49 +154,49 @@ end @inline function Base.checkbounds(arr::PeriodicArray, I...) J = Base.to_indices(arr, I) length(J) == 1 || length(J) >= ndims(arr) || throw(BoundsError(arr, I)) - nothing + return nothing end -@inline function _similar(arr::PeriodicArray, ::Type{T}, dims) where T +@inline function _similar(arr::PeriodicArray, ::Type{T}, dims) where {T} return PeriodicArray(similar(parent(arr), T, dims), arr.map) end @inline function Base.similar( arr::PeriodicArray, ::Type{T}, dims::Tuple{Base.DimOrInd, Vararg{Base.DimOrInd}} - ) where T + ) where {T} return _similar(arr, T, dims) end # Ambiguity resolution with Base -@inline function Base.similar(arr::PeriodicArray, ::Type{T}, dims::Dims) where T +@inline function Base.similar(arr::PeriodicArray, ::Type{T}, dims::Dims) where {T} return _similar(arr, T, dims) end @inline function Base.similar( arr::PeriodicArray, ::Type{T}, dims::Tuple{Integer, Vararg{Integer}} - ) where T + ) where {T} return _similar(arr, T, dims) end @inline function Base.similar( - arr::PeriodicArray, ::Type{T}, + arr::PeriodicArray, ::Type{T}, dims::Tuple{Union{Integer, Base.OneTo}, Vararg{Union{Integer, Base.OneTo}}} - ) where T + ) where {T} return _similar(arr, T, dims) end struct PeriodicArrayStyle{N} <: Broadcast.AbstractArrayStyle{N} end -PeriodicArrayStyle{N}(::Val{M}) where {N,M} = PeriodicArrayStyle{M}() +PeriodicArrayStyle{N}(::Val{M}) where {N, M} = PeriodicArrayStyle{M}() -Broadcast.BroadcastStyle(::Type{<:PeriodicArray{T,N}}) where {T,N} = PeriodicArrayStyle{N}() -Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::PeriodicArrayStyle{N}) where {M,N} = PeriodicArrayStyle{max(M,N)}() -Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::Broadcast.DefaultArrayStyle{N}) where {M,N} = PeriodicArrayStyle{max(M,N)}() -Broadcast.BroadcastStyle(::Broadcast.DefaultArrayStyle{N}, ::PeriodicArrayStyle{M}) where {N,M} = PeriodicArrayStyle{max(N,M)}() +Broadcast.BroadcastStyle(::Type{<:PeriodicArray{T, N}}) where {T, N} = PeriodicArrayStyle{N}() +Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::PeriodicArrayStyle{N}) where {M, N} = PeriodicArrayStyle{max(M, N)}() +Broadcast.BroadcastStyle(::PeriodicArrayStyle{M}, ::Broadcast.DefaultArrayStyle{N}) where {M, N} = PeriodicArrayStyle{max(M, N)}() +Broadcast.BroadcastStyle(::Broadcast.DefaultArrayStyle{N}, ::PeriodicArrayStyle{M}) where {N, M} = PeriodicArrayStyle{max(N, M)}() _find_pa(bc::Broadcast.Broadcasted) = _find_pa(bc.args...) _find_pa(a::Broadcast.Extruded, rest...) = _find_pa(a.x, rest...) _find_pa() = nothing _find_pa(a::PeriodicArray, rest...) = a _find_pa(a::Broadcast.Broadcasted, rest...) = - let r = _find_pa(a) - r !== nothing ? r : _find_pa(rest...) - end +let r = _find_pa(a) + r !== nothing ? r : _find_pa(rest...) +end _find_pa(::Any, rest...) = _find_pa(rest...) @inline function Base.similar( @@ -211,13 +211,12 @@ end function Base.showarg(io::IO, arr::PeriodicArray, toplevel) print(io, ndims(arr) == 1 ? "PeriodicVector(" : "PeriodicArray(") Base.showarg(io, parent(arr), false) - print(io, ')') + return print(io, ')') # toplevel && print(io, " with eltype ", eltype(arr)) end - -Base.empty(a::PeriodicVector{T}, ::Type{U}=T) where {T, U} = PeriodicVector{U}(U[], a.map) +Base.empty(a::PeriodicVector{T}, ::Type{U} = T) where {T, U} = PeriodicVector{U}(U[], a.map) Base.empty!(a::PeriodicVector) = (empty!(parent(a)); a) Base.push!(a::PeriodicVector, x...) = (push!(parent(a), x...); a) Base.append!(a::PeriodicVector, items) = (append!(parent(a), items); a) diff --git a/test/runtests.jl b/test/runtests.jl index 067c0fc..2124a45 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -67,4 +67,4 @@ end end end end -end \ No newline at end of file +end diff --git a/test/test_basics.jl b/test/test_basics.jl index 3179c7c..03e9d1d 100644 --- a/test/test_basics.jl +++ b/test/test_basics.jl @@ -8,12 +8,14 @@ using OffsetArrays end @testset "construction" begin - @testset "construction ($T)" for T = (Float64, Int) - data = rand(T,10) - arrays = [PeriodicVector(data), PeriodicVector{T}(data), - PeriodicArray(data), PeriodicArray{T}(data), PeriodicArray{T,1}(data)] + @testset "construction ($T)" for T in (Float64, Int) + data = rand(T, 10) + arrays = [ + PeriodicVector(data), PeriodicVector{T}(data), + PeriodicArray(data), PeriodicArray{T}(data), PeriodicArray{T, 1}(data), + ] @test all(a == first(arrays) for a in arrays) - @test all(a isa PeriodicVector{T,Vector{T}} for a in arrays) + @test all(a isa PeriodicVector{T, Vector{T}} for a in arrays) end @testset "matrix construction" begin @@ -28,11 +30,11 @@ end @testset "type stability" begin @testset "type stability $(n)d" for n in 1:10 - a = PeriodicArray(fill(1, ntuple(_->1, n))) + a = PeriodicArray(fill(1, ntuple(_ -> 1, n))) @test @inferred(a[1]) isa Int64 @test @inferred(a[[1]]) isa PeriodicVector{Int64} - @test @inferred(a[[1]']) isa PeriodicArray{Int64,2} + @test @inferred(a[[1]']) isa PeriodicArray{Int64, 2} @test @inferred(axes(a)) isa Tuple{Vararg{AbstractUnitRange}} @test @inferred(similar(a)) isa typeof(a) @test @inferred(similar(a, Float64, Int8.(size(a)))) isa PeriodicArray{Float64} @@ -42,7 +44,7 @@ end @testset "display" begin @testset "display $(n)d" for n in 1:3 - data = rand(Int64, ntuple(_->3, n)) + data = rand(Int64, ntuple(_ -> 3, n)) v1 = PeriodicArray(data) io = IOBuffer() io_compare = IOBuffer() @@ -69,7 +71,7 @@ end @test size(v1, 1) == 5 @test parent(v1) == data - @test typeof(v1) == PeriodicVector{Int64,Vector{Int64},typeof(PeriodicArrays.identity_map)} + @test typeof(v1) == PeriodicVector{Int64, Vector{Int64}, typeof(PeriodicArrays.identity_map)} @test isa(v1, PeriodicVector) @test isa(v1, AbstractVector{Int}) @test !isa(v1, AbstractVector{String}) @@ -79,7 +81,7 @@ end @test v1[0] == data[end] @test v1[-4:10] == [data; data; data] @test v1[-3:1][-1] == data[end] - @test v1[[true,false,true,false,true]] == v1[[1,3,0]] + @test v1[[true, false, true, false, true]] == v1[[1, 3, 0]] @test all(e in data for e in v1) @test all(e in v1 for e in data) @@ -97,7 +99,7 @@ end @test prod(v2) == "abcde"^5 @testset "empty/empty!" begin - v1 = PeriodicVector([1,2,3]) + v1 = PeriodicVector([1, 2, 3]) @test empty(v1) isa PeriodicVector{Int64} @test empty(v1, Float64) isa PeriodicVector{Float64} v1 == PeriodicVector([]) @@ -111,48 +113,48 @@ end @testset "resize!" begin @test_throws ArgumentError("new length must be ≥ 0") resize!(PeriodicVector([]), -2) - v = PeriodicVector([1,2,3,4,5,6,7]) + v = PeriodicVector([1, 2, 3, 4, 5, 6, 7]) resize!(v, 3) @test length(v) == 3 # ensure defining `resize!` induces `push!` and `append!` methods @testset "push!" begin - v = PeriodicVector([1,2,3]) + v = PeriodicVector([1, 2, 3]) push!(v, 42) - @test v == PeriodicVector([1,2,3,42]) + @test v == PeriodicVector([1, 2, 3, 42]) push!(v, -9, -99, -999) - @test v == PeriodicVector([1,2,3,42, -9, -99, -999]) + @test v == PeriodicVector([1, 2, 3, 42, -9, -99, -999]) end @testset "append!" begin - v1 = PeriodicVector([1,2,3]) + v1 = PeriodicVector([1, 2, 3]) append!(v1, [-9, -99, -999]) - @test v1 == PeriodicVector([1,2,3, -9, -99, -999]) + @test v1 == PeriodicVector([1, 2, 3, -9, -99, -999]) - v2 = PeriodicVector([1,2,3]) - append!(v2, PeriodicVector([-1,-2])) - @test v2 == PeriodicVector([1,2,3,-1,-2]) + v2 = PeriodicVector([1, 2, 3]) + append!(v2, PeriodicVector([-1, -2])) + @test v2 == PeriodicVector([1, 2, 3, -1, -2]) - v3 = PeriodicVector([1,2,3]) + v3 = PeriodicVector([1, 2, 3]) append!(v3, [4, 5], [6]) - @test v3 == PeriodicVector([1,2,3,4,5,6]) + @test v3 == PeriodicVector([1, 2, 3, 4, 5, 6]) - v4 = PeriodicVector([1,2,3]) - o4 = OffsetVector([-1,-2,-3], -2:0) + v4 = PeriodicVector([1, 2, 3]) + o4 = OffsetVector([-1, -2, -3], -2:0) append!(v4, o4) - @test v4 == PeriodicVector([1,2,3,-1,-2,-3]) + @test v4 == PeriodicVector([1, 2, 3, -1, -2, -3]) - v5 = PeriodicVector([1,2,3]) - o5 = OffsetVector([-1,-2,-3], -2:0) + v5 = PeriodicVector([1, 2, 3]) + o5 = OffsetVector([-1, -2, -3], -2:0) append!(v5, o5, -4) - @test v5 == PeriodicVector([1,2,3,-1,-2,-3,-4]) + @test v5 == PeriodicVector([1, 2, 3, -1, -2, -3, -4]) end end @testset "pop!" begin - v1 = PeriodicVector([1,2,3,42]) + v1 = PeriodicVector([1, 2, 3, 42]) pop!(v1) == 42 - @test v1 == PeriodicVector([1,2,3]) + @test v1 == PeriodicVector([1, 2, 3]) v2 = PeriodicVector([1]) pop!(v2) == 1 @@ -162,7 +164,7 @@ end end @testset "sizehint!" begin - v = PeriodicVector([1,2,3,4,5,6,7]) + v = PeriodicVector([1, 2, 3, 4, 5, 6, 7]) resize!(v, 1) sizehint!(v, 1) @test length(v) == 1 @@ -196,7 +198,7 @@ end end @testset "type stability" begin - v3 = @inferred(map(x -> x+1, PeriodicArray([1, 2, 3, 4]))) + v3 = @inferred(map(x -> x + 1, PeriodicArray([1, 2, 3, 4]))) @test v3 isa PeriodicVector{Int64} @test v3 == PeriodicArray([2, 3, 4, 5]) @test similar(v3, Base.OneTo(4)) isa typeof(v3) @@ -242,12 +244,12 @@ end @test a1[1, 3] == 99 a1[18] = 9 - @test a1[18] == a1[-6] == a1[6] == a1[3,2] == a1[0,6] == b_arr[3,2] == b_arr[6] == 9 + @test a1[18] == a1[-6] == a1[6] == a1[3, 2] == a1[0, 6] == b_arr[3, 2] == b_arr[6] == 9 @test IndexStyle(a1) == IndexStyle(typeof(a1)) == IndexCartesian() - @test a1[3] == a1[3,1] - @test a1[Int32(4)] == a1[1,2] - @test a1[-1] == a1[length(a1)-1] + @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 @@ -280,30 +282,30 @@ end end @testset "3-array" begin - t3 = collect(reshape('a':'x', 2,3,4)) + t3 = collect(reshape('a':'x', 2, 3, 4)) c3 = PeriodicArray(t3) @test parent(c3) == t3 - @test c3[1,3,3] == c3[3,3,3] == c3[3,3,7] == c3[3,3,7,1] + @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' + 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 c3[3, CartesianIndex(3, 7)] == 'Z' + c3[Int32(3), CartesianIndex(3, 7)] = 'ζ' + @test t3[1, 3, 3] == 'ζ' c3[34] = 'J' - @test c3[34] == c3[-38] == c3[10] == c3[2,2,2] == c3[4,5,6] == t3[2,2,2] == t3[10] == 'J' + @test c3[34] == c3[-38] == c3[10] == c3[2, 2, 2] == c3[4, 5, 6] == t3[2, 2, 2] == t3[10] == 'J' @test vec(c3[:, [CartesianIndex()], 1, 5]) == vec(t3[:, 1, 1]) @test IndexStyle(c3) == IndexStyle(typeof(c3)) == IndexCartesian() - @test c3[-1] == t3[length(t3)-1] + @test c3[-1] == t3[length(t3) - 1] - @test_throws BoundsError c3[2,3] # too few indices - @test_throws BoundsError c3[CartesianIndex(2,3)] + @test_throws BoundsError c3[2, 3] # too few indices + @test_throws BoundsError c3[CartesianIndex(2, 3)] @testset "doubly periodic" begin c = PeriodicArray(t3) @@ -315,7 +317,7 @@ end end @testset "offset indices" begin - i = OffsetArray(1:5,-3) + i = OffsetArray(1:5, -3) a = PeriodicArray(i) @test axes(a) == axes(i) @test a[1] == 4 @@ -323,7 +325,7 @@ end @test a[-2:7] == [1:5; 1:5] @test a[0:9] == [3:5; 1:5; 1:2] @test a[1:10][-10] == 3 - @test a[i] == OffsetArray([4,5,1,2,3],-3) + @test a[i] == OffsetArray([4, 5, 1, 2, 3], -3) @testset "type stability" begin @test @inferred(similar(a)) isa PeriodicVector @@ -332,18 +334,18 @@ end @test @inferred(similar(b, 3:5)) isa PeriodicVector end - circ_a = circshift(a,3) + circ_a = circshift(a, 3) @test axes(circ_a) == axes(a) @test circ_a[1:5] == 1:5 - j = OffsetArray([true,false,true],1) - @test a[j] == [5,2] + j = OffsetArray([true, false, true], 1) + @test a[j] == [5, 2] - data = reshape(1:9,3,3) - a = PeriodicArray(OffsetArray(data,-1,-1)) + data = reshape(1:9, 3, 3) + a = PeriodicArray(OffsetArray(data, -1, -1)) @test collect(a) == data - @test all(a[x,y] == data[mod1(x+1,3),mod1(y+1,3)] for x=-10:10, y=-10:10) - @test a[i,1] == PeriodicArray(OffsetArray([5,6,4,5,6],-2:2)) - @test a[CartesianIndex.(i,i)] == PeriodicArray(OffsetArray([5,9,1,5,9],-2:2)) + @test all(a[x, y] == data[mod1(x + 1, 3), mod1(y + 1, 3)] for x in -10:10, y in -10:10) + @test a[i, 1] == PeriodicArray(OffsetArray([5, 6, 4, 5, 6], -2:2)) + @test a[CartesianIndex.(i, i)] == PeriodicArray(OffsetArray([5, 9, 1, 5, 9], -2:2)) @test a[a .> 4] == 5:9 end diff --git a/test/test_nontrivial_boundary.jl b/test/test_nontrivial_boundary.jl index 4f5d4b5..1c1c6c4 100644 --- a/test/test_nontrivial_boundary.jl +++ b/test/test_nontrivial_boundary.jl @@ -2,11 +2,11 @@ using PeriodicArrays using Test using OffsetArrays -f1(x,shift::Vararg{Int,N}) where {N} = x + sum(shift) -f2(x,shift::Vararg{Int,N}) where {N} = x + shift[1] +f1(x, shift::Vararg{Int, N}) where {N} = x + sum(shift) +f2(x, shift::Vararg{Int, N}) where {N} = x + shift[1] struct TestTranslation end -function (f::TestTranslation)(x, shift::Vararg{Int,N}) where {N} +function (f::TestTranslation)(x, shift::Vararg{Int, N}) where {N} return x - sum(shift) end f3 = TestTranslation() @@ -14,12 +14,14 @@ translation_functions = [f1, f2, f3] for f in translation_functions @testset "construction" begin - @testset "construction ($T)" for T = (Float64, Int) - data = rand(T,10) - arrays = [PeriodicVector(data, f), PeriodicVector{T}(data, f), - PeriodicArray(data, f), PeriodicArray{T}(data, f), PeriodicArray{T,1}(data, f)] + @testset "construction ($T)" for T in (Float64, Int) + data = rand(T, 10) + arrays = [ + PeriodicVector(data, f), PeriodicVector{T}(data, f), + PeriodicArray(data, f), PeriodicArray{T}(data, f), PeriodicArray{T, 1}(data, f), + ] @test all(a == first(arrays) for a in arrays) - @test all(a isa PeriodicVector{T,Vector{T}} for a in arrays) + @test all(a isa PeriodicVector{T, Vector{T}} for a in arrays) end @testset "matrix construction" begin @@ -33,10 +35,10 @@ for f in translation_functions end @testset "type stability" begin @testset "type stability $(n)d" for n in 1:10 - a = PeriodicArray(fill(1, ntuple(_->1, n)), f) + a = PeriodicArray(fill(1, ntuple(_ -> 1, n)), f) @test @inferred(a[1]) isa Int64 @test @inferred(a[[1]]) isa PeriodicVector{Int64} - @test @inferred(a[[1]']) isa PeriodicArray{Int64,2} + @test @inferred(a[[1]']) isa PeriodicArray{Int64, 2} @test @inferred(axes(a)) isa Tuple{Vararg{AbstractUnitRange}} @test @inferred(similar(a)) isa typeof(a) @test @inferred(similar(a, Float64, Int8.(size(a)))) isa PeriodicArray{Float64} @@ -45,7 +47,7 @@ for f in translation_functions end @testset "display" begin @testset "display $(n)d" for n in 1:3 - data = rand(Int64, ntuple(_->3, n)) + data = rand(Int64, ntuple(_ -> 3, n)) v1 = PeriodicArray(data, f) io = IOBuffer() io_compare = IOBuffer() @@ -71,7 +73,7 @@ for f in translation_functions @test size(v1, 1) == 5 @test parent(v1) == data - @test typeof(v1) == PeriodicVector{Int64,Vector{Int64},typeof(f)} + @test typeof(v1) == PeriodicVector{Int64, Vector{Int64}, typeof(f)} @test isa(v1, PeriodicVector) @test isa(v1, AbstractVector{Int}) @test !isa(v1, AbstractVector{String}) @@ -94,7 +96,7 @@ for f in translation_functions @test prod(v2) == "abcde"^5 @testset "empty/empty!" begin - v1 = PeriodicVector([1,2,3], f) + v1 = PeriodicVector([1, 2, 3], f) @test empty(v1) isa PeriodicVector{Int64} @test empty(v1, Float64) isa PeriodicVector{Float64} v1 == PeriodicVector([], f) @@ -108,48 +110,48 @@ for f in translation_functions @testset "resize!" begin @test_throws ArgumentError("new length must be ≥ 0") resize!(PeriodicVector([], f), -2) - v = PeriodicVector([1,2,3,4,5,6,7], f) + v = PeriodicVector([1, 2, 3, 4, 5, 6, 7], f) resize!(v, 3) @test length(v) == 3 # ensure defining `resize!` induces `push!` and `append!` methods @testset "push!" begin - v = PeriodicVector([1,2,3], f) + v = PeriodicVector([1, 2, 3], f) push!(v, 42) - @test v == PeriodicVector([1,2,3,42], f) + @test v == PeriodicVector([1, 2, 3, 42], f) push!(v, -9, -99, -999) - @test v == PeriodicVector([1,2,3,42, -9, -99, -999], f) + @test v == PeriodicVector([1, 2, 3, 42, -9, -99, -999], f) end @testset "append!" begin - v1 = PeriodicVector([1,2,3], f) + v1 = PeriodicVector([1, 2, 3], f) append!(v1, [-9, -99, -999]) - @test v1 == PeriodicVector([1,2,3, -9, -99, -999], f) + @test v1 == PeriodicVector([1, 2, 3, -9, -99, -999], f) - v2 = PeriodicVector([1,2,3], f) - append!(v2, PeriodicVector([-1,-2], f)) - @test v2 == PeriodicVector([1,2,3,-1,-2], f) + v2 = PeriodicVector([1, 2, 3], f) + append!(v2, PeriodicVector([-1, -2], f)) + @test v2 == PeriodicVector([1, 2, 3, -1, -2], f) - v3 = PeriodicVector([1,2,3], f) + v3 = PeriodicVector([1, 2, 3], f) append!(v3, [4, 5], [6]) - @test v3 == PeriodicVector([1,2,3,4,5,6], f) + @test v3 == PeriodicVector([1, 2, 3, 4, 5, 6], f) - v4 = PeriodicVector([1,2,3], f) - o4 = OffsetVector([-1,-2,-3], -2:0) + v4 = PeriodicVector([1, 2, 3], f) + o4 = OffsetVector([-1, -2, -3], -2:0) append!(v4, o4) - @test v4 == PeriodicVector([1,2,3,-1,-2,-3], f) + @test v4 == PeriodicVector([1, 2, 3, -1, -2, -3], f) - v5 = PeriodicVector([1,2,3], f) - o5 = OffsetVector([-1,-2,-3], -2:0) + v5 = PeriodicVector([1, 2, 3], f) + o5 = OffsetVector([-1, -2, -3], -2:0) append!(v5, o5, -4) - @test v5 == PeriodicVector([1,2,3,-1,-2,-3,-4], f) + @test v5 == PeriodicVector([1, 2, 3, -1, -2, -3, -4], f) end end @testset "pop!" begin - v1 = PeriodicVector([1,2,3,42], f) + v1 = PeriodicVector([1, 2, 3, 42], f) pop!(v1) == 42 - @test v1 == PeriodicVector([1,2,3], f) + @test v1 == PeriodicVector([1, 2, 3], f) v2 = PeriodicVector([1], f) pop!(v2) == 1 @@ -159,7 +161,7 @@ for f in translation_functions end @testset "sizehint!" begin - v = PeriodicVector([1,2,3,4,5,6,7], f) + v = PeriodicVector([1, 2, 3, 4, 5, 6, 7], f) resize!(v, 1) sizehint!(v, 1) @test length(v) == 1 @@ -193,7 +195,7 @@ for f in translation_functions end @testset "type stability" begin - v3 = @inferred(map(x -> x+1, PeriodicArray([1, 2, 3, 4], f))) + v3 = @inferred(map(x -> x + 1, PeriodicArray([1, 2, 3, 4], f))) @test v3 isa PeriodicVector{Int64} @test v3 == PeriodicArray([2, 3, 4, 5], f) @test similar(v3, Base.OneTo(4)) isa typeof(v3) @@ -234,8 +236,8 @@ for f in translation_functions @test f(a1[1, 3], -1, 1) == 99 @test IndexStyle(a1) == IndexStyle(typeof(a1)) == IndexCartesian() - @test a1[3] == a1[3,1] - @test a1[Int32(4)] == a1[1,2] + @test a1[3] == a1[3, 1] + @test a1[Int32(4)] == a1[1, 2] @test a1[2, 3, 1] == 17 # trailing index @test a1[2, 3, 99] == 17 @@ -274,24 +276,24 @@ for f in translation_functions end @testset "3-array" begin - t3 = collect(reshape(1:24, 2,3,4)) + t3 = collect(reshape(1:24, 2, 3, 4)) c3 = PeriodicArray(t3, f) @test parent(c3) == t3 - @test c3[1,3,3] == f(c3[3,3,3],-1,0,0) == f(c3[3,3,7],-1,0,-1) == f(c3[3,3,7,1],-1,0,-1) + @test c3[1, 3, 3] == f(c3[3, 3, 3], -1, 0, 0) == f(c3[3, 3, 7], -1, 0, -1) == f(c3[3, 3, 7, 1], -1, 0, -1) - @test c3[3,3,7] == f(c3[1,3,3],1,0,1) + @test c3[3, 3, 7] == f(c3[1, 3, 3], 1, 0, 1) - @test c3[3, CartesianIndex(3,7)] == f(c3[1,3,3],1,0,1) - @test c3[Int32(3), CartesianIndex(3,7)] == f(c3[1,3,3],1,0,1) + @test c3[3, CartesianIndex(3, 7)] == f(c3[1, 3, 3], 1, 0, 1) + @test c3[Int32(3), CartesianIndex(3, 7)] == f(c3[1, 3, 3], 1, 0, 1) - @test vec(c3[:, [CartesianIndex()], 1, 5]) == map(x->f(x,0,0,1), vec(t3[:, 1, 1])) + @test vec(c3[:, [CartesianIndex()], 1, 5]) == map(x -> f(x, 0, 0, 1), vec(t3[:, 1, 1])) @test IndexStyle(c3) == IndexStyle(typeof(c3)) == IndexCartesian() - @test_throws BoundsError c3[2,3] # too few indices - @test_throws BoundsError c3[CartesianIndex(2,3)] + @test_throws BoundsError c3[2, 3] # too few indices + @test_throws BoundsError c3[CartesianIndex(2, 3)] @test_throws BoundsError c3[30] @testset "doubly periodic" begin @@ -303,13 +305,13 @@ for f in translation_functions end @testset "offset indices" begin - i = OffsetArray(1:5,-3) + i = OffsetArray(1:5, -3) a = PeriodicArray(i, f) @test axes(a) == axes(i) @test a[1] == 4 - @test f(a[10],-2) == f(a[-10],2) == a[0] == 3 + @test f(a[10], -2) == f(a[-10], 2) == a[0] == 3 @test a[1:10][-10] == 3 - @test a[i] == OffsetArray([4,5,f(1,1),f(2,1),f(3,1)],-3) + @test a[i] == OffsetArray([4, 5, f(1, 1), f(2, 1), f(3, 1)], -3) @testset "type stability" begin @test @inferred(similar(a)) isa PeriodicVector @@ -318,19 +320,19 @@ for f in translation_functions @test @inferred(similar(b, 3:5)) isa PeriodicVector end - circ_a = circshift(a,3) + circ_a = circshift(a, 3) @test axes(circ_a) == axes(a) - @test circ_a[1:5] == [1,2,f(3,1),f(4,1),f(5,1)] + @test circ_a[1:5] == [1, 2, f(3, 1), f(4, 1), f(5, 1)] - j = OffsetArray([true,false,true],1) - @test a[j] == [5,f(2,1)] + j = OffsetArray([true, false, true], 1) + @test a[j] == [5, f(2, 1)] - data = reshape(1:9,3,3) - a = PeriodicArray(OffsetArray(data,-1,-1), f) + data = reshape(1:9, 3, 3) + a = PeriodicArray(OffsetArray(data, -1, -1), f) @test collect(a) == data - @test all(a[x,y] == f(data[mod1(x+1,3),mod1(y+1,3)], fld(x,3), fld(y,3)) for x=-10:10, y=-10:10) - @test a[i,1] == PeriodicArray(OffsetArray([5,6,f(4,1,0),f(5,1,0),f(6,1,0)],-2:2), f) - @test a[CartesianIndex.(i,i)] == PeriodicArray(OffsetArray([5,9,f(1,1,1),f(5,1,1),f(9,1,1)],-2:2), f) + @test all(a[x, y] == f(data[mod1(x + 1, 3), mod1(y + 1, 3)], fld(x, 3), fld(y, 3)) for x in -10:10, y in -10:10) + @test a[i, 1] == PeriodicArray(OffsetArray([5, 6, f(4, 1, 0), f(5, 1, 0), f(6, 1, 0)], -2:2), f) + @test a[CartesianIndex.(i, i)] == PeriodicArray(OffsetArray([5, 9, f(1, 1, 1), f(5, 1, 1), f(9, 1, 1)], -2:2), f) # TODO: Figure out how to fix indexing for non-trivial f #@test a[a .> 4] == 5:9 end