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() {
|
func Example() {
|
||||||
ivt := &adt.IntervalTree{}
|
ivt := adt.NewIntervalTree()
|
||||||
|
|
||||||
ivt.Insert(adt.NewInt64Interval(1, 3), 123)
|
ivt.Insert(adt.NewInt64Interval(1, 3), 123)
|
||||||
ivt.Insert(adt.NewInt64Interval(9, 13), 456)
|
ivt.Insert(adt.NewInt64Interval(9, 13), 456)
|
||||||
ivt.Insert(adt.NewInt64Interval(7, 20), 789)
|
ivt.Insert(adt.NewInt64Interval(7, 20), 789)
|
||||||
|
@ -16,7 +16,9 @@ package adt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Comparable is an interface for trichotomic comparisons.
|
// Comparable is an interface for trichotomic comparisons.
|
||||||
@ -35,6 +37,17 @@ const (
|
|||||||
red
|
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)
|
// Interval implements a Comparable interval [begin, end)
|
||||||
// TODO: support different sorts of intervals: (a,b), [a,b], (a, b]
|
// TODO: support different sorts of intervals: (a,b), [a,b], (a, b]
|
||||||
type Interval struct {
|
type Interval struct {
|
||||||
@ -160,22 +173,55 @@ func (x *intervalNode) visit(iv *Interval, nv nodeVisitor) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntervalValue represents a range tree node that contains a range and a value.
|
||||||
type IntervalValue struct {
|
type IntervalValue struct {
|
||||||
Ivl Interval
|
Ivl Interval
|
||||||
Val interface{}
|
Val interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntervalTree represents a (mostly) textbook implementation of the
|
// 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".
|
// 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
|
root *intervalNode
|
||||||
count int
|
count int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes the node with the given interval from the tree, returning
|
// Delete removes the node with the given interval from the tree, returning
|
||||||
// true if a node is in fact removed.
|
// 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)
|
z := ivt.find(ivl)
|
||||||
if z == nil {
|
if z == nil {
|
||||||
return false
|
return false
|
||||||
@ -217,7 +263,7 @@ func (ivt *IntervalTree) Delete(ivl Interval) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ivt *IntervalTree) deleteFixup(x *intervalNode) {
|
func (ivt *intervalTree) deleteFixup(x *intervalNode) {
|
||||||
for x != ivt.root && x.color() == black && x.parent != nil {
|
for x != ivt.root && x.color() == black && x.parent != nil {
|
||||||
if x == x.parent.left {
|
if x == x.parent.left {
|
||||||
w := x.parent.right
|
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.
|
// 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
|
var y *intervalNode
|
||||||
z := &intervalNode{iv: IntervalValue{ivl, val}, max: ivl.End, c: red}
|
z := &intervalNode{iv: IntervalValue{ivl, val}, max: ivl.End, c: red}
|
||||||
x := ivt.root
|
x := ivt.root
|
||||||
@ -311,7 +357,7 @@ func (ivt *IntervalTree) Insert(ivl Interval, val interface{}) {
|
|||||||
ivt.count++
|
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 {
|
for z.parent != nil && z.parent.parent != nil && z.parent.color() == red {
|
||||||
if z.parent == z.parent.parent.left {
|
if z.parent == z.parent.parent.left {
|
||||||
y := z.parent.parent.right
|
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
|
// 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
|
y := x.right
|
||||||
x.right = y.left
|
x.right = y.left
|
||||||
if y.left != nil {
|
if y.left != nil {
|
||||||
@ -364,8 +410,8 @@ func (ivt *IntervalTree) rotateLeft(x *intervalNode) {
|
|||||||
y.updateMax()
|
y.updateMax()
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotateLeft moves x so it is right of its left child
|
// rotateRight moves x so it is right of its left child
|
||||||
func (ivt *IntervalTree) rotateRight(x *intervalNode) {
|
func (ivt *intervalTree) rotateRight(x *intervalNode) {
|
||||||
if x == nil {
|
if x == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -381,7 +427,7 @@ func (ivt *IntervalTree) rotateRight(x *intervalNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// replaceParent replaces x's parent with y
|
// 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
|
y.parent = x.parent
|
||||||
if x.parent == nil {
|
if x.parent == nil {
|
||||||
ivt.root = y
|
ivt.root = y
|
||||||
@ -397,13 +443,13 @@ func (ivt *IntervalTree) replaceParent(x *intervalNode, y *intervalNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Len gives the number of elements in the tree
|
// 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.
|
// 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
|
// 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)
|
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.
|
// 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.
|
// 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) })
|
ivt.root.visit(&ivl, func(n *intervalNode) bool { return ivv(&n.iv) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the exact node for a given interval
|
// 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 {
|
f := func(n *intervalNode) bool {
|
||||||
if n.iv.Ivl != ivl {
|
if n.iv.Ivl != ivl {
|
||||||
return true
|
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
|
// 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)
|
n := ivt.find(ivl)
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return 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.
|
// 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
|
x := ivt.root
|
||||||
for x != nil && iv.Compare(&x.iv.Ivl) != 0 {
|
for x != nil && iv.Compare(&x.iv.Ivl) != 0 {
|
||||||
if x.left != nil && x.left.max.Compare(iv.Begin) > 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.
|
// 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
|
var maxEnd, minBegin Comparable
|
||||||
|
|
||||||
isContiguous := true
|
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.
|
// 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 {
|
if ivt.count == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -486,7 +532,7 @@ func (ivt *IntervalTree) Stab(iv Interval) (ivs []*IntervalValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Union merges a given interval tree into the receiver.
|
// 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 {
|
f := func(n *IntervalValue) bool {
|
||||||
ivt.Insert(n.Ivl, n.Val)
|
ivt.Insert(n.Ivl, n.Val)
|
||||||
return true
|
return true
|
||||||
@ -494,6 +540,64 @@ func (ivt *IntervalTree) Union(inIvt IntervalTree, ivl Interval) {
|
|||||||
inIvt.Visit(ivl, f)
|
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
|
type StringComparable string
|
||||||
|
|
||||||
func (s StringComparable) Compare(c Comparable) int {
|
func (s StringComparable) Compare(c Comparable) int {
|
||||||
@ -543,6 +647,7 @@ func (s StringAffineComparable) Compare(c Comparable) int {
|
|||||||
func NewStringAffineInterval(begin, end string) Interval {
|
func NewStringAffineInterval(begin, end string) Interval {
|
||||||
return Interval{StringAffineComparable(begin), StringAffineComparable(end)}
|
return Interval{StringAffineComparable(begin), StringAffineComparable(end)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStringAffinePoint(s string) Interval {
|
func NewStringAffinePoint(s string) Interval {
|
||||||
return NewStringAffineInterval(s, s+"\x00")
|
return NewStringAffineInterval(s, s+"\x00")
|
||||||
}
|
}
|
||||||
@ -551,6 +656,10 @@ func NewInt64Interval(a int64, b int64) Interval {
|
|||||||
return Interval{Int64Comparable(a), Int64Comparable(b)}
|
return Interval{Int64Comparable(a), Int64Comparable(b)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newInt64EmptyInterval() Interval {
|
||||||
|
return Interval{Begin: nil, End: nil}
|
||||||
|
}
|
||||||
|
|
||||||
func NewInt64Point(a int64) Interval {
|
func NewInt64Point(a int64) Interval {
|
||||||
return Interval{Int64Comparable(a), Int64Comparable(a + 1)}
|
return Interval{Int64Comparable(a), Int64Comparable(a + 1)}
|
||||||
}
|
}
|
||||||
@ -591,6 +700,7 @@ func (b BytesAffineComparable) Compare(c Comparable) int {
|
|||||||
func NewBytesAffineInterval(begin, end []byte) Interval {
|
func NewBytesAffineInterval(begin, end []byte) Interval {
|
||||||
return Interval{BytesAffineComparable(begin), BytesAffineComparable(end)}
|
return Interval{BytesAffineComparable(begin), BytesAffineComparable(end)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBytesAffinePoint(b []byte) Interval {
|
func NewBytesAffinePoint(b []byte) Interval {
|
||||||
be := make([]byte, len(b)+1)
|
be := make([]byte, len(b)+1)
|
||||||
copy(be, b)
|
copy(be, b)
|
||||||
|
@ -16,12 +16,247 @@ package adt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func TestIntervalTreeIntersects(t *testing.T) {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
ivt.Insert(NewStringInterval("1", "3"), 123)
|
ivt.Insert(NewStringInterval("1", "3"), 123)
|
||||||
|
|
||||||
if ivt.Intersects(NewStringPoint("0")) {
|
if ivt.Intersects(NewStringPoint("0")) {
|
||||||
@ -42,7 +277,7 @@ func TestIntervalTreeIntersects(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntervalTreeStringAffine(t *testing.T) {
|
func TestIntervalTreeStringAffine(t *testing.T) {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
ivt.Insert(NewStringAffineInterval("8", ""), 123)
|
ivt.Insert(NewStringAffineInterval("8", ""), 123)
|
||||||
if !ivt.Intersects(NewStringAffinePoint("9")) {
|
if !ivt.Intersects(NewStringAffinePoint("9")) {
|
||||||
t.Errorf("missing 9")
|
t.Errorf("missing 9")
|
||||||
@ -53,15 +288,16 @@ func TestIntervalTreeStringAffine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntervalTreeStab(t *testing.T) {
|
func TestIntervalTreeStab(t *testing.T) {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
ivt.Insert(NewStringInterval("0", "1"), 123)
|
ivt.Insert(NewStringInterval("0", "1"), 123)
|
||||||
ivt.Insert(NewStringInterval("0", "2"), 456)
|
ivt.Insert(NewStringInterval("0", "2"), 456)
|
||||||
ivt.Insert(NewStringInterval("5", "6"), 789)
|
ivt.Insert(NewStringInterval("5", "6"), 789)
|
||||||
ivt.Insert(NewStringInterval("6", "8"), 999)
|
ivt.Insert(NewStringInterval("6", "8"), 999)
|
||||||
ivt.Insert(NewStringInterval("0", "3"), 0)
|
ivt.Insert(NewStringInterval("0", "3"), 0)
|
||||||
|
|
||||||
if ivt.root.max.Compare(StringComparable("8")) != 0 {
|
tr := ivt.(*intervalTree)
|
||||||
t.Fatalf("wrong root max got %v, expected 8", ivt.root.max)
|
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 {
|
if x := len(ivt.Stab(NewStringPoint("0"))); x != 3 {
|
||||||
t.Errorf("got %d, expected 3", x)
|
t.Errorf("got %d, expected 3", x)
|
||||||
@ -94,7 +330,7 @@ type xy struct {
|
|||||||
func TestIntervalTreeRandom(t *testing.T) {
|
func TestIntervalTreeRandom(t *testing.T) {
|
||||||
// generate unique intervals
|
// generate unique intervals
|
||||||
ivs := make(map[xy]struct{})
|
ivs := make(map[xy]struct{})
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
maxv := 128
|
maxv := 128
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
@ -168,7 +404,7 @@ func TestIntervalTreeSortedVisit(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
for _, ivl := range tt.ivls {
|
for _, ivl := range tt.ivls {
|
||||||
ivt.Insert(ivl, struct{}{})
|
ivt.Insert(ivl, struct{}{})
|
||||||
}
|
}
|
||||||
@ -217,7 +453,7 @@ func TestIntervalTreeVisitExit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
for _, ivl := range ivls {
|
for _, ivl := range ivls {
|
||||||
ivt.Insert(ivl, struct{}{})
|
ivt.Insert(ivl, struct{}{})
|
||||||
}
|
}
|
||||||
@ -284,7 +520,7 @@ func TestIntervalTreeContains(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ivt := &IntervalTree{}
|
ivt := NewIntervalTree()
|
||||||
for _, ivl := range tt.ivls {
|
for _, ivl := range tt.ivls {
|
||||||
ivt.Insert(ivl, struct{}{})
|
ivt.Insert(ivl, struct{}{})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user