From cfe75ee3e2eee80bbd322600367822a00d0aa696 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 20:27:07 +0100 Subject: [PATCH 01/15] Switch to using retest for faster development. Undone autoformat in rebase. --- Project.toml | 6 - test/Project.toml | 4 + test/SentinelArrayTests.jl | 11 ++ test/chainedvector.jl | 17 ++ test/missingvector.jl | 126 ++++++++++++ test/runtests.jl | 379 +------------------------------------ test/sentinelarrays.jl | 248 ++++++++++++++++++++++++ 7 files changed, 410 insertions(+), 381 deletions(-) create mode 100644 test/Project.toml create mode 100644 test/SentinelArrayTests.jl create mode 100644 test/missingvector.jl create mode 100644 test/sentinelarrays.jl diff --git a/Project.toml b/Project.toml index 43ceb48..c6ceb02 100644 --- a/Project.toml +++ b/Project.toml @@ -9,9 +9,3 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] julia = "1.3" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[targets] -test = ["Test"] diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..afc51cc --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,4 @@ +[deps] +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +ReTest = "e0db7c4e-2690-44b9-bad6-7687da720f89" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/test/SentinelArrayTests.jl b/test/SentinelArrayTests.jl new file mode 100644 index 0000000..822a7bf --- /dev/null +++ b/test/SentinelArrayTests.jl @@ -0,0 +1,11 @@ +module SentinelArrayTests +using ReTest +using Random, SparseArrays + +using SentinelArrays + +include("sentinelarrays.jl") +include("missingvector.jl") +include("chainedvector.jl") + +end \ No newline at end of file diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 93363c1..7fea81a 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -560,3 +560,20 @@ end @test deleteat!(v2, m2) == deleteat!(s2, m2) end end + +@testset "deleteat! with Bool mask" begin + x = SentinelArray(["a", "b", "c", "d", "e"]) + mask = [false, true, false, false, false] + @test deleteat!(x, mask) == ["a", "c", "d", "e"] + + for i in 1:100 + v1 = collect(string.(1:i)) + v2 = copy(v1) + s1 = SentinelArray(copy(v1)) + s2 = SentinelArray(copy(v1)) + m1 = rand(Bool, i) + m2 = BitVector(m1) + @test deleteat!(v1, m1) == deleteat!(s1, m1) + @test deleteat!(v2, m2) == deleteat!(s2, m2) + end +end \ No newline at end of file diff --git a/test/missingvector.jl b/test/missingvector.jl new file mode 100644 index 0000000..9cdc792 --- /dev/null +++ b/test/missingvector.jl @@ -0,0 +1,126 @@ + +@testset "MissingVector" begin + + x = MissingVector(10) + @test all(x .=== missing) + @test length(x) == 10 + @test length(x) isa Int + @test Base.IndexStyle(x) == Base.IndexLinear() + + y = similar(x, Missing, 5) + @test length(y) == 5 + + y = empty(x) + @test length(y) == 0 + + x[1] = missing + x[end] = missing + @test x[1] === missing + @test x[end] === missing + + y = similar(x) + @test typeof(y) == MissingVector + @test length(y) == 10 + y = similar(x, 20) + @test typeof(y) == MissingVector + @test length(y) == 20 + + @test isequal(copy(x), x) + empty!(x) + @test length(x) == 0 + @test isequal(copy(x), x) + + @test_throws ArgumentError resize!(x, -1) + resize!(x, 10) + @test length(x) == 10 + + push!(x, missing) + @test x[end] === missing + empty!(x) + push!(x, missing) + @test x[1] === x[end] === missing + + pushfirst!(x, missing) + @test x[1] === missing + empty!(x) + pushfirst!(x, missing) + @test x[1] === x[end] === missing + pushfirst!(x, missing) + + @test pop!(x) === missing + @test popfirst!(x) === missing + @test isempty(x) + + @test_throws BoundsError insert!(x, 0, missing) + @test_throws BoundsError insert!(x, 2, missing) + insert!(x, 1, missing) + @test x[1] === missing + insert!(x, 1, missing) + @test x[1] === missing + + x = MissingVector(10) + y = MissingVector(10) + + z = vcat(x, y) + @test length(z) == 20 + + empty!(x) + z = vcat(x, y) + @test isequal(z, y) + + x = MissingVector(10) + append!(x, y) + @test length(x) == 20 + + x = MissingVector(10) + append!(x, (missing for _ = 1:10)) + @test length(x) == 20 + + empty!(x) + append!(x, y) + @test isequal(x, y) + + x = MissingVector(10) + y = MissingVector(10) + + prepend!(y, x) + @test length(y) == 20 + + y = MissingVector(10) + prepend!(y, (missing for _ = 1:10)) + @test length(y) == 20 + + empty!(y) + prepend!(y, x) + @test isequal(y, x) + + x = MissingVector(10) + deleteat!(x, 1) + @test x[1] === missing + deleteat!(x, 1:4) + @test length(x) == 5 + deleteat!(x, [2, 4]) + @test length(x) == 3 + + m = similar(x) + @test length(m) == length(x) + m = similar(x, Missing) + @test length(m) == length(x) + @test typeof(m[1:3]) == typeof(m) + + deleteat!(x, [true, true, false]) + @test length(x) == 1 + empty!(x) + @test_throws ArgumentError pop!(x) + @test_throws ArgumentError popfirst!(x) + + m = MissingVector(5) + c = ChainedVector([m, m, m]) + c2 = copy(c) + @test length(c) == length(c2) + @test c2 isa MissingVector + + deleteat!(c2, Int[]) + @test length(c2) == 15 + +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index e41e38a..8ac902c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,378 +1,7 @@ -using SentinelArrays, Test, Random +using ReTest -@testset "SentinelArrays" begin +using SentinelArrays -# this is a very rare corner case -# we put this test first because it relies on -# the seeded random number generator for thread 1 -# the issue is if we had an underlying array of non-sentinel values -# then tried to `setindex!` to the _NEXT_ chosen sentinel value -# then the element getting set would end up `missing` (because it == the sentinel value) -# instead of the sentinel value getting cycled to something else -Random.seed!(0) -x = SentinelVector{Int64}(undef, 1) -x[1] = 1 -x.sentinel = 1369352191816061504 -x[1] = 1369352191816061504 -@test x[1] == 1369352191816061504 +include("SentinelArrayTests.jl") -x = SentinelVector{Int}(undef, 10) -fill!(x, missing) -@test length(x) == 10 -@test all(isequal.(x.data, x.sentinel)) - -@test x[1] === missing -# force recoding -sent = x.sentinel -x[1] = x.sentinel -@test x[1] === sent -r = reverse(x) -@test x.sentinel == r.sentinel - -@test size(x) == (10,) -x[1] = 3 -@test x[1] === 3 - -resize!(x, length(x) + 1) -@test x[end] === missing - -x = SentinelVector{Int64}(undef, 1) -x[1] = missing -@test x[1] === missing - -x = SentinelArray{Float64, 2}(undef, 10, 10) -@test size(x) == (10, 10) -x = SentinelArray{Float64}(undef, 10, 10) -@test size(x) == (10, 10) - -x = SentinelArray(fill(3.0, 10, 10)) -y = convert(SentinelArray{Int64}, x) -@test size(y) == (10, 10) -@test y isa SentinelArray{Int64} -@test all(y .=== Int64(3)) - -y = convert(SentinelVector{Int64}, x) -@test size(y) == (10, 10) -@test y isa SentinelArray{Int64} -@test all(y .=== Int64(3)) - -y = convert(SentinelArray, fill(Int64(3), 10, 10)) -@test size(y) == (10, 10) -@test size(y, 1) == 10 -@test axes(y, 1) == Base.OneTo(10) -@test stride(y, 1) == 1 -@test strides(y) == (1, 10) -@test y isa SentinelArray{Int64} -@test all(y .=== Int64(3)) - -x = SentinelVector{Union{Bool, Missing}}(undef, 1, missing, missing) -@test x[1] === missing -x[1] = true -@test x[1] === true -x[1] = missing -@test x[1] === missing - -x = SentinelArray(["$i" for i = 1:10]) -deleteat!(x, [9, 10]) -@test length(x) == 8 -@test x == ["$i" for i = 1:8] -deleteat!(x, [true, false, true, false, true, false, true, false]) -@test length(x) == 4 -@test x == ["2", "4", "6", "8"] - -x = SentinelVector{String}(undef, 10) -@test x[1] === missing -x[1] = "hey" -@test x[1] == "hey" -x[1] = missing -@test x[1] === missing - -@test SentinelArrays.newsentinel!(x) === nothing - -@test all(x .=== copy(x)) -@test length(empty!(x)) == 0 -@test length(resize!(x, 10)) == 10 -@test x[1] === missing - -push!(x, missing) -@test x[end] === missing -push!(x, "ho") -@test x[end] == "ho" - -deleteat!(x, length(x)) -@test x[end] === missing -@test length(x) == 11 - -empty!(x) -append!(x, ["hey", "ho", missing]) -@test length(x) == 3 -@test isequal(x, ["hey", "ho", missing]) - -pushfirst!(x, missing) -@test x[1] === missing -prepend!(x, [missing, "first"]) -@test x[1] === missing -@test x[2] == "first" - -@test popfirst!(x) === missing -@test x[1] == "first" - -insert!(x, 1, missing) -@test x[1] === missing -insert!(x, length(x) + 1, "pirate") -@test x[end] == "pirate" - -@test splice!(x, length(x), ["pirate2"]) == "pirate" -@test splice!(x, length(x)) == "pirate2" -@test splice!(x, length(x), ["pirate3", "pirate4"]) === missing - -@test splice!(x, (length(x)-1):length(x), ["pirate5", "pirate6"]) == ["pirate3", "pirate4"] -@test splice!(x, (length(x)-1):length(x), ["pirate7"]) == ["pirate5", "pirate6"] -@test splice!(x, length(x), ["pirate8", "pirate9"]) == "pirate7" -@test splice!(x, (length(x)-1):length(x), ["pirate10", "pirate11", "pirate12"]) == ["pirate8", "pirate9"] -@test splice!(x, (length(x)-2):length(x)) == ["pirate10", "pirate11", "pirate12"] - -t = [SentinelVector{Int64}(undef, 10), SentinelVector{Int64}(undef, 10), SentinelVector{Int64}(undef, 5)] -sent = t[1].sentinel -@test all(x->x.sentinel == sent, t) -SentinelArrays.newsentinel!(t...; force=false) -# make sure nothing got recoded -@test all(x->x.sentinel == sent, t) -SentinelArrays.newsentinel!(t...; force=true) -# make sure all got recoded -@test all(x->x.sentinel != sent, t) - -# force recode of just one vector -sent = t[1].sentinel -t[2][1] = t[2].sentinel -@test !all(x->x.sentinel == sent, t) -SentinelArrays.newsentinel!(t...) -# make sure all got recoded to same -@test all(x->x.sentinel == t[1].sentinel, t) - -A = SentinelArray(Int64[i for i = 1:10]) -B = SentinelVector{Int64}(undef, 10) - -C = vcat(A, B) -@test C[1:10] == collect(1:10) -@test all(C[11:20] .=== missing) - -append!(A, B) -@test A[1:10] == collect(1:10) -@test all(A[11:20] .=== missing) - -A = SentinelArray(Int64[i for i = 1:10]) -B = SentinelVector{Int64}(undef, 10) -# force B to recode -B[1] = B.sentinel -B[1] = missing - -C = vcat(A, B) -@test C[1:10] == collect(1:10) -@test all(C[11:20] .=== missing) - -append!(A, B) -@test A[1:10] == collect(1:10) -@test all(A[11:20] .=== missing) - -# make sure we bail when can't find a new automatic sentinel -A = SentinelArray([i for i = 0x00:0xff]) -@test_throws SentinelCollisionError setindex!(A, A.sentinel, 1) - -@test_throws ErrorException SentinelVector{Bool}(undef, 1) - -t = SentinelVector{Tuple{Int32, Int32}}(undef, 1) -@test t.data[1] === t.sentinel - -t = SentinelMatrix{Float64}(undef, (10, 10)) -@test size(t) == (10, 10) - -t = SentinelArray(collect(1:10)) -pushfirst!(t, 3, 2, 1) -@test t[1:3] == [3, 2, 1] - -prepend!(t, (i for i = 1:3 if i > 0)) -@test t[1:3] == [1, 2, 3] - -@test pop!(t) == 10 -empty!(t) -@test_throws ArgumentError pop!(t) -@test_throws ArgumentError popfirst!(t) - -x = SentinelArray(ones(3)) - -# broadcasting -@test x .+ x == 2 * ones(3) -@test x .+ x .+ x == 3 * ones(3) -@test (x .= 0 .* x .+ 7) == 7 * ones(3) - -v = SentinelArray(zeros(3,)) -m = SentinelArray(ones(3, 3)) -s = 0 - -@test v .+ m == ones(3, 3) == m .+ v -@test s .+ m == ones(3, 3) == m .+ s -@test s .+ v .+ m == ones(3, 3) == m .+ s .+ v - -casts = ( - SentinelArray, # Named Matrix - x->SentinelArray(x[:, 1]), # Named Vector - x->SentinelArray(x[:, 1:1]), # Named Single Column Matrix - identity, # Matrix - x->x[:, 1], # Vector - x->x[:, 1:1], # Single Column Matrix - first, # Scalar - ) -for (T1, T2, T3) in Iterators.product(casts, casts, casts) - all(isequal(identity), (T1, T2, T3)) && continue - !any(isequal(SentinelArray), (T1, T2, T3)) && continue - - total = T1(ones(3, 6)) .+ T2(2ones(3, 6)) .+ T3(3ones(3, 6)) - @test total == 6ones(3, 6) -end - -s = SentinelVector{Int}(undef, 2) -@test isequal(max.(s, 0), [missing, missing]) -@test (s .=== missing) isa BitArray - -s = SentinelArray(collect(1:5)) -c = ChainedVector([s, s, s]) -c2 = copy(c) -@test length(c) == length(c2) -@test c2 isa Vector - -# deleteat! of SentinelArray w/ underlying ChainedVector w/ UndefInitializer -x = SentinelArray(ChainedVector([Vector{String}(undef, 5)])) -deleteat!(x, 1) -@test length(x) == 4 - -end # @testset - - -@testset "MissingVector" begin - -x = MissingVector(10) -@test all(x .=== missing) -@test length(x) == 10 -@test length(x) isa Int -@test Base.IndexStyle(x) == Base.IndexLinear() - -y = similar(x, Missing, 5) -@test length(y) == 5 - -y = empty(x) -@test length(y) == 0 - -x[1] = missing -x[end] = missing -@test x[1] === missing -@test x[end] === missing - -y = similar(x) -@test typeof(y) == MissingVector -@test length(y) == 10 -y = similar(x, 20) -@test typeof(y) == MissingVector -@test length(y) == 20 - -@test isequal(copy(x), x) -empty!(x) -@test length(x) == 0 -@test isequal(copy(x), x) - -@test_throws ArgumentError resize!(x, -1) -resize!(x, 10) -@test length(x) == 10 - -push!(x, missing) -@test x[end] === missing -empty!(x) -push!(x, missing) -@test x[1] === x[end] === missing - -pushfirst!(x, missing) -@test x[1] === missing -empty!(x) -pushfirst!(x, missing) -@test x[1] === x[end] === missing -pushfirst!(x, missing) - -@test pop!(x) === missing -@test popfirst!(x) === missing -@test isempty(x) - -@test_throws BoundsError insert!(x, 0, missing) -@test_throws BoundsError insert!(x, 2, missing) -insert!(x, 1, missing) -@test x[1] === missing -insert!(x, 1, missing) -@test x[1] === missing - -x = MissingVector(10) -y = MissingVector(10) - -z = vcat(x, y) -@test length(z) == 20 - -empty!(x) -z = vcat(x, y) -@test isequal(z, y) - -x = MissingVector(10) -append!(x, y) -@test length(x) == 20 - -x = MissingVector(10) -append!(x, (missing for _ = 1:10)) -@test length(x) == 20 - -empty!(x) -append!(x, y) -@test isequal(x, y) - -x = MissingVector(10) -y = MissingVector(10) - -prepend!(y, x) -@test length(y) == 20 - -y = MissingVector(10) -prepend!(y, (missing for _ = 1:10)) -@test length(y) == 20 - -empty!(y) -prepend!(y, x) -@test isequal(y, x) - -x = MissingVector(10) -deleteat!(x, 1) -@test x[1] === missing -deleteat!(x, 1:4) -@test length(x) == 5 -deleteat!(x, [2, 4]) -@test length(x) == 3 - -m = similar(x) -@test length(m) == length(x) -m = similar(x, Missing) -@test length(m) == length(x) -@test typeof(m[1:3]) == typeof(m) - -deleteat!(x, [true, true, false]) -@test length(x) == 1 -empty!(x) -@test_throws ArgumentError pop!(x) -@test_throws ArgumentError popfirst!(x) - -m = MissingVector(5) -c = ChainedVector([m, m, m]) -c2 = copy(c) -@test length(c) == length(c2) -@test c2 isa MissingVector - -deleteat!(c2, Int[]) -@test length(c2) == 15 - -end - -include("chainedvector.jl") \ No newline at end of file +retest(SentinelArrays, SentinelArrayTests) \ No newline at end of file diff --git a/test/sentinelarrays.jl b/test/sentinelarrays.jl new file mode 100644 index 0000000..382b777 --- /dev/null +++ b/test/sentinelarrays.jl @@ -0,0 +1,248 @@ +@testset "SentinelArrays" begin + + # this is a very rare corner case + # we put this test first because it relies on + # the seeded random number generator for thread 1 + # the issue is if we had an underlying array of non-sentinel values + # then tried to `setindex!` to the _NEXT_ chosen sentinel value + # then the element getting set would end up `missing` (because it == the sentinel value) + # instead of the sentinel value getting cycled to something else + Random.seed!(0) + x = SentinelVector{Int64}(undef, 1) + x[1] = 1 + x.sentinel = 1369352191816061504 + x[1] = 1369352191816061504 + @test x[1] == 1369352191816061504 + + x = SentinelVector{Int}(undef, 10) + fill!(x, missing) + @test length(x) == 10 + @test all(isequal.(x.data, x.sentinel)) + + @test x[1] === missing + # force recoding + sent = x.sentinel + x[1] = x.sentinel + @test x[1] === sent + r = reverse(x) + @test x.sentinel == r.sentinel + + @test size(x) == (10,) + x[1] = 3 + @test x[1] === 3 + + resize!(x, length(x) + 1) + @test x[end] === missing + + x = SentinelVector{Int64}(undef, 1) + x[1] = missing + @test x[1] === missing + + x = SentinelArray{Float64,2}(undef, 10, 10) + @test size(x) == (10, 10) + x = SentinelArray{Float64}(undef, 10, 10) + @test size(x) == (10, 10) + + x = SentinelArray(fill(3.0, 10, 10)) + y = convert(SentinelArray{Int64}, x) + @test size(y) == (10, 10) + @test y isa SentinelArray{Int64} + @test all(y .=== Int64(3)) + + y = convert(SentinelVector{Int64}, x) + @test size(y) == (10, 10) + @test y isa SentinelArray{Int64} + @test all(y .=== Int64(3)) + + y = convert(SentinelArray, fill(Int64(3), 10, 10)) + @test size(y) == (10, 10) + @test size(y, 1) == 10 + @test axes(y, 1) == Base.OneTo(10) + @test stride(y, 1) == 1 + @test strides(y) == (1, 10) + @test y isa SentinelArray{Int64} + @test all(y .=== Int64(3)) + + x = SentinelVector{Union{Bool,Missing}}(undef, 1, missing, missing) + @test x[1] === missing + x[1] = true + @test x[1] === true + x[1] = missing + @test x[1] === missing + + x = SentinelArray(["$i" for i = 1:10]) + deleteat!(x, [9, 10]) + @test length(x) == 8 + @test x == ["$i" for i = 1:8] + deleteat!(x, [true, false, true, false, true, false, true, false]) + @test length(x) == 4 + @test x == ["2", "4", "6", "8"] + + x = SentinelVector{String}(undef, 10) + @test x[1] === missing + x[1] = "hey" + @test x[1] == "hey" + x[1] = missing + @test x[1] === missing + + @test SentinelArrays.newsentinel!(x) === nothing + + @test all(x .=== copy(x)) + @test length(empty!(x)) == 0 + @test length(resize!(x, 10)) == 10 + @test x[1] === missing + + push!(x, missing) + @test x[end] === missing + push!(x, "ho") + @test x[end] == "ho" + + deleteat!(x, length(x)) + @test x[end] === missing + @test length(x) == 11 + + empty!(x) + append!(x, ["hey", "ho", missing]) + @test length(x) == 3 + @test isequal(x, ["hey", "ho", missing]) + + pushfirst!(x, missing) + @test x[1] === missing + prepend!(x, [missing, "first"]) + @test x[1] === missing + @test x[2] == "first" + + @test popfirst!(x) === missing + @test x[1] == "first" + + insert!(x, 1, missing) + @test x[1] === missing + insert!(x, length(x) + 1, "pirate") + @test x[end] == "pirate" + + @test splice!(x, length(x), ["pirate2"]) == "pirate" + @test splice!(x, length(x)) == "pirate2" + @test splice!(x, length(x), ["pirate3", "pirate4"]) === missing + + @test splice!(x, (length(x)-1):length(x), ["pirate5", "pirate6"]) == ["pirate3", "pirate4"] + @test splice!(x, (length(x)-1):length(x), ["pirate7"]) == ["pirate5", "pirate6"] + @test splice!(x, length(x), ["pirate8", "pirate9"]) == "pirate7" + @test splice!(x, (length(x)-1):length(x), ["pirate10", "pirate11", "pirate12"]) == ["pirate8", "pirate9"] + @test splice!(x, (length(x)-2):length(x)) == ["pirate10", "pirate11", "pirate12"] + + t = [SentinelVector{Int64}(undef, 10), SentinelVector{Int64}(undef, 10), SentinelVector{Int64}(undef, 5)] + sent = t[1].sentinel + @test all(x -> x.sentinel == sent, t) + SentinelArrays.newsentinel!(t...; force=false) + # make sure nothing got recoded + @test all(x -> x.sentinel == sent, t) + SentinelArrays.newsentinel!(t...; force=true) + # make sure all got recoded + @test all(x -> x.sentinel != sent, t) + + # force recode of just one vector + sent = t[1].sentinel + t[2][1] = t[2].sentinel + @test !all(x -> x.sentinel == sent, t) + SentinelArrays.newsentinel!(t...) + # make sure all got recoded to same + @test all(x -> x.sentinel == t[1].sentinel, t) + + A = SentinelArray(Int64[i for i = 1:10]) + B = SentinelVector{Int64}(undef, 10) + + C = vcat(A, B) + @test C[1:10] == collect(1:10) + @test all(C[11:20] .=== missing) + + append!(A, B) + @test A[1:10] == collect(1:10) + @test all(A[11:20] .=== missing) + + A = SentinelArray(Int64[i for i = 1:10]) + B = SentinelVector{Int64}(undef, 10) + # force B to recode + B[1] = B.sentinel + B[1] = missing + + C = vcat(A, B) + @test C[1:10] == collect(1:10) + @test all(C[11:20] .=== missing) + + append!(A, B) + @test A[1:10] == collect(1:10) + @test all(A[11:20] .=== missing) + + # make sure we bail when can't find a new automatic sentinel + A = SentinelArray([i for i = 0x00:0xff]) + @test_throws SentinelCollisionError setindex!(A, A.sentinel, 1) + + @test_throws ErrorException SentinelVector{Bool}(undef, 1) + + t = SentinelVector{Tuple{Int32,Int32}}(undef, 1) + @test t.data[1] === t.sentinel + + t = SentinelMatrix{Float64}(undef, (10, 10)) + @test size(t) == (10, 10) + + t = SentinelArray(collect(1:10)) + pushfirst!(t, 3, 2, 1) + @test t[1:3] == [3, 2, 1] + + prepend!(t, (i for i = 1:3 if i > 0)) + @test t[1:3] == [1, 2, 3] + + @test pop!(t) == 10 + empty!(t) + @test_throws ArgumentError pop!(t) + @test_throws ArgumentError popfirst!(t) + + x = SentinelArray(ones(3)) + + # broadcasting + @test x .+ x == 2 * ones(3) + @test x .+ x .+ x == 3 * ones(3) + @test (x .= 0 .* x .+ 7) == 7 * ones(3) + + v = SentinelArray(zeros(3,)) + m = SentinelArray(ones(3, 3)) + s = 0 + + @test v .+ m == ones(3, 3) == m .+ v + @test s .+ m == ones(3, 3) == m .+ s + @test s .+ v .+ m == ones(3, 3) == m .+ s .+ v + + casts = ( + SentinelArray, # Named Matrix + x -> SentinelArray(x[:, 1]), # Named Vector + x -> SentinelArray(x[:, 1:1]), # Named Single Column Matrix + identity, # Matrix + x -> x[:, 1], # Vector + x -> x[:, 1:1], # Single Column Matrix + first, # Scalar + ) + for (T1, T2, T3) in Iterators.product(casts, casts, casts) + all(isequal(identity), (T1, T2, T3)) && continue + !any(isequal(SentinelArray), (T1, T2, T3)) && continue + + total = T1(ones(3, 6)) .+ T2(2ones(3, 6)) .+ T3(3ones(3, 6)) + @test total == 6ones(3, 6) + end + + s = SentinelVector{Int}(undef, 2) + @test isequal(max.(s, 0), [missing, missing]) + @test (s .=== missing) isa BitArray + + s = SentinelArray(collect(1:5)) + c = ChainedVector([s, s, s]) + c2 = copy(c) + @test length(c) == length(c2) + @test c2 isa Vector + + # deleteat! of SentinelArray w/ underlying ChainedVector w/ UndefInitializer + x = SentinelArray(ChainedVector([Vector{String}(undef, 5)])) + deleteat!(x, 1) + @test length(x) == 4 + +end # @testset + From ca220d403c03be6cfbc16c9352d88a3e5d3e3014 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 20:30:53 +0100 Subject: [PATCH 02/15] Added tests known to fail on current implementation. --- test/chainedvector.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 7fea81a..5443b46 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -576,4 +576,20 @@ end @test deleteat!(v1, m1) == deleteat!(s1, m1) @test deleteat!(v2, m2) == deleteat!(s2, m2) end +end + +@testset "Method ambiguities" begin + # some objects to use for testing current ambiguities + cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) + pda = PermutedDimsArray([1 2; 3 4], [2, 1]) + sv = SparseArrays.SparseVector(4, [2, 3], [2.0, 3.0]) + fix2in = Base.Fix2(in, [1, 2]) + + @test_broken ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any + @test_broken reduce(hcat, cv) isa Any + @test_broken reduce(vcat, cv) isa Any + @test_broken broadcasted(Base.Broadcast.BroadcastStyle(), cv) isa Any + @test_broken copyto!(pda, cv) isa Any + @test_broken copyto!(sv, cv) isa Any + @test_broken findall(fix2in, cv) isa Any end \ No newline at end of file From ed6ce80461e517bd9fefeb32bb93e12d7cfd1f93 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 20:43:20 +0100 Subject: [PATCH 03/15] Fixed some of the broken tests to be properly broken. --- test/chainedvector.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 5443b46..fc90361 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -581,15 +581,16 @@ end @testset "Method ambiguities" begin # some objects to use for testing current ambiguities cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) - pda = PermutedDimsArray([1 2; 3 4], [2, 1]) + cv_of_abstractvectors = ChainedVector([[1:10, 2:10], [3:10, 4:10]], [5, 6]) + pda_1dim = PermutedDimsArray([1, 2], [1,]) sv = SparseArrays.SparseVector(4, [2, 3], [2.0, 3.0]) fix2in = Base.Fix2(in, [1, 2]) @test_broken ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any - @test_broken reduce(hcat, cv) isa Any - @test_broken reduce(vcat, cv) isa Any + @test_broken reduce(hcat, cv_of_abstractvectors) isa Any + @test_broken reduce(vcat, cv_of_abstractvectors) isa Any @test_broken broadcasted(Base.Broadcast.BroadcastStyle(), cv) isa Any - @test_broken copyto!(pda, cv) isa Any + @test_broken copyto!(pda_1dim, cv) isa Any @test_broken copyto!(sv, cv) isa Any @test_broken findall(fix2in, cv) isa Any end \ No newline at end of file From d5588a135ab8c6a2c1690866eaafe4d145c6cbc0 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 19:23:46 +0100 Subject: [PATCH 04/15] Added BigInt along Integer in the definition of several comparisons for ChainedVectorIndex. --- src/chainedvector.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/chainedvector.jl b/src/chainedvector.jl index 7df46cd..7ceb3dd 100644 --- a/src/chainedvector.jl +++ b/src/chainedvector.jl @@ -204,8 +204,10 @@ end import Base: +, -, *, <, >, <=, >=, == for f in (:+, :-, :*, :<, :>, :<=, :>=, :(==)) - @eval $f(a::ChainedVectorIndex, b::Integer) = $f(a.i, b) - @eval $f(a::Integer, b::ChainedVectorIndex) = $f(a, b.i) + for inttype in (:Integer, :BigInt) + @eval $f(a::ChainedVectorIndex, b::$inttype) = $f(a.i, b) + @eval $f(a::$inttype, b::ChainedVectorIndex) = $f(a, b.i) + end @eval $f(a::ChainedVectorIndex, b::ChainedVectorIndex) = $f(a.i, b.i) end Base.convert(::Type{T}, x::ChainedVectorIndex) where {T <: Union{Signed, Unsigned}} = convert(T, x.i) From 67d948a98cc01e64e6a1c08a7ca83b4fff50f1a5 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 19:42:11 +0100 Subject: [PATCH 05/15] Also define Base.reduce for hcat and vcat. Undone autoformat in rebase. --- src/chainedvector.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chainedvector.jl b/src/chainedvector.jl index 7ceb3dd..d401e14 100644 --- a/src/chainedvector.jl +++ b/src/chainedvector.jl @@ -777,7 +777,9 @@ Base.any(x::ChainedVector) = any(y -> any(y), x.arrays) Base.all(f::Function, x::ChainedVector) = all(y -> all(f, y), x.arrays) Base.all(x::ChainedVector) = all(y -> all(y), x.arrays) -Base.reduce(op::OP, x::ChainedVector) where {OP} = reduce(op, (reduce(op, y) for y in x.arrays)) +for optype in (:Any, :hcat, :vcat) + @eval Base.reduce(op::typeof($optype), x::ChainedVector) = reduce(op, (reduce(op, y) for y in x.arrays)) +end Base.foldl(op::OP, x::ChainedVector) where {OP} = foldl(op, (foldl(op, y) for y in x.arrays)) Base.foldr(op::OP, x::ChainedVector) where {OP} = foldr(op, (foldr(op, y) for y in x.arrays)) Base.mapreduce(f::F, op::OP, x::ChainedVector) where {F, OP} = reduce(op, (mapreduce(f, op, y) for y in x.arrays)) From 8cc04a8ac1988c95cff4bf4b7c852736eb442957 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 20:59:55 +0100 Subject: [PATCH 06/15] Implemented methods for removing ambiguity in hcat and vcat. --- test/chainedvector.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index fc90361..940b0c6 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -581,14 +581,14 @@ end @testset "Method ambiguities" begin # some objects to use for testing current ambiguities cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) - cv_of_abstractvectors = ChainedVector([[1:10, 2:10], [3:10, 4:10]], [5, 6]) + cv_of_abstractvectors = ChainedVector([[1:4, 2:5], [3:6, 4:7]], [5, 6]) pda_1dim = PermutedDimsArray([1, 2], [1,]) sv = SparseArrays.SparseVector(4, [2, 3], [2.0, 3.0]) fix2in = Base.Fix2(in, [1, 2]) - @test_broken ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any - @test_broken reduce(hcat, cv_of_abstractvectors) isa Any - @test_broken reduce(vcat, cv_of_abstractvectors) isa Any + @test ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any + @test reduce(hcat, cv_of_abstractvectors) isa Any + @test reduce(vcat, cv_of_abstractvectors) isa Any @test_broken broadcasted(Base.Broadcast.BroadcastStyle(), cv) isa Any @test_broken copyto!(pda_1dim, cv) isa Any @test_broken copyto!(sv, cv) isa Any From 25beb567249b7b9c4e13ee2ab23f5aad3151beb2 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 21:10:26 +0100 Subject: [PATCH 07/15] Made broadcast test work as expected. --- test/chainedvector.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 940b0c6..8757995 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -589,7 +589,7 @@ end @test ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any @test reduce(hcat, cv_of_abstractvectors) isa Any @test reduce(vcat, cv_of_abstractvectors) isa Any - @test_broken broadcasted(Base.Broadcast.BroadcastStyle(), cv) isa Any + @test Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) isa Any @test_broken copyto!(pda_1dim, cv) isa Any @test_broken copyto!(sv, cv) isa Any @test_broken findall(fix2in, cv) isa Any From d49c10b9f59f1ec3eb3413e84a938692770bb83a Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 21:24:20 +0100 Subject: [PATCH 08/15] Reserve behaviour of Base.Broadcast.broadcasted(s::S, c::ChainedVector) where {S<:Base.Broadcast.BroadcastStyle}. Too difficult to know what to do. --- src/chainedvector.jl | 3 +++ test/chainedvector.jl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/chainedvector.jl b/src/chainedvector.jl index d401e14..5c5930a 100644 --- a/src/chainedvector.jl +++ b/src/chainedvector.jl @@ -931,3 +931,6 @@ Base.replace(a::ChainedVector, old_new::Pair...; count::Union{Integer,Nothing}=n Base.replace!(a::ChainedVector, old_new::Pair...; count::Integer=typemax(Int)) = (foreach(A -> replace!(A, old_new...; count=count), a.arrays); return a) Base.Broadcast.broadcasted(f::F, A::ChainedVector) where {F} = map(f, A) +function Base.Broadcast.broadcasted(s::S, c::ChainedVector) where {S<:Base.Broadcast.BroadcastStyle} + error("Broadcasting with BroadcastStyle $s and ChainedVector $c is reserved.") +end \ No newline at end of file diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 8757995..0be5225 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -589,7 +589,7 @@ end @test ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any @test reduce(hcat, cv_of_abstractvectors) isa Any @test reduce(vcat, cv_of_abstractvectors) isa Any - @test Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) isa Any + @test_throws "reserved" Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) @test_broken copyto!(pda_1dim, cv) isa Any @test_broken copyto!(sv, cv) isa Any @test_broken findall(fix2in, cv) isa Any From 0a7dd11d3945f19afba66016f46ab684bfe6268c Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 21:45:19 +0100 Subject: [PATCH 09/15] Fixed copyto! with a PermutedDimsArray{Any, 1} --- src/chainedvector.jl | 4 +++- test/chainedvector.jl | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/chainedvector.jl b/src/chainedvector.jl index 5c5930a..173aa77 100644 --- a/src/chainedvector.jl +++ b/src/chainedvector.jl @@ -382,7 +382,9 @@ end Base.copyto!(dest::AbstractVector, src::ChainedVector) = copyto!(dest, 1, src, 1, length(src)) -Base.copyto!(dest::AbstractVector, doffs::Union{Signed, Unsigned}, src::ChainedVector) = +Base.copyto!(dest::PermutedDimsArray{T, 1}, src::ChainedVector{T, A} where {A<:AbstractVector{T}}) where {T} = + copyto!(dest, 1, src, 1, length(src)) +Base.copyto!(dest::AbstractVector, doffs::Union{Signed,Unsigned}, src::ChainedVector) = copyto!(dest, doffs, src, 1, length(src)) Base.copyto!(dest::AbstractVector, doffs::Union{Signed, Unsigned}, src::ChainedVector, soffs::Union{Signed, Unsigned}) = copyto!(dest, doffs, src, soffs, length(src) - soffs + 1) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 0be5225..224ca69 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -582,7 +582,7 @@ end # some objects to use for testing current ambiguities cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) cv_of_abstractvectors = ChainedVector([[1:4, 2:5], [3:6, 4:7]], [5, 6]) - pda_1dim = PermutedDimsArray([1, 2], [1,]) + pda_1dim = PermutedDimsArray(1:6 |> collect, [1,]) sv = SparseArrays.SparseVector(4, [2, 3], [2.0, 3.0]) fix2in = Base.Fix2(in, [1, 2]) @@ -590,7 +590,7 @@ end @test reduce(hcat, cv_of_abstractvectors) isa Any @test reduce(vcat, cv_of_abstractvectors) isa Any @test_throws "reserved" Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) - @test_broken copyto!(pda_1dim, cv) isa Any + @test copyto!(pda_1dim, cv) isa Any @test_broken copyto!(sv, cv) isa Any @test_broken findall(fix2in, cv) isa Any end \ No newline at end of file From 09e01a9eca53e0a8277de92fbb2fd30828b55d19 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 21:50:53 +0100 Subject: [PATCH 10/15] Added comment explaning that one test is maybe NOT our responsibility. --- test/chainedvector.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 224ca69..5e7a464 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -583,7 +583,7 @@ end cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) cv_of_abstractvectors = ChainedVector([[1:4, 2:5], [3:6, 4:7]], [5, 6]) pda_1dim = PermutedDimsArray(1:6 |> collect, [1,]) - sv = SparseArrays.SparseVector(4, [2, 3], [2.0, 3.0]) + sv = SparseArrays.SparseVector(6, [2, 3], [2.0, 3.0]) fix2in = Base.Fix2(in, [1, 2]) @test ==(SentinelArrays.ChainedVectorIndex(1, 2, 3, 4), BigInt(21)) isa Any @@ -591,6 +591,7 @@ end @test reduce(vcat, cv_of_abstractvectors) isa Any @test_throws "reserved" Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) @test copyto!(pda_1dim, cv) isa Any - @test_broken copyto!(sv, cv) isa Any @test_broken findall(fix2in, cv) isa Any + # I think this should not be fixed by us as long as we don't import SparseArrays: + @test_throws MethodError copyto!(sv, cv) isa Any end \ No newline at end of file From 8ea444c37731f713269a09323d0512f3e0725a73 Mon Sep 17 00:00:00 2001 From: KeithWM Date: Wed, 15 Mar 2023 22:28:28 +0100 Subject: [PATCH 11/15] Fixed findall(Fix2{typeof(in)}, ::ChainedVector) ambiguity. --- src/chainedvector.jl | 1 + test/chainedvector.jl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chainedvector.jl b/src/chainedvector.jl index 173aa77..bbe47b6 100644 --- a/src/chainedvector.jl +++ b/src/chainedvector.jl @@ -906,6 +906,7 @@ function Base.findall(A::ChainedVector{Bool}) end Base.findall(f::Function, x::ChainedVector) = findall(map(f, x)) +Base.findall(f::Base.Fix2{typeof(in)}, x::ChainedVector) = findall(map(f, x)) function Base.filter(f, a::ChainedVector{T}) where {T} j = 1 diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 5e7a464..a0e70ff 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -591,7 +591,7 @@ end @test reduce(vcat, cv_of_abstractvectors) isa Any @test_throws "reserved" Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) @test copyto!(pda_1dim, cv) isa Any - @test_broken findall(fix2in, cv) isa Any + @test findall(fix2in, cv) isa Any # I think this should not be fixed by us as long as we don't import SparseArrays: @test_throws MethodError copyto!(sv, cv) isa Any end \ No newline at end of file From 7776fc4a4a7c5b87fcc804f4371dc17a5b3083eb Mon Sep 17 00:00:00 2001 From: KeithWM Date: Tue, 23 May 2023 07:50:46 +0200 Subject: [PATCH 12/15] Version bumped Julia to 1.6 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c6ceb02..b66d430 100644 --- a/Project.toml +++ b/Project.toml @@ -8,4 +8,4 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] -julia = "1.3" +julia = "1.6" From 8a279a126c28d8ff8e2c6dc95ced4ce014de14bf Mon Sep 17 00:00:00 2001 From: KeithWM Date: Tue, 23 May 2023 19:55:27 +0200 Subject: [PATCH 13/15] Added a test to cover the case of copyto!(::AbstractVector, ::Union{Signed, Unsigned}, ::ChainedVector} --- test/chainedvector.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index a0e70ff..74fd6d2 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -591,6 +591,7 @@ end @test reduce(vcat, cv_of_abstractvectors) isa Any @test_throws "reserved" Base.broadcasted(Base.Broadcast.ArrayStyle{Matrix}(), cv) @test copyto!(pda_1dim, cv) isa Any + @test copyto!(pda_1dim, 1, cv) isa Any @test findall(fix2in, cv) isa Any # I think this should not be fixed by us as long as we don't import SparseArrays: @test_throws MethodError copyto!(sv, cv) isa Any From e7f17671c9e93d3e5602dfc294e89f05335e469d Mon Sep 17 00:00:00 2001 From: KeithWM Date: Tue, 23 May 2023 20:18:30 +0200 Subject: [PATCH 14/15] Included a test case for the broadcasting of a ChainedVector --- test/chainedvector.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 74fd6d2..7356050 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -595,4 +595,19 @@ end @test findall(fix2in, cv) isa Any # I think this should not be fixed by us as long as we don't import SparseArrays: @test_throws MethodError copyto!(sv, cv) isa Any +end + +@testset "ChainedVectorIndex arithmetic" begin + # Metaprogramming defines these operations for (:+, :-, :*, :<, :>, :<=, :>=, :(==)), test only + + cvi = SentinelArrays.ChainedVectorIndex(1, 2, 3, 4) + @test cvi + 1 == 5 + @test cvi + BigInt(21) == 25 + @test 1 + cvi == 5 + @test BigInt(21) + cvi == 25 + @test cvi + cvi == 8 +end + +@testset "Broadcasting a ChainedVector" begin + cv = ChainedVector([[1, 2], [3, 4]], [5, 6]) + @test Base.Fix2(^, 2).(cv) |> collect == (1:4) .^ 2 |> collect end \ No newline at end of file From 9ae27d9d6146743be85a3688d1320a3ce6aa3eef Mon Sep 17 00:00:00 2001 From: KeithWM Date: Thu, 1 Jun 2023 22:45:30 +0200 Subject: [PATCH 15/15] Removed duplicated deleteat! testset. --- test/chainedvector.jl | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/chainedvector.jl b/test/chainedvector.jl index 7356050..1123b54 100644 --- a/test/chainedvector.jl +++ b/test/chainedvector.jl @@ -561,23 +561,6 @@ end end end -@testset "deleteat! with Bool mask" begin - x = SentinelArray(["a", "b", "c", "d", "e"]) - mask = [false, true, false, false, false] - @test deleteat!(x, mask) == ["a", "c", "d", "e"] - - for i in 1:100 - v1 = collect(string.(1:i)) - v2 = copy(v1) - s1 = SentinelArray(copy(v1)) - s2 = SentinelArray(copy(v1)) - m1 = rand(Bool, i) - m2 = BitVector(m1) - @test deleteat!(v1, m1) == deleteat!(s1, m1) - @test deleteat!(v2, m2) == deleteat!(s2, m2) - end -end - @testset "Method ambiguities" begin # some objects to use for testing current ambiguities cv = ChainedVector([[1, 2], [3, 4]], [5, 6])