pkg/adt: add "visitLevel", make "IntervalTree" interface, more tests

Make "IntervalTree" an interface to abstract range tree interface

Signed-off-by: Gyuho Lee <leegyuho@amazon.com>
release-3.4
Gyuho Lee 2019-07-29 21:27:02 -07:00
parent 149e5dc291
commit 6917c495e8
3 changed files with 376 additions and 31 deletions

View File

@ -21,8 +21,7 @@ import (
)
func Example() {
ivt := &adt.IntervalTree{}
ivt := adt.NewIntervalTree()
ivt.Insert(adt.NewInt64Interval(1, 3), 123)
ivt.Insert(adt.NewInt64Interval(9, 13), 456)
ivt.Insert(adt.NewInt64Interval(7, 20), 789)

View File

@ -16,7 +16,9 @@ package adt
import (
"bytes"
"fmt"
"math"
"strings"
)
// Comparable is an interface for trichotomic comparisons.
@ -35,6 +37,17 @@ const (
red
)
func (c rbcolor) String() string {
switch c {
case black:
return "black"
case red:
return "black"
default:
panic(fmt.Errorf("unknown color %d", c))
}
}
// Interval implements a Comparable interval [begin, end)
// TODO: support different sorts of intervals: (a,b), [a,b], (a, b]
type Interval struct {
@ -160,22 +173,55 @@ func (x *intervalNode) visit(iv *Interval, nv nodeVisitor) bool {
return true
}
// IntervalValue represents a range tree node that contains a range and a value.
type IntervalValue struct {
Ivl Interval
Val interface{}
}
// IntervalTree represents a (mostly) textbook implementation of the
// "Introduction to Algorithms" (Cormen et al, 2nd ed.) chapter 13 red-black tree
// "Introduction to Algorithms" (Cormen et al, 3rd ed.) chapter 13 red-black tree
// and chapter 14.3 interval tree with search supporting "stabbing queries".
type IntervalTree struct {
type IntervalTree interface {
// Insert adds a node with the given interval into the tree.
Insert(ivl Interval, val interface{})
// Delete removes the node with the given interval from the tree, returning
// true if a node is in fact removed.
Delete(ivl Interval) bool
// Len gives the number of elements in the tree.
Len() int
// Height is the number of levels in the tree; one node has height 1.
Height() int
// MaxHeight is the expected maximum tree height given the number of nodes.
MaxHeight() int
// Visit calls a visitor function on every tree node intersecting the given interval.
// It will visit each interval [x, y) in ascending order sorted on x.
Visit(ivl Interval, ivv IntervalVisitor)
// Find gets the IntervalValue for the node matching the given interval
Find(ivl Interval) *IntervalValue
// Intersects returns true if there is some tree node intersecting the given interval.
Intersects(iv Interval) bool
// Contains returns true if the interval tree's keys cover the entire given interval.
Contains(ivl Interval) bool
// Stab returns a slice with all elements in the tree intersecting the interval.
Stab(iv Interval) []*IntervalValue
// Union merges a given interval tree into the receiver.
Union(inIvt IntervalTree, ivl Interval)
}
// NewIntervalTree returns a new interval tree.
func NewIntervalTree() IntervalTree {
return &intervalTree{}
}
type intervalTree struct {
root *intervalNode
count int
}
// Delete removes the node with the given interval from the tree, returning
// true if a node is in fact removed.
func (ivt *IntervalTree) Delete(ivl Interval) bool {
func (ivt *intervalTree) Delete(ivl Interval) bool {
z := ivt.find(ivl)
if z == nil {
return false
@ -217,7 +263,7 @@ func (ivt *IntervalTree) Delete(ivl Interval) bool {
return true
}
func (ivt *IntervalTree) deleteFixup(x *intervalNode) {
func (ivt *intervalTree) deleteFixup(x *intervalNode) {
for x != ivt.root && x.color() == black && x.parent != nil {
if x == x.parent.left {
w := x.parent.right
@ -282,7 +328,7 @@ func (ivt *IntervalTree) deleteFixup(x *intervalNode) {
}
// Insert adds a node with the given interval into the tree.
func (ivt *IntervalTree) Insert(ivl Interval, val interface{}) {
func (ivt *intervalTree) Insert(ivl Interval, val interface{}) {
var y *intervalNode
z := &intervalNode{iv: IntervalValue{ivl, val}, max: ivl.End, c: red}
x := ivt.root
@ -311,7 +357,7 @@ func (ivt *IntervalTree) Insert(ivl Interval, val interface{}) {
ivt.count++
}
func (ivt *IntervalTree) insertFixup(z *intervalNode) {
func (ivt *intervalTree) insertFixup(z *intervalNode) {
for z.parent != nil && z.parent.parent != nil && z.parent.color() == red {
if z.parent == z.parent.parent.left {
y := z.parent.parent.right
@ -352,7 +398,7 @@ func (ivt *IntervalTree) insertFixup(z *intervalNode) {
}
// rotateLeft moves x so it is left of its right child
func (ivt *IntervalTree) rotateLeft(x *intervalNode) {
func (ivt *intervalTree) rotateLeft(x *intervalNode) {
y := x.right
x.right = y.left
if y.left != nil {
@ -364,8 +410,8 @@ func (ivt *IntervalTree) rotateLeft(x *intervalNode) {
y.updateMax()
}
// rotateLeft moves x so it is right of its left child
func (ivt *IntervalTree) rotateRight(x *intervalNode) {
// rotateRight moves x so it is right of its left child
func (ivt *intervalTree) rotateRight(x *intervalNode) {
if x == nil {
return
}
@ -381,7 +427,7 @@ func (ivt *IntervalTree) rotateRight(x *intervalNode) {
}
// replaceParent replaces x's parent with y
func (ivt *IntervalTree) replaceParent(x *intervalNode, y *intervalNode) {
func (ivt *intervalTree) replaceParent(x *intervalNode, y *intervalNode) {
y.parent = x.parent
if x.parent == nil {
ivt.root = y
@ -397,13 +443,13 @@ func (ivt *IntervalTree) replaceParent(x *intervalNode, y *intervalNode) {
}
// Len gives the number of elements in the tree
func (ivt *IntervalTree) Len() int { return ivt.count }
func (ivt *intervalTree) Len() int { return ivt.count }
// Height is the number of levels in the tree; one node has height 1.
func (ivt *IntervalTree) Height() int { return ivt.root.height() }
func (ivt *intervalTree) Height() int { return ivt.root.height() }
// MaxHeight is the expected maximum tree height given the number of nodes
func (ivt *IntervalTree) MaxHeight() int {
func (ivt *intervalTree) MaxHeight() int {
return int((2 * math.Log2(float64(ivt.Len()+1))) + 0.5)
}
@ -412,12 +458,12 @@ type IntervalVisitor func(n *IntervalValue) bool
// Visit calls a visitor function on every tree node intersecting the given interval.
// It will visit each interval [x, y) in ascending order sorted on x.
func (ivt *IntervalTree) Visit(ivl Interval, ivv IntervalVisitor) {
func (ivt *intervalTree) Visit(ivl Interval, ivv IntervalVisitor) {
ivt.root.visit(&ivl, func(n *intervalNode) bool { return ivv(&n.iv) })
}
// find the exact node for a given interval
func (ivt *IntervalTree) find(ivl Interval) (ret *intervalNode) {
func (ivt *intervalTree) find(ivl Interval) (ret *intervalNode) {
f := func(n *intervalNode) bool {
if n.iv.Ivl != ivl {
return true
@ -430,7 +476,7 @@ func (ivt *IntervalTree) find(ivl Interval) (ret *intervalNode) {
}
// Find gets the IntervalValue for the node matching the given interval
func (ivt *IntervalTree) Find(ivl Interval) (ret *IntervalValue) {
func (ivt *intervalTree) Find(ivl Interval) (ret *IntervalValue) {
n := ivt.find(ivl)
if n == nil {
return nil
@ -439,7 +485,7 @@ func (ivt *IntervalTree) Find(ivl Interval) (ret *IntervalValue) {
}
// Intersects returns true if there is some tree node intersecting the given interval.
func (ivt *IntervalTree) Intersects(iv Interval) bool {
func (ivt *intervalTree) Intersects(iv Interval) bool {
x := ivt.root
for x != nil && iv.Compare(&x.iv.Ivl) != 0 {
if x.left != nil && x.left.max.Compare(iv.Begin) > 0 {
@ -452,7 +498,7 @@ func (ivt *IntervalTree) Intersects(iv Interval) bool {
}
// Contains returns true if the interval tree's keys cover the entire given interval.
func (ivt *IntervalTree) Contains(ivl Interval) bool {
func (ivt *intervalTree) Contains(ivl Interval) bool {
var maxEnd, minBegin Comparable
isContiguous := true
@ -476,7 +522,7 @@ func (ivt *IntervalTree) Contains(ivl Interval) bool {
}
// Stab returns a slice with all elements in the tree intersecting the interval.
func (ivt *IntervalTree) Stab(iv Interval) (ivs []*IntervalValue) {
func (ivt *intervalTree) Stab(iv Interval) (ivs []*IntervalValue) {
if ivt.count == 0 {
return nil
}
@ -486,7 +532,7 @@ func (ivt *IntervalTree) Stab(iv Interval) (ivs []*IntervalValue) {
}
// Union merges a given interval tree into the receiver.
func (ivt *IntervalTree) Union(inIvt IntervalTree, ivl Interval) {
func (ivt *intervalTree) Union(inIvt IntervalTree, ivl Interval) {
f := func(n *IntervalValue) bool {
ivt.Insert(n.Ivl, n.Val)
return true
@ -494,6 +540,64 @@ func (ivt *IntervalTree) Union(inIvt IntervalTree, ivl Interval) {
inIvt.Visit(ivl, f)
}
type visitedInterval struct {
root Interval
left Interval
right Interval
color rbcolor
depth int
}
func (vi visitedInterval) String() string {
bd := new(strings.Builder)
bd.WriteString(fmt.Sprintf("root [%v,%v,%v], left [%v,%v], right [%v,%v], depth %d",
vi.root.Begin, vi.root.End, vi.color,
vi.left.Begin, vi.left.End,
vi.right.Begin, vi.right.End,
vi.depth,
))
return bd.String()
}
// visitLevel traverses tree in level order.
// used for testing
func (ivt *intervalTree) visitLevel() []visitedInterval {
if ivt.root == nil {
return nil
}
rs := make([]visitedInterval, 0, ivt.Len())
type pair struct {
node *intervalNode
depth int
}
queue := []pair{{ivt.root, 0}}
for len(queue) > 0 {
f := queue[0]
queue = queue[1:]
ivt := visitedInterval{
root: f.node.iv.Ivl,
color: f.node.color(),
depth: f.depth,
}
if f.node.left != nil {
ivt.left = f.node.left.iv.Ivl
queue = append(queue, pair{f.node.left, f.depth + 1})
}
if f.node.right != nil {
ivt.right = f.node.right.iv.Ivl
queue = append(queue, pair{f.node.right, f.depth + 1})
}
rs = append(rs, ivt)
}
return rs
}
type StringComparable string
func (s StringComparable) Compare(c Comparable) int {
@ -543,6 +647,7 @@ func (s StringAffineComparable) Compare(c Comparable) int {
func NewStringAffineInterval(begin, end string) Interval {
return Interval{StringAffineComparable(begin), StringAffineComparable(end)}
}
func NewStringAffinePoint(s string) Interval {
return NewStringAffineInterval(s, s+"\x00")
}
@ -551,6 +656,10 @@ func NewInt64Interval(a int64, b int64) Interval {
return Interval{Int64Comparable(a), Int64Comparable(b)}
}
func newInt64EmptyInterval() Interval {
return Interval{Begin: nil, End: nil}
}
func NewInt64Point(a int64) Interval {
return Interval{Int64Comparable(a), Int64Comparable(a + 1)}
}
@ -591,6 +700,7 @@ func (b BytesAffineComparable) Compare(c Comparable) int {
func NewBytesAffineInterval(begin, end []byte) Interval {
return Interval{BytesAffineComparable(begin), BytesAffineComparable(end)}
}
func NewBytesAffinePoint(b []byte) Interval {
be := make([]byte, len(b)+1)
copy(be, b)

View File

@ -16,12 +16,247 @@ package adt
import (
"math/rand"
"reflect"
"testing"
"time"
)
// TestIntervalTreeInsert tests interval tree insertion.
func TestIntervalTreeInsert(t *testing.T) {
// "Introduction to Algorithms" (Cormen et al, 3rd ed.) chapter 14, Figure 14.4
ivt := NewIntervalTree()
ivt.Insert(NewInt64Interval(16, 21), 30)
ivt.Insert(NewInt64Interval(8, 9), 23)
ivt.Insert(NewInt64Interval(0, 3), 3)
ivt.Insert(NewInt64Interval(5, 8), 10)
ivt.Insert(NewInt64Interval(6, 10), 10)
ivt.Insert(NewInt64Interval(15, 23), 23)
ivt.Insert(NewInt64Interval(17, 19), 20)
ivt.Insert(NewInt64Interval(25, 30), 30)
ivt.Insert(NewInt64Interval(26, 26), 26)
ivt.Insert(NewInt64Interval(19, 20), 20)
expected := []visitedInterval{
{root: NewInt64Interval(16, 21), color: black, left: NewInt64Interval(8, 9), right: NewInt64Interval(25, 30), depth: 0},
{root: NewInt64Interval(8, 9), color: red, left: NewInt64Interval(5, 8), right: NewInt64Interval(15, 23), depth: 1},
{root: NewInt64Interval(25, 30), color: red, left: NewInt64Interval(17, 19), right: NewInt64Interval(26, 26), depth: 1},
{root: NewInt64Interval(5, 8), color: black, left: NewInt64Interval(0, 3), right: NewInt64Interval(6, 10), depth: 2},
{root: NewInt64Interval(15, 23), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 2},
{root: NewInt64Interval(17, 19), color: black, left: newInt64EmptyInterval(), right: NewInt64Interval(19, 20), depth: 2},
{root: NewInt64Interval(26, 26), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 2},
{root: NewInt64Interval(0, 3), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(6, 10), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(19, 20), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
}
tr := ivt.(*intervalTree)
visits := tr.visitLevel()
if !reflect.DeepEqual(expected, visits) {
t.Fatalf("level order expected %v, got %v", expected, visits)
}
}
// TestIntervalTreeSelfBalanced ensures range tree is self-balanced after inserting ranges to the tree.
// Use https://www.cs.usfca.edu/~galles/visualization/RedBlack.html for test case creation.
//
// Regular Binary Search Tree
// [0,1]
// \
// [1,2]
// \
// [3,4]
// \
// [5,6]
// \
// [7,8]
// \
// [8,9]
//
// Self-Balancing Binary Search Tree
// [1,2]
// / \
// [0,1] [5,6]
// / \
// [3,4] [7,8]
// \
// [8,9]
//
func TestIntervalTreeSelfBalanced(t *testing.T) {
ivt := NewIntervalTree()
ivt.Insert(NewInt64Interval(0, 1), 0)
ivt.Insert(NewInt64Interval(1, 2), 0)
ivt.Insert(NewInt64Interval(3, 4), 0)
ivt.Insert(NewInt64Interval(5, 6), 0)
ivt.Insert(NewInt64Interval(7, 8), 0)
ivt.Insert(NewInt64Interval(8, 9), 0)
expected := []visitedInterval{
{root: NewInt64Interval(1, 2), color: black, left: NewInt64Interval(0, 1), right: NewInt64Interval(5, 6), depth: 0},
{root: NewInt64Interval(0, 1), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 1},
{root: NewInt64Interval(5, 6), color: red, left: NewInt64Interval(3, 4), right: NewInt64Interval(7, 8), depth: 1},
{root: NewInt64Interval(3, 4), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 2},
{root: NewInt64Interval(7, 8), color: black, left: newInt64EmptyInterval(), right: NewInt64Interval(8, 9), depth: 2},
{root: NewInt64Interval(8, 9), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
}
tr := ivt.(*intervalTree)
visits := tr.visitLevel()
if !reflect.DeepEqual(expected, visits) {
t.Fatalf("level order expected %v, got %v", expected, visits)
}
if visits[len(visits)-1].depth != 3 {
t.Fatalf("expected self-balanced tree with last level 3, but last level got %d", visits[len(visits)-1].depth)
}
}
// TestIntervalTreeDelete ensures delete operation maintains red-black tree properties.
// Use https://www.cs.usfca.edu/~galles/visualization/RedBlack.html for test case creation.
// See https://github.com/etcd-io/etcd/issues/10877 for more detail.
//
//
// After insertion:
// [510,511]
// / \
// ---------- -----------------------
// / \
// [82,83] [830,831]
// / \ / \
// / \ / \
// [11,12] [383,384](red) [647,648] [899,900](red)
// / \ / \ / \
// / \ / \ / \
// [261,262] [410,411] [514,515](red) [815,816](red) [888,889] [972,973]
// / \ /
// / \ /
// [238,239](red) [292,293](red) [953,954](red)
//
//
// After deleting 514 (no rebalance):
// [510,511]
// / \
// ---------- -----------------------
// / \
// [82,83] [830,831]
// / \ / \
// / \ / \
// [11,12] [383,384](red) [647,648] [899,900](red)
// / \ \ / \
// / \ \ / \
// [261,262] [410,411] [815,816](red) [888,889] [972,973]
// / \ /
// / \ /
// [238,239](red) [292,293](red) [953,954](red)
//
//
// After deleting 11 (requires rebalancing):
// [510,511]
// / \
// ---------- --------------------------
// / \
// [383,384] [830,831]
// / \ / \
// / \ / \
// [261,262](red) [410,411] [647,648] [899,900](red)
// / \ \ / \
// / \ \ / \
// [82,83] [292,293] [815,816](red) [888,889] [972,973]
// \ /
// \ /
// [238,239](red) [953,954](red)
//
//
func TestIntervalTreeDelete(t *testing.T) {
ivt := NewIntervalTree()
ivt.Insert(NewInt64Interval(510, 511), 0)
ivt.Insert(NewInt64Interval(82, 83), 0)
ivt.Insert(NewInt64Interval(830, 831), 0)
ivt.Insert(NewInt64Interval(11, 12), 0)
ivt.Insert(NewInt64Interval(383, 384), 0)
ivt.Insert(NewInt64Interval(647, 648), 0)
ivt.Insert(NewInt64Interval(899, 900), 0)
ivt.Insert(NewInt64Interval(261, 262), 0)
ivt.Insert(NewInt64Interval(410, 411), 0)
ivt.Insert(NewInt64Interval(514, 515), 0)
ivt.Insert(NewInt64Interval(815, 816), 0)
ivt.Insert(NewInt64Interval(888, 889), 0)
ivt.Insert(NewInt64Interval(972, 973), 0)
ivt.Insert(NewInt64Interval(238, 239), 0)
ivt.Insert(NewInt64Interval(292, 293), 0)
ivt.Insert(NewInt64Interval(953, 954), 0)
tr := ivt.(*intervalTree)
expectedBeforeDelete := []visitedInterval{
{root: NewInt64Interval(510, 511), color: black, left: NewInt64Interval(82, 83), right: NewInt64Interval(830, 831), depth: 0},
{root: NewInt64Interval(82, 83), color: black, left: NewInt64Interval(11, 12), right: NewInt64Interval(383, 384), depth: 1},
{root: NewInt64Interval(830, 831), color: black, left: NewInt64Interval(647, 648), right: NewInt64Interval(899, 900), depth: 1},
{root: NewInt64Interval(11, 12), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 2},
{root: NewInt64Interval(383, 384), color: red, left: NewInt64Interval(261, 262), right: NewInt64Interval(410, 411), depth: 2},
{root: NewInt64Interval(647, 648), color: black, left: NewInt64Interval(514, 515), right: NewInt64Interval(815, 816), depth: 2},
{root: NewInt64Interval(899, 900), color: red, left: NewInt64Interval(888, 889), right: NewInt64Interval(972, 973), depth: 2},
{root: NewInt64Interval(261, 262), color: black, left: NewInt64Interval(238, 239), right: NewInt64Interval(292, 293), depth: 3},
{root: NewInt64Interval(410, 411), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(514, 515), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(815, 816), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(888, 889), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(972, 973), color: black, left: NewInt64Interval(953, 954), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(238, 239), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
{root: NewInt64Interval(292, 293), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
{root: NewInt64Interval(953, 954), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
}
visitsBeforeDelete := tr.visitLevel()
if !reflect.DeepEqual(expectedBeforeDelete, visitsBeforeDelete) {
t.Fatalf("level order after insertion expected %v, got %v", expectedBeforeDelete, visitsBeforeDelete)
}
// delete the node "514"
range514 := NewInt64Interval(514, 515)
if deleted := tr.Delete(NewInt64Interval(514, 515)); !deleted {
t.Fatalf("range %v not deleted", range514)
}
expectedAfterDelete514 := []visitedInterval{
{root: NewInt64Interval(510, 511), color: black, left: NewInt64Interval(82, 83), right: NewInt64Interval(830, 831), depth: 0},
{root: NewInt64Interval(82, 83), color: black, left: NewInt64Interval(11, 12), right: NewInt64Interval(383, 384), depth: 1},
{root: NewInt64Interval(830, 831), color: black, left: NewInt64Interval(647, 648), right: NewInt64Interval(899, 900), depth: 1},
{root: NewInt64Interval(11, 12), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 2},
{root: NewInt64Interval(383, 384), color: red, left: NewInt64Interval(261, 262), right: NewInt64Interval(410, 411), depth: 2},
{root: NewInt64Interval(647, 648), color: black, left: newInt64EmptyInterval(), right: NewInt64Interval(815, 816), depth: 2},
{root: NewInt64Interval(899, 900), color: red, left: NewInt64Interval(888, 889), right: NewInt64Interval(972, 973), depth: 2},
{root: NewInt64Interval(261, 262), color: black, left: NewInt64Interval(238, 239), right: NewInt64Interval(292, 293), depth: 3},
{root: NewInt64Interval(410, 411), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(815, 816), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(888, 889), color: black, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(972, 973), color: black, left: NewInt64Interval(953, 954), right: newInt64EmptyInterval(), depth: 3},
{root: NewInt64Interval(238, 239), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
{root: NewInt64Interval(292, 293), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
{root: NewInt64Interval(953, 954), color: red, left: newInt64EmptyInterval(), right: newInt64EmptyInterval(), depth: 4},
}
visitsAfterDelete514 := tr.visitLevel()
if !reflect.DeepEqual(expectedAfterDelete514, visitsAfterDelete514) {
t.Fatalf("level order after deleting '514' expected %v, got %v", expectedAfterDelete514, visitsAfterDelete514)
}
// TODO: validate deletion 11
}
func TestIntervalTreeIntersects(t *testing.T) {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
ivt.Insert(NewStringInterval("1", "3"), 123)
if ivt.Intersects(NewStringPoint("0")) {
@ -42,7 +277,7 @@ func TestIntervalTreeIntersects(t *testing.T) {
}
func TestIntervalTreeStringAffine(t *testing.T) {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
ivt.Insert(NewStringAffineInterval("8", ""), 123)
if !ivt.Intersects(NewStringAffinePoint("9")) {
t.Errorf("missing 9")
@ -53,15 +288,16 @@ func TestIntervalTreeStringAffine(t *testing.T) {
}
func TestIntervalTreeStab(t *testing.T) {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
ivt.Insert(NewStringInterval("0", "1"), 123)
ivt.Insert(NewStringInterval("0", "2"), 456)
ivt.Insert(NewStringInterval("5", "6"), 789)
ivt.Insert(NewStringInterval("6", "8"), 999)
ivt.Insert(NewStringInterval("0", "3"), 0)
if ivt.root.max.Compare(StringComparable("8")) != 0 {
t.Fatalf("wrong root max got %v, expected 8", ivt.root.max)
tr := ivt.(*intervalTree)
if tr.root.max.Compare(StringComparable("8")) != 0 {
t.Fatalf("wrong root max got %v, expected 8", tr.root.max)
}
if x := len(ivt.Stab(NewStringPoint("0"))); x != 3 {
t.Errorf("got %d, expected 3", x)
@ -94,7 +330,7 @@ type xy struct {
func TestIntervalTreeRandom(t *testing.T) {
// generate unique intervals
ivs := make(map[xy]struct{})
ivt := &IntervalTree{}
ivt := NewIntervalTree()
maxv := 128
rand.Seed(time.Now().UnixNano())
@ -168,7 +404,7 @@ func TestIntervalTreeSortedVisit(t *testing.T) {
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
for _, ivl := range tt.ivls {
ivt.Insert(ivl, struct{}{})
}
@ -217,7 +453,7 @@ func TestIntervalTreeVisitExit(t *testing.T) {
}
for i, tt := range tests {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
for _, ivl := range ivls {
ivt.Insert(ivl, struct{}{})
}
@ -284,7 +520,7 @@ func TestIntervalTreeContains(t *testing.T) {
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
ivt := NewIntervalTree()
for _, ivl := range tt.ivls {
ivt.Insert(ivl, struct{}{})
}