mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
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>
This commit is contained in:
parent
0b37ae05b1
commit
266214d19e
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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{}{})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user