-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Finally. I should really learn to commit more often. I've already spent weeks on this.
- Loading branch information
0 parents
commit bf127a6
Showing
8 changed files
with
789 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Copyright (c) 2012 Seppe Stas. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are | ||
met: | ||
|
||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above | ||
copyright notice, this list of conditions and the following disclaimer | ||
in the documentation and/or other materials provided with the | ||
distribution. | ||
* Neither the name of Seppe Stas nor the names of its | ||
contributors may be used to endorse or promote products derived from | ||
this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
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,74 @@ | ||
# Go segtree | ||
|
||
A segment tree implementation written in Go. | ||
|
||
This library allows storing and retrieving elements indexed by a range. It does this by storing the elements in a segment tree. | ||
|
||
Based on: | ||
- [go-stree](https://github.com/toberndo/go-stree) by @toberndo | ||
- Chapter 10.3 of [Computational Geometry: Algorithms and Applications](http://www.cs.uu.nl/geobook/) rev. 3 by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars (ISBN 978-3-540-77973-5) | ||
|
||
The elements are sent to a channel as soon as they are found in the tree. This allows efficient querying of e.g multi-dimensional trees (trees containing trees). The elements are not sent in any specific order, however each found element will only be sent once. | ||
|
||
# Example usages: | ||
|
||
```go | ||
tree := new(segtree.Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.BuildTree() | ||
|
||
results, err := tree.QueryIndex(4) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query tree: %s", err.Error())) | ||
} | ||
|
||
result := <-results | ||
fmt.Println("Found:", result) | ||
``` | ||
|
||
An exemplary 2-dimensional segment tree as a tree in a tree: | ||
```go | ||
inner := new(segtree.Tree) | ||
inner.Push(1, 10, "hello, world") | ||
inner.BuildTree() | ||
|
||
outer := new(Tree) | ||
outer.Push(0, 99, inner) | ||
outer.BuildTree() | ||
|
||
resultsOuter, err := outer.QueryIndex(10) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query outer tree: %s", err.Error())) | ||
} | ||
|
||
result := <-resultsOuter | ||
resultsInner, err := result.(*Tree).QueryIndex(4) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query inner tree: %s", err.Error())) | ||
} | ||
|
||
result = <-resultsInner | ||
fmt.Println("Found:", result.(string)) | ||
``` | ||
|
||
The library also allows to pretty print the content of the tree for easy debugging. | ||
|
||
Example: | ||
```go | ||
tree := new(segtree.Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.Push(5, 6, "how are you today?") | ||
tree.Push(9, 45, "test") | ||
tree.BuildTree() | ||
|
||
tree.Print() | ||
``` | ||
|
||
## State of this package | ||
|
||
I'm still experimenting with walking the tree concurrently using go routines, but at first sight this does not have any real benefits. | ||
I will commit my benchmarks once I have them properly defined so that performance changes can be tracked consistently. | ||
|
||
I'm also still experimenting with "real" multi-dimensional segment trees. | ||
|
||
However there are no planned changes to the interface of this package, so it should be safe to use it in your project. |
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,107 @@ | ||
package segtree | ||
|
||
import "fmt" | ||
|
||
func ExampleTree_QueryIndex() { | ||
tree := new(Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.BuildTree() | ||
|
||
results, err := tree.QueryIndex(4) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query tree: %s", err.Error())) | ||
} | ||
|
||
result := <-results | ||
fmt.Println("Found:", result) | ||
|
||
// Output: Found: hello, world | ||
} | ||
|
||
func ExampleTree_QueryIndex_multiple_elements() { | ||
tree := new(Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.Push(5, 15, 3.14) | ||
tree.BuildTree() | ||
|
||
results, err := tree.QueryIndex(6) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query tree: %s", err.Error())) | ||
} | ||
|
||
for result := range results { | ||
fmt.Println("Found:", result) | ||
} | ||
|
||
// Output: | ||
// Found: hello, world | ||
// Found: 3.14 | ||
} | ||
|
||
func ExampleTree_QueryIndex_2_dimensional() { | ||
inner := new(Tree) | ||
inner.Push(1, 10, "hello, world") | ||
inner.BuildTree() | ||
|
||
outer := new(Tree) | ||
outer.Push(0, 99, inner) | ||
outer.BuildTree() | ||
|
||
resultsOuter, err := outer.QueryIndex(10) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query outer tree: %s", err.Error())) | ||
} | ||
|
||
result := <-resultsOuter | ||
resultsInner, err := result.(*Tree).QueryIndex(4) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query inner tree: %s", err.Error())) | ||
} | ||
|
||
result = <-resultsInner | ||
fmt.Println("Found:", result.(string)) | ||
|
||
// Output: Found: hello, world | ||
} | ||
|
||
func ExampleTree_Clear() { | ||
tree := new(Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.BuildTree() | ||
|
||
tree.Clear() | ||
|
||
_, err := tree.QueryIndex(4) | ||
if err != nil { | ||
// The tree is empty. Trying to Query it will fail | ||
fmt.Println(fmt.Sprintf("Failed to query tree: %s", err.Error())) | ||
} | ||
|
||
tree.Push(1, 10, "I destroyed the world") | ||
tree.BuildTree() | ||
|
||
results, err := tree.QueryIndex(4) | ||
if err != nil { | ||
panic(fmt.Sprintf("Failed to query tree: %s", err.Error())) | ||
} | ||
|
||
result := <-results | ||
// "hello world" will not be found | ||
fmt.Println("Found:", result) | ||
|
||
// Output: | ||
// Failed to query tree: Tree is empty. Build the tree first | ||
// Found: I destroyed the world | ||
} | ||
|
||
func ExampleTree_Print() { | ||
tree := new(Tree) | ||
tree.Push(1, 10, "hello, world") | ||
tree.Push(5, 6, "how are you today?") | ||
tree.Push(9, 45, "test") | ||
tree.BuildTree() | ||
|
||
tree.Print() | ||
|
||
// Output is a pretty tree (note that the leafs are not always placed properly) | ||
} |
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,90 @@ | ||
package segtree | ||
|
||
import "fmt" | ||
|
||
func (n *node) print() { | ||
from := fmt.Sprintf("%d", n.segment.from) | ||
switch n.segment.from { | ||
case Inf: | ||
from = "+∞" | ||
case NegInf: | ||
from = "-∞" | ||
} | ||
to := fmt.Sprintf("%d", n.segment.to) | ||
switch n.segment.to { | ||
case Inf: | ||
to = "Inf" | ||
case NegInf: | ||
to = "NegInf" | ||
} | ||
fmt.Printf("(%s,%s)", from, to) | ||
if n.intervals != nil { | ||
fmt.Print("->[") | ||
for _, intrvl := range n.intervals { | ||
fmt.Printf("(%v,%v)=[%v]", intrvl.from, intrvl.to, intrvl.element) | ||
} | ||
fmt.Print("]") | ||
} | ||
|
||
} | ||
|
||
// Traverse tree recursively call enter when entering node, resp. leave | ||
func traverse(node *node, depth int, enter, leave func(*node, int)) { | ||
if node == nil { | ||
return | ||
} | ||
if enter != nil { | ||
enter(node, depth) | ||
} | ||
traverse(node.left, depth+1, enter, leave) | ||
traverse(node.right, depth+1, enter, leave) | ||
if leave != nil { | ||
leave(node, depth) | ||
} | ||
} | ||
|
||
// Returs log with base 2 of an int. | ||
func log2(num int) int { | ||
if num == 0 { | ||
return NegInf | ||
} | ||
i := -1 | ||
for num > 0 { | ||
num = num >> 1 | ||
i++ | ||
} | ||
return i | ||
} | ||
|
||
func space(n int) { | ||
for i := 0; i < n; i++ { | ||
fmt.Print(" ") | ||
} | ||
} | ||
|
||
// Print prints a binary tree recursively to sdout | ||
func (t *Tree) Print() { | ||
endpoints := len(t.base)*2 + 2 | ||
leaves := endpoints*2 - 3 | ||
height := 1 + log2(leaves) | ||
|
||
fmt.Println("Height:", height, ", leaves:", leaves) | ||
levels := make([][]*node, height+1) | ||
|
||
traverse(t.root, 0, func(n *node, depth int) { | ||
levels[depth] = append(levels[depth], n) | ||
}, nil) | ||
|
||
for i, level := range levels { | ||
for j, n := range level { | ||
space(12 * (len(levels) - 1 - i)) | ||
n.print() | ||
space(1 * (height - i)) | ||
|
||
if j-1%2 == 0 { | ||
space(2) | ||
} | ||
} | ||
fmt.Println() | ||
} | ||
} |
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,28 @@ | ||
package segtree | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestLog2(t *testing.T) { | ||
test := func(num, expected int) { | ||
if result := log2(num); result != expected { | ||
t.Errorf("Log₂(%d) should be %d, got %d", num, expected, result) | ||
} | ||
} | ||
|
||
// Log₂ of a negative integer is impossible, but instead it returns -1. | ||
// I did not want to bother with NaN errors. | ||
test(-10, -1) | ||
// Log₂ of 0 is indefined, but it's limit aprouches -∞. It returs NegInf, | ||
// which is equal to the minimal value of an integer. | ||
// I did not want to bother with Inf errors. | ||
test(1, 0) | ||
test(2, 1) | ||
test(3, 1) | ||
test(4, 2) | ||
test(9, 3) | ||
test(10, 3) | ||
test(1024, 10) | ||
|
||
} |
Oops, something went wrong.