Skip to content

Commit 16df7f1

Browse files
committed
updates to prefix search and its keyed versions
1 parent a31c55a commit 16df7f1

File tree

4 files changed

+255
-3
lines changed

4 files changed

+255
-3
lines changed

src/prefixsearch/binarytreeprefixsearch.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,7 @@ Random.rand(rng::AbstractRNG, d::Random.SamplerTrivial{BinaryTreePrefixSearch{T}
200200
choose(d[], rand(rng, Uniform{T}(zero(T), d[].array[1])))
201201

202202

203-
Base.haskey(md::BinaryTreePrefixSearch, clock) = false
204-
205-
function Base.haskey(pst::BinaryTreePrefixSearch{T}, clock::Int) where {T}
203+
function Base.haskey(pst::BinaryTreePrefixSearch{T}, clock::Integer) where {T}
206204
if 0 < clock length(pst)
207205
return getindex(pst, clock) > zero(T)
208206
else

test/test_keyedprefixsearch.jl

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,94 @@ end
8585
keyed_scan = CompetingClocks.KeyedKeepPrefixSearch{String,typeof(prefix_tree)}(prefix_tree)
8686
scan_gymnasium(keyed_scan)
8787
end
88+
89+
@safetestset keyedkeep_copy = "KeyedKeepPrefixSearch copy!" begin
90+
using CompetingClocks
91+
92+
prefix_src = CompetingClocks.BinaryTreePrefixSearch{Float64}()
93+
src = CompetingClocks.KeyedKeepPrefixSearch{String,typeof(prefix_src)}(prefix_src)
94+
src["a"] = 1.0
95+
src["b"] = 2.0
96+
src["c"] = 3.0
97+
98+
prefix_dst = CompetingClocks.BinaryTreePrefixSearch{Float64}()
99+
dst = CompetingClocks.KeyedKeepPrefixSearch{String,typeof(prefix_dst)}(prefix_dst)
100+
dst["x"] = 0.0
101+
dst["y"] = 0.0
102+
dst["z"] = 0.0
103+
104+
copy!(dst, src)
105+
106+
@test dst["a"] == 1.0
107+
@test dst["b"] == 2.0
108+
@test dst["c"] == 3.0
109+
@test length(dst) == 3
110+
end
111+
112+
@safetestset keyedkeep_key_type = "KeyedKeepPrefixSearch key_type" begin
113+
using CompetingClocks
114+
using CompetingClocks: key_type
115+
116+
prefix = CompetingClocks.BinaryTreePrefixSearch{Float64}()
117+
kp_string = CompetingClocks.KeyedKeepPrefixSearch{String,typeof(prefix)}(prefix)
118+
@test key_type(kp_string) == String
119+
120+
prefix2 = CompetingClocks.BinaryTreePrefixSearch{Float64}()
121+
kp_int = CompetingClocks.KeyedKeepPrefixSearch{Int,typeof(prefix2)}(prefix2)
122+
@test key_type(kp_int) == Int
123+
end
124+
125+
@safetestset keyedkeep_prefixenabled = "PrefixEnabled in and eltype" begin
126+
using CompetingClocks
127+
using CompetingClocks: enabled, isenabled
128+
129+
prefix = CompetingClocks.BinaryTreePrefixSearch{Float64}()
130+
kp = CompetingClocks.KeyedKeepPrefixSearch{String,typeof(prefix)}(prefix)
131+
kp["a"] = 1.0
132+
kp["b"] = 0.0 # disabled (zero)
133+
kp["c"] = 3.0
134+
135+
en = enabled(kp)
136+
137+
# Test `in` for PrefixEnabled
138+
@test "a" in en
139+
@test !("b" in en) # zero value means not enabled
140+
@test "c" in en
141+
@test !("d" in en) # not present
142+
143+
# Test eltype
144+
@test eltype(typeof(en)) == String
145+
end
146+
147+
@safetestset keyedremoval_key_time_type = "KeyedRemovalPrefixSearch key_type and time_type" begin
148+
using CompetingClocks
149+
using CompetingClocks: key_type, time_type
150+
151+
prefix = CompetingClocks.BinaryTreePrefixSearch{Float64}()
152+
kp = CompetingClocks.KeyedRemovalPrefixSearch{String,typeof(prefix)}(prefix)
153+
154+
@test key_type(kp) == String
155+
@test time_type(kp) == Float64
156+
end
157+
158+
@safetestset keyedremoval_update_existing = "KeyedRemovalPrefixSearch update existing clock" begin
159+
using CompetingClocks
160+
161+
prefix = CompetingClocks.BinaryTreePrefixSearch{Float64}()
162+
kp = CompetingClocks.KeyedRemovalPrefixSearch{String,typeof(prefix)}(prefix)
163+
164+
# Add initial values
165+
kp["a"] = 1.0
166+
kp["b"] = 2.0
167+
168+
@test kp["a"] == 1.0
169+
@test kp["b"] == 2.0
170+
171+
# Update existing clock (not delete and re-add, just update)
172+
kp["a"] = 5.0
173+
kp["b"] = 6.0
174+
175+
@test kp["a"] == 5.0
176+
@test kp["b"] == 6.0
177+
@test length(kp) == 2
178+
end

test/test_prefixsearch.jl

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,58 @@ end
4444
end
4545
end
4646

47+
@safetestset cumsumprefix_copy = "cumsum prefix copy!" begin
48+
using CompetingClocks: CumSumPrefixSearch
49+
50+
src = CumSumPrefixSearch{Float64}()
51+
for v in [1.0, 2.0, 3.0]
52+
push!(src, v)
53+
end
54+
sum!(src) # populate cumulant
55+
56+
dst = CumSumPrefixSearch{Float64}()
57+
for v in [0.0, 0.0, 0.0]
58+
push!(dst, v)
59+
end
60+
61+
copy!(dst, src)
62+
63+
@test dst.array == src.array
64+
@test dst.cumulant == src.cumulant
65+
@test dst.dirty == src.dirty
66+
@test length(dst) == length(src)
67+
end
68+
69+
@safetestset cumsumprefix_time_type = "cumsum prefix time_type instance" begin
70+
using CompetingClocks: CumSumPrefixSearch, time_type
71+
72+
ps_float = CumSumPrefixSearch{Float64}()
73+
@test time_type(ps_float) == Float64
74+
75+
ps_float32 = CumSumPrefixSearch{Float32}()
76+
@test time_type(ps_float32) == Float32
77+
end
78+
79+
@safetestset cumsumprefix_isenabled = "cumsum prefix isenabled" begin
80+
using CompetingClocks: CumSumPrefixSearch, isenabled
81+
82+
ps = CumSumPrefixSearch{Float64}()
83+
push!(ps, 1.0)
84+
push!(ps, 0.0) # zero value
85+
push!(ps, 3.0)
86+
87+
# Enabled clocks (positive value)
88+
@test isenabled(ps, 1) == true
89+
@test isenabled(ps, 3) == true
90+
91+
# Disabled clock (zero value)
92+
@test isenabled(ps, 2) == false
93+
94+
# Out of bounds
95+
@test isenabled(ps, 0) == false
96+
@test isenabled(ps, 4) == false
97+
@test isenabled(ps, -1) == false
98+
end
4799

48100
@safetestset prefixsearch_structure = "test structure" begin
49101
using CompetingClocks: _btps_sizes
@@ -249,3 +301,76 @@ end
249301
@test length(btps1) == 0
250302
@test allocated(btps1) == Int(2^ceil(log2(cnt-1)))
251303
end
304+
305+
@safetestset prefixsearch_time_type = "time_type instance method" begin
306+
using CompetingClocks: BinaryTreePrefixSearch, time_type
307+
308+
btps_float = BinaryTreePrefixSearch{Float64}(4)
309+
@test time_type(btps_float) == Float64
310+
311+
btps_int = BinaryTreePrefixSearch{Int64}(4)
312+
@test time_type(btps_int) == Int64
313+
end
314+
315+
@safetestset prefixsearch_choose_error = "choose errors on invalid value" begin
316+
using CompetingClocks: BinaryTreePrefixSearch, choose, sum!
317+
318+
btps = BinaryTreePrefixSearch{Float64}(4)
319+
push!(btps, 1.0)
320+
push!(btps, 2.0)
321+
push!(btps, 3.0)
322+
323+
total = sum!(btps)
324+
@test total == 6.0
325+
326+
# Value equal to total should error
327+
@test_throws ErrorException choose(btps, 6.0)
328+
329+
# Value greater than total should error
330+
@test_throws ErrorException choose(btps, 7.0)
331+
end
332+
333+
@safetestset prefixsearch_haskey = "haskey methods" begin
334+
using CompetingClocks: BinaryTreePrefixSearch
335+
336+
btps = BinaryTreePrefixSearch{Float64}(4)
337+
push!(btps, 1.0)
338+
push!(btps, 0.0) # zero value
339+
push!(btps, 3.0)
340+
341+
# haskey with Int clock - positive value exists
342+
@test haskey(btps, 1) == true
343+
@test haskey(btps, 3) == true
344+
345+
# haskey with Int clock - zero value does not count as "has"
346+
@test haskey(btps, 2) == false
347+
348+
# haskey with Int clock - out of bounds
349+
@test haskey(btps, 0) == false
350+
@test haskey(btps, 4) == false
351+
@test haskey(btps, -1) == false
352+
353+
# haskey works with any Integer type
354+
@test haskey(btps, Int32(1)) == true
355+
@test haskey(btps, UInt(3)) == true
356+
end
357+
358+
@safetestset prefixsearch_rand_sampler = "rand with SamplerTrivial" begin
359+
using Random
360+
using CompetingClocks: BinaryTreePrefixSearch
361+
362+
btps = BinaryTreePrefixSearch{Float64}(4)
363+
push!(btps, 1.0)
364+
push!(btps, 2.0)
365+
push!(btps, 3.0)
366+
push!(btps, 4.0)
367+
368+
rng = Xoshiro(12345)
369+
370+
# Sample multiple times and verify results are valid
371+
for _ in 1:100
372+
idx, remainder = rand(rng, btps)
373+
@test 1 <= idx <= 4
374+
@test remainder >= 0.0
375+
end
376+
end

test/test_setofsets.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,41 @@ end
195195
@test !("baz" in sos)
196196
@test length(sos) == 4
197197
end
198+
199+
@safetestset setofsets_union_two_sos = "SetOfSets union of two SetOfSets" begin
200+
using CompetingClocks: SetOfSets
201+
202+
a = Set([1, 2])
203+
b = Set([3, 4])
204+
sos1 = SetOfSets([a, b])
205+
206+
c = Set([5, 6])
207+
d = Set([7, 8])
208+
sos2 = SetOfSets([c, d])
209+
210+
# Union of two SetOfSets
211+
result = union(sos1, sos2)
212+
@test result isa SetOfSets
213+
@test Set(collect(result)) == Set(1:8)
214+
@test length(result) == 8
215+
end
216+
217+
@safetestset setofsets_isempty_method = "SetOfSets isempty" begin
218+
using CompetingClocks: SetOfSets
219+
220+
# Non-empty SetOfSets
221+
a = Set([1, 2])
222+
b = Set([3, 4])
223+
sos = SetOfSets([a, b])
224+
@test !isempty(sos)
225+
226+
# Empty SetOfSets (no subsets)
227+
empty_sos = SetOfSets(Set{Int}[])
228+
@test isempty(empty_sos)
229+
230+
# SetOfSets with only empty subsets
231+
empty_a = Set{Int}()
232+
empty_b = Set{Int}()
233+
sos_empty_subsets = SetOfSets([empty_a, empty_b])
234+
@test isempty(sos_empty_subsets)
235+
end

0 commit comments

Comments
 (0)