Skip to content

Commit 9f6962b

Browse files
Merge pull request #84 from Workiva/hilbert_duplicates
Hilbert duplicates
2 parents f72bc81 + d3790dd commit 9f6962b

File tree

5 files changed

+122
-13
lines changed

5 files changed

+122
-13
lines changed

rtree/hilbert/action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (ga *getAction) nodes() []*node {
6262
}
6363

6464
func (ga *getAction) rects() []*hilbertBundle {
65-
return nil
65+
return []*hilbertBundle{&hilbertBundle{}}
6666
}
6767

6868
func newGetAction(rect rtree.Rectangle) *getAction {

rtree/hilbert/node.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,37 @@ type hilbert int64
2626

2727
type hilberts []hilbert
2828

29-
func getParent(parent *node, key hilbert) *node {
29+
func getParent(parent *node, key hilbert, r1 rtree.Rectangle) *node {
3030
var n *node
3131
for parent != nil && !parent.isLeaf {
3232
n = parent.searchNode(key)
3333
parent = n
3434
}
3535

36+
if parent != nil && r1 != nil { // must be leaf and we need exact match
37+
// we are safe to travel to the right
38+
i := parent.search(key)
39+
for parent.keys.byPosition(i) == key {
40+
if equal(parent.nodes.list[i], r1) {
41+
break
42+
}
43+
44+
i++
45+
if i == parent.keys.len() {
46+
if parent.right == nil { // we are far to the right
47+
break
48+
}
49+
50+
if parent.right.keys.byPosition(0) != key {
51+
break
52+
}
53+
54+
parent = parent.right
55+
i = 0
56+
}
57+
}
58+
}
59+
3660
return parent
3761
}
3862

@@ -195,8 +219,8 @@ type node struct {
195219

196220
func (n *node) insert(kb *keyBundle) rtree.Rectangle {
197221
i := n.keys.search(kb.key)
198-
if n.isLeaf && i != n.keys.len() { // we can have multiple keys with the same hilbert number
199-
for n.keys.list[i] == kb.key {
222+
if n.isLeaf { // we can have multiple keys with the same hilbert number
223+
for i < n.keys.len() && n.keys.list[i] == kb.key {
200224
if equal(n.nodes.list[i], kb.left) {
201225
old := n.nodes.list[i]
202226
n.nodes.list[i] = kb.left

rtree/hilbert/tree.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ type keyBundle struct {
5757

5858
type tree struct {
5959
root *node
60-
_padding0 [8]uint64
60+
_ [8]uint64
6161
number uint64
62-
_padding1 [8]uint64
62+
_ [8]uint64
6363
ary, bufferSize uint64
6464
actions *queue.RingBuffer
6565
cache []interface{}
66-
buffer0 [8]uint64
66+
_ [8]uint64
6767
disposed uint64
68-
buffer1 [8]uint64
68+
_ [8]uint64
6969
running uint64
7070
}
7171

@@ -169,7 +169,7 @@ func (tree *tree) fetchKeysInSerial(xns interfaces) {
169169
switch action.operation() {
170170
case add, remove:
171171
for i, key := range action.rects() {
172-
n := getParent(tree.root, key.hilbert)
172+
n := getParent(tree.root, key.hilbert, key.rect)
173173
action.addNode(int64(i), n)
174174
}
175175
case get:
@@ -217,16 +217,17 @@ func (tree *tree) fetchKeysInParallel(xns []interface{}) {
217217
action := xns[index].(action)
218218

219219
j := atomic.AddInt64(&forCache.js[index], 1)
220-
if j > int64(len(action.keys())) { // someone else is updating i
220+
if j > int64(len(action.rects())) { // someone else is updating i
221221
continue
222-
} else if j == int64(len(action.keys())) {
222+
} else if j == int64(len(action.rects())) {
223223
atomic.StoreInt64(&forCache.i, index+1)
224224
continue
225225
}
226226

227227
switch action.operation() {
228228
case add, remove:
229-
n := getParent(tree.root, action.keys()[j])
229+
hb := action.rects()[j]
230+
n := getParent(tree.root, hb.hilbert, hb.rect)
230231
action.addNode(j, n)
231232
case get:
232233
ga := action.(*getAction)
@@ -423,3 +424,8 @@ func newTree(bufferSize, ary uint64) *tree {
423424
tree.init(bufferSize, ary)
424425
return tree
425426
}
427+
428+
// New will construct a new Hilbert R-Tree and return it.
429+
func New(bufferSize, ary uint64) rtree.RTree {
430+
return newTree(bufferSize, ary)
431+
}

rtree/hilbert/tree_test.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func TestDeleteIdenticalHilbergNumber(t *testing.T) {
121121
}
122122

123123
func TestDeleteAll(t *testing.T) {
124-
points := constructRandomMockPoints(5)
124+
points := constructRandomMockPoints(3)
125125
tree := newTree(3, 3)
126126
tree.Insert(points...)
127127
assert.Equal(t, uint64(len(points)), tree.Len())
@@ -346,6 +346,56 @@ func TestMultipleInsertsCauseInternalSplitEvenAryRandomOrder(t *testing.T) {
346346
}
347347
}
348348

349+
func TestInsertDuplicateHilbert(t *testing.T) {
350+
r1 := newMockRectangle(0, 0, 20, 20)
351+
r2 := newMockRectangle(1, 1, 19, 19)
352+
r3 := newMockRectangle(2, 2, 18, 18)
353+
r4 := newMockRectangle(3, 3, 17, 17)
354+
tree := newTree(3, 3)
355+
tree.Insert(r1)
356+
tree.Insert(r2)
357+
tree.Insert(r3)
358+
tree.Insert(r4)
359+
360+
assert.Equal(t, uint64(4), tree.Len())
361+
q := newMockRectangle(0, 0, 30, 30)
362+
result := tree.Search(q)
363+
assert.Len(t, result, 4)
364+
assert.Contains(t, result, r1)
365+
assert.Contains(t, result, r2)
366+
assert.Contains(t, result, r3)
367+
assert.Contains(t, result, r4)
368+
}
369+
370+
func TestDeleteAllDuplicateHilbert(t *testing.T) {
371+
r1 := newMockRectangle(0, 0, 20, 20)
372+
r2 := newMockRectangle(1, 1, 19, 19)
373+
r3 := newMockRectangle(2, 2, 18, 18)
374+
r4 := newMockRectangle(3, 3, 17, 17)
375+
tree := newTree(3, 3)
376+
tree.Insert(r1)
377+
tree.Insert(r2)
378+
tree.Insert(r3)
379+
tree.Insert(r4)
380+
381+
tree.Delete(r1, r2, r3, r4)
382+
assert.Equal(t, uint64(0), tree.Len())
383+
result := tree.Search(constructInfiniteRect())
384+
assert.Len(t, result, 0)
385+
}
386+
387+
func TestInsertDuplicateRect(t *testing.T) {
388+
r1 := newMockRectangle(0, 0, 20, 20)
389+
r2 := newMockRectangle(0, 0, 20, 20)
390+
tree := newTree(3, 3)
391+
tree.Insert(r1)
392+
tree.Insert(r2)
393+
394+
assert.Equal(t, uint64(1), tree.Len())
395+
result := tree.Search(constructInfiniteRect())
396+
assert.Equal(t, rtree.Rectangles{r2}, result)
397+
}
398+
349399
func BenchmarkBulkAddPoints(b *testing.B) {
350400
numItems := 1000
351401
points := constructMockPoints(numItems)
@@ -408,3 +458,16 @@ func BenchmarkQueryBulkPoints(b *testing.B) {
408458
tree.Search(newMockRectangle(i, i, int32(numItems), int32(numItems)))
409459
}
410460
}
461+
462+
func BenchmarkDelete(b *testing.B) {
463+
numItems := b.N
464+
points := constructMockPoints(numItems)
465+
tree := newTree(8, 8)
466+
tree.Insert(points...)
467+
468+
b.ResetTimer()
469+
470+
for i := 0; i < b.N; i++ {
471+
tree.Delete(points[i%numItems])
472+
}
473+
}

rtree/interface.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,19 @@ type Rectangle interface {
2626
// UpperRight describes the upper right coordinate of this rectangle.
2727
UpperRight() (int32, int32)
2828
}
29+
30+
// RTree defines an object that can be returned from any subpackage
31+
// of this package.
32+
type RTree interface {
33+
// Search will perform an intersection search of the given
34+
// rectangle and return any rectangles that intersect.
35+
Search(Rectangle) Rectangles
36+
// Len returns in the number of items in the RTree.
37+
Len() uint64
38+
// Dispose will clean up any objects used by the RTree.
39+
Dispose()
40+
// Delete will remove the provided rectangles from the RTree.
41+
Delete(...Rectangle)
42+
// Insert will add the provided rectangles to the RTree.
43+
Insert(...Rectangle)
44+
}

0 commit comments

Comments
 (0)