Skip to content

Commit 4fc58fb

Browse files
committed
wip
1 parent ee1c3ba commit 4fc58fb

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

treap.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package interval
1111

1212
import (
1313
"math/rand"
14+
"sync"
1415
)
1516

1617
// node is the basic recursive data structure.
@@ -55,6 +56,58 @@ func NewTree[T any](cmp func(a, b T) (ll, rr, lr, rl int), items ...T) Tree[T] {
5556
return t
5657
}
5758

59+
// NewTreeConcurrent, convenience function for initializing the interval tree for large inputs (> 100_000).
60+
// A good value reference for jobs is the number of logical CPUs usable by the current process.
61+
func NewTreeConcurrent[T any](jobs int, cmp func(a, b T) (ll, rr, lr, rl int), items ...T) Tree[T] {
62+
if jobs <= 1 {
63+
return NewTree[T](cmp, items...)
64+
}
65+
66+
l := len(items)
67+
68+
chunkSize := l/jobs + 1
69+
if chunkSize < 10_000 {
70+
chunkSize = 10_000
71+
}
72+
73+
var wg sync.WaitGroup
74+
var chunk []T
75+
partialTrees := make(chan Tree[T])
76+
77+
// fan out
78+
for ; l > 0; l = len(items) {
79+
// partition input into chunks
80+
switch {
81+
case l > chunkSize:
82+
chunk = items[:chunkSize]
83+
items = items[chunkSize:]
84+
default: // rest
85+
chunk = items[:l]
86+
items = nil
87+
}
88+
89+
wg.Add(1)
90+
go func(chunk ...T) {
91+
defer wg.Done()
92+
partialTrees <- NewTree[T](cmp, chunk...)
93+
}(chunk...)
94+
}
95+
96+
// wait and close chan
97+
go func() {
98+
wg.Wait()
99+
close(partialTrees)
100+
}()
101+
102+
// fan in
103+
t := NewTree[T](cmp)
104+
for other := range partialTrees {
105+
// fast union, immutable is false
106+
t = t.Union(other, false, false)
107+
}
108+
return t
109+
}
110+
58111
// makeNode, create new node with item and random priority.
59112
func (t *Tree[T]) makeNode(item T) *node[T] {
60113
n := new(node[T])

treap_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ func TestNewTree(t *testing.T) {
7575
t.Errorf("String() = %v, want \"\"", "")
7676
}
7777

78+
tree = interval.NewTreeConcurrent(0, cmpUintInterval)
79+
80+
if tree.String() != "" {
81+
t.Errorf("String() = %v, want \"\"", "")
82+
}
83+
7884
w := new(strings.Builder)
7985
if err := tree.Fprint(w); err != nil {
8086
t.Fatal(err)
@@ -155,6 +161,44 @@ func TestNewTree(t *testing.T) {
155161
}
156162
}
157163

164+
func TestNewTreeConcurrent(t *testing.T) {
165+
t.Parallel()
166+
167+
ivals := genUintIvals(100_000)
168+
169+
tree1 := interval.NewTree(cmpUintInterval, ivals[0])
170+
tree2 := interval.NewTreeConcurrent(1, cmpUintInterval, ivals[0])
171+
172+
if !equalStatistics(tree1, tree2) {
173+
t.Fatal("New() differs with NewConcurrent(), statistics differ")
174+
}
175+
176+
tree1 = interval.NewTree(cmpUintInterval, ivals[:2]...)
177+
tree2 = interval.NewTreeConcurrent(2, cmpUintInterval, ivals[:2]...)
178+
179+
if !equalStatistics(tree1, tree2) {
180+
t.Fatal("New() differs with NewConcurrent(), statistics differ")
181+
}
182+
183+
tree1 = interval.NewTree(cmpUintInterval, ivals[:30_000]...)
184+
tree2 = interval.NewTreeConcurrent(3, cmpUintInterval, ivals[:30_000]...)
185+
186+
if !equalStatistics(tree1, tree2) {
187+
t.Log(tree1.Statistics())
188+
t.Log(tree2.Statistics())
189+
t.Fatal("New() differs with NewConcurrent(), statistics differ")
190+
}
191+
192+
tree1 = interval.NewTree(cmpUintInterval, ivals...)
193+
tree2 = interval.NewTreeConcurrent(4, cmpUintInterval, ivals...)
194+
195+
if !equalStatistics(tree1, tree2) {
196+
t.Log(tree1.Statistics())
197+
t.Log(tree2.Statistics())
198+
t.Fatal("New() differs with NewConcurrent(), statistics differ")
199+
}
200+
}
201+
158202
func TestTreeWithDups(t *testing.T) {
159203
t.Parallel()
160204

0 commit comments

Comments
 (0)