From 1903e041e8a7f0de73807abbe38e53f83b6bbc7e Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Tue, 8 Aug 2023 19:02:36 -0500 Subject: [PATCH] feat: many methods for PrimitiveArray and ListOffsetArray (#2) * Implemented a type and overrode a Base function for it. * Implemented most methods for PrimitiveArray and ListOffsetArray. * ListOffsetArray's is_valid. * Some tweaks. * More robust is_valid(::ListOffsetArray). * Concrete types no longer contain abstract-typed fields. * Rename x/y -> layout. * Empty constructors and appending methods. * Done for now. --- src/AwkwardArray.jl | 144 +++++++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 103 ++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 2 deletions(-) diff --git a/src/AwkwardArray.jl b/src/AwkwardArray.jl index 2920ada..961898d 100644 --- a/src/AwkwardArray.jl +++ b/src/AwkwardArray.jl @@ -1,5 +1,147 @@ module AwkwardArray -# Write your package code here. +### Index ################################################################ + +Index8 = AbstractArray{Int8,1} +IndexU8 = AbstractArray{UInt8,1} +Index32 = AbstractArray{Int32,1} +IndexU32 = AbstractArray{UInt32,1} +Index64 = AbstractArray{Int64,1} +IndexBig = Union{Index32,IndexU32,Index64} + +### Content ############################################################## + +abstract type Content <: AbstractArray{T where T,1} end + +function Base.iterate(layout::Content) + start = firstindex(layout) + stop = lastindex(layout) + if stop >= start + layout[start], start + 1 + else + nothing + end +end + +function Base.iterate(layout::Content, state) + stop = lastindex(layout) + if stop >= state + layout[state], state + 1 + else + nothing + end +end + +function Base.size(layout::Content) + (length(layout),) +end + +### PrimitiveArray ####################################################### + +struct PrimitiveArray{T,ARRAY<:AbstractArray{T,1}} <: Content + data::ARRAY +end + +function PrimitiveArray{T}() where {T} + PrimitiveArray(Vector{T}([])) +end + +function is_valid(layout::PrimitiveArray) + true +end + +function Base.length(layout::PrimitiveArray) + length(layout.data) +end + +function Base.firstindex(layout::PrimitiveArray) + firstindex(layout.data) +end + +function Base.lastindex(layout::PrimitiveArray) + lastindex(layout.data) +end + +function Base.getindex(layout::PrimitiveArray, i::Int) + layout.data[i] +end + +function Base.getindex(layout::PrimitiveArray, r::UnitRange{Int}) + PrimitiveArray(layout.data[r]) +end + +function Base.:(==)(layout1::PrimitiveArray, layout2::PrimitiveArray) + layout1.data == layout2.data +end + +function push!(layout::PrimitiveArray{T}, x::T) where {T} + Base.push!(layout.data, x) +end + +### ListOffsetArray ###################################################### + +struct ListOffsetArray{INDEX<:IndexBig,CONTENT<:Content} <: Content + offsets::INDEX + content::CONTENT +end + +function ListOffsetArray{INDEX,CONTENT}() where {INDEX<:IndexBig} where {CONTENT<:Content} + AwkwardArray.ListOffsetArray(INDEX([0]), CONTENT()) +end + +function is_valid(layout::ListOffsetArray) + if length(layout.offsets) < 1 + return false + end + if layout.offsets[end] + firstindex(layout.content) - 1 > lastindex(layout.content) + return false + end + for i in eachindex(layout) + if layout.offsets[i] < 0 || layout.offsets[i+1] < layout.offsets[i] + return false + end + end + return true +end + +function Base.length(layout::ListOffsetArray) + length(layout.offsets) - 1 +end + +function Base.firstindex(layout::ListOffsetArray) + firstindex(layout.offsets) +end + +function Base.lastindex(layout::ListOffsetArray) + lastindex(layout.offsets) - 1 +end + +function Base.getindex(layout::ListOffsetArray, i::Int) + start = layout.offsets[i] + firstindex(layout.content) + stop = layout.offsets[i+1] + firstindex(layout.content) - 1 + layout.content[start:stop] +end + +function Base.getindex(layout::ListOffsetArray, r::UnitRange{Int}) + ListOffsetArray(layout.offsets[(r.start):(r.stop+1)], layout.content) +end + +function Base.:(==)(layout1::ListOffsetArray, layout2::ListOffsetArray) + if length(layout1) != length(layout2) + return false + else + for (x, y) in zip(layout1, layout2) + if x != y + return false + end + end + return true + end +end + +function end_list!(layout::ListOffsetArray) + Base.push!(layout.offsets, length(layout.content)) + layout.content +end end diff --git a/test/runtests.jl b/test/runtests.jl index 7c97e63..b91044b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,5 +2,106 @@ using AwkwardArray using Test @testset "AwkwardArray.jl" begin - # Write your tests here. + begin + layout = AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]) + @test AwkwardArray.is_valid(layout) + @test length(layout) == 5 + @test layout[2] == 2.2 + @test layout[end] == 5.5 + @test layout[end-1] == 4.4 + @test layout[2:4] == AwkwardArray.PrimitiveArray([2.2, 3.3, 4.4]) + tmp = 0.0 + for x in layout + @test x < 6 + tmp += x + end + @test tmp == 16.5 + end + + begin + layout = AwkwardArray.PrimitiveArray{Float64}() + @test length(layout) == 0 + AwkwardArray.push!(layout, 1.1) + @test length(layout) == 1 + AwkwardArray.push!(layout, 2.2) + @test length(layout) == 2 + AwkwardArray.push!(layout, 3.3) + @test length(layout) == 3 + AwkwardArray.push!(layout, 4.4) + @test length(layout) == 4 + AwkwardArray.push!(layout, 5.5) + @test length(layout) == 5 + @test layout == AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]) + end + + begin + layout = AwkwardArray.ListOffsetArray( + [0, 3, 3, 5], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]), + ) + @test AwkwardArray.is_valid(layout) + @test length(layout) == 3 + @test layout[1] == AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3]) + @test layout[end-1] == AwkwardArray.PrimitiveArray([]) + @test layout[end] == AwkwardArray.PrimitiveArray([4.4, 5.5]) + @test layout[1:2] == AwkwardArray.ListOffsetArray( + [0, 3, 3], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3]), + ) + tmp = 0 + for x in layout + @test length(x) <= 3 + tmp += length(x) + end + @test tmp == 5 + end + + begin + layout = AwkwardArray.ListOffsetArray( + [0, 3, 2, 5], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]), + ) + @test !AwkwardArray.is_valid(layout) + end + + begin + layout = AwkwardArray.ListOffsetArray( + [0, 3, 3, 6], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]), + ) + @test !AwkwardArray.is_valid(layout) + end + + begin + layout = AwkwardArray.ListOffsetArray( + [-1, 3, 3, 5], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]), + ) + @test !AwkwardArray.is_valid(layout) + end + + begin + layout = AwkwardArray.ListOffsetArray{ + AwkwardArray.Index64, + AwkwardArray.PrimitiveArray{Float64}, + }() + sublayout = layout.content + @test length(layout) == 0 + AwkwardArray.push!(sublayout, 1.1) + AwkwardArray.push!(sublayout, 2.2) + AwkwardArray.push!(sublayout, 3.3) + AwkwardArray.end_list!(layout) + @test length(layout) == 1 + AwkwardArray.end_list!(layout) + @test length(layout) == 2 + AwkwardArray.push!(sublayout, 4.4) + AwkwardArray.push!(sublayout, 5.5) + AwkwardArray.end_list!(layout) + @test length(layout) == 3 + @test layout == AwkwardArray.ListOffsetArray( + [0, 3, 3, 5], + AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]), + ) + end + end