-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into compathelper/new_version/2024-06-26-00-08-39…
…-685-03276309031
- Loading branch information
Showing
7 changed files
with
213 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Data structure that behaves like a `Vector{Vector}`, but uses a contiguous memory layout. | ||
# Similar to `VectorOfVectors` of ArraysOfArrays.jl, but allows to resize the inner vectors. | ||
struct DynamicVectorOfVectors{T, ARRAY2D, ARRAY1D} <: AbstractVector{Array{T, 1}} | ||
backend::ARRAY2D # Array{T, 2}, where each column represents a vector | ||
length_::Base.RefValue{Int32} # Number of vectors | ||
lengths::ARRAY1D # Array{Int32, 1} storing the lengths of the vectors | ||
end | ||
|
||
function DynamicVectorOfVectors{T}(; max_outer_length, max_inner_length) where {T} | ||
backend = Array{T, 2}(undef, max_inner_length, max_outer_length) | ||
length_ = Ref(zero(Int32)) | ||
lengths = zeros(Int32, max_outer_length) | ||
|
||
return DynamicVectorOfVectors{T, typeof(backend), typeof(lengths)}(backend, length_, | ||
lengths) | ||
end | ||
|
||
@inline Base.size(vov::DynamicVectorOfVectors) = (vov.length_[],) | ||
|
||
@inline function Base.getindex(vov::DynamicVectorOfVectors, i) | ||
(; backend, lengths) = vov | ||
|
||
@boundscheck checkbounds(vov, i) | ||
|
||
return view(backend, 1:lengths[i], i) | ||
end | ||
|
||
@inline function Base.push!(vov::DynamicVectorOfVectors, vector::AbstractVector) | ||
(; backend, length_, lengths) = vov | ||
|
||
# This data structure only supports one-based indexing | ||
Base.require_one_based_indexing(vector) | ||
|
||
# Activate a new column of `backend` | ||
j = length_[] += 1 | ||
lengths[j] = length(vector) | ||
|
||
# Fill the new column | ||
for i in eachindex(vector) | ||
backend[i, j] = vector[i] | ||
end | ||
|
||
return vov | ||
end | ||
|
||
@inline function Base.push!(vov::DynamicVectorOfVectors, vector::AbstractVector, vectors...) | ||
push!(vov, vector) | ||
push!(vov, vectors...) | ||
end | ||
|
||
# `push!(vov[i], value)` | ||
@inline function pushat!(vov::DynamicVectorOfVectors, i, value) | ||
(; backend, lengths) = vov | ||
|
||
@boundscheck checkbounds(vov, i) | ||
|
||
# Activate new entry in column `i` | ||
backend[lengths[i] += 1, i] = value | ||
|
||
return vov | ||
end | ||
|
||
# `deleteat!(vov[i], j)` | ||
@inline function deleteatat!(vov::DynamicVectorOfVectors, i, j) | ||
(; backend, lengths) = vov | ||
|
||
# Outer bounds check | ||
@boundscheck checkbounds(vov, i) | ||
# Inner bounds check | ||
@boundscheck checkbounds(1:lengths[i], j) | ||
|
||
# Replace value to delete by the last value in this column | ||
last_value = backend[lengths[i], i] | ||
backend[j, i] = last_value | ||
|
||
# Remove the last value in this column | ||
lengths[i] -= 1 | ||
|
||
return vov | ||
end | ||
|
||
@inline function Base.empty!(vov::DynamicVectorOfVectors) | ||
# Move all pointers to the beginning | ||
vov.lengths .= zero(Int32) | ||
vov.length_[] = zero(Int32) | ||
|
||
return vov | ||
end | ||
|
||
# `empty!(vov[i])` | ||
@inline function emptyat!(vov::DynamicVectorOfVectors, i) | ||
# Move length pointer to the beginning | ||
vov.lengths[i] = zero(Int32) | ||
|
||
return vov | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
@testset verbose=true "`DynamicVectorOfVectors`" begin | ||
# Test different types by defining a function to convert to this type | ||
types = [Int32, Float64, i -> (i, i)] | ||
|
||
@testset verbose=true "Eltype $(eltype(type(1)))" for type in types | ||
ELTYPE = typeof(type(1)) | ||
vov_ref = Vector{Vector{ELTYPE}}() | ||
vov = PointNeighbors.DynamicVectorOfVectors{ELTYPE}(max_outer_length = 20, | ||
max_inner_length = 10) | ||
|
||
# Test internal size | ||
@test size(vov.backend) == (10, 20) | ||
|
||
function verify(vov, vov_ref) | ||
@test length(vov) == length(vov_ref) | ||
@test eachindex(vov) == eachindex(vov_ref) | ||
@test axes(vov) == axes(vov_ref) | ||
|
||
@test_throws BoundsError vov[0] | ||
@test_throws BoundsError vov[length(vov) + 1] | ||
|
||
for i in eachindex(vov_ref) | ||
@test vov[i] == vov_ref[i] | ||
end | ||
end | ||
|
||
# Initial check | ||
verify(vov, vov_ref) | ||
|
||
# First `push!` | ||
push!(vov_ref, type.([1, 2, 3])) | ||
push!(vov, type.([1, 2, 3])) | ||
|
||
verify(vov, vov_ref) | ||
|
||
# `push!` multiple items | ||
push!(vov_ref, type.([4]), type.([5, 6, 7, 8])) | ||
push!(vov, type.([4]), type.([5, 6, 7, 8])) | ||
|
||
verify(vov, vov_ref) | ||
|
||
# `push!` to an inner vector | ||
push!(vov_ref[1], type(12)) | ||
PointNeighbors.pushat!(vov, 1, type(12)) | ||
|
||
verify(vov, vov_ref) | ||
|
||
# Delete entry of inner vector. Note that this changes the order of the elements. | ||
deleteat!(vov_ref[3], 2) | ||
PointNeighbors.deleteatat!(vov, 3, 2) | ||
|
||
@test vov_ref[3] == type.([5, 7, 8]) | ||
@test vov[3] == type.([5, 8, 7]) | ||
|
||
# Delete second to last entry | ||
deleteat!(vov_ref[3], 2) | ||
PointNeighbors.deleteatat!(vov, 3, 2) | ||
|
||
@test vov_ref[3] == type.([5, 8]) | ||
@test vov[3] == type.([5, 7]) | ||
|
||
# Delete last entry | ||
deleteat!(vov_ref[3], 2) | ||
PointNeighbors.deleteatat!(vov, 3, 2) | ||
|
||
# Now they are identical again | ||
verify(vov, vov_ref) | ||
|
||
# Delete the remaining entry of this vector | ||
deleteat!(vov_ref[3], 1) | ||
PointNeighbors.deleteatat!(vov, 3, 1) | ||
|
||
verify(vov, vov_ref) | ||
|
||
# `empty!` | ||
empty!(vov_ref) | ||
empty!(vov) | ||
|
||
verify(vov, vov_ref) | ||
end | ||
end |