diff --git a/README.md b/README.md index 496c859..71cdc2d 100644 --- a/README.md +++ b/README.md @@ -59,24 +59,27 @@ Especially useful is the paper "[Fast Set Operations Using Treaps]" by Guy E. Bl [some links about treaps]: http://faculty.washington.edu/aragon/treaps.html [Fast Set Operations Using Treaps]: https://www.cs.cmu.edu/~scandal/papers/treaps-spaa98.pdf -## Interface +## Compare function -To apply this library to types of one-dimensional intervals, they must just implement the following small interface: +To apply this library to types of one-dimensional intervals, you must provide a compare funtion: ```go -type Interface[T any] interface { - // Compare the left (l) and right (r) points of two intervals and returns four integers with values (-1, 0, +1). - Compare(T) (ll, rr, lr, rl int) -} + // cmp must return four int values: + // + // ll: left point interval a compared with left point interval b (-1, 0, +1) + // rr: right point interval a compared with right point interval b (-1, 0, +1) + // lr: left point interval a compared with right point interval b (-1, 0, +1) + // rl: right point interval a compared with left point interval b (-1, 0, +1) + // + func[T any] cmp(a, b T) (ll, rr, lr, rl int) ``` ## API ```go import "github.com/gaissmai/interval" - type Tree[T Interface[T]] struct{ ... } - - func NewTree[T Interface[T]](items ...T) Tree[T] + type Tree[T any] struct{ ... } + func NewTree[T any](cmp func(a, b T) (ll, rr, lr, rl int)) Tree[T] func (t Tree[T]) Insert(items ...T) Tree[T] func (t Tree[T]) Delete(item T) (Tree[T], bool) diff --git a/bench_test.go b/bench_test.go index f94d30b..1d33263 100644 --- a/bench_test.go +++ b/bench_test.go @@ -17,9 +17,12 @@ var intMap = map[int]string{ 1_000_000: "1_000_000", } +var tree = interval.NewTree(compareIval) + func BenchmarkInsert(b *testing.B) { for n := 1; n <= 1_000_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) + probe := generateIvals(1)[0] name := "Into" + intMap[n] @@ -34,7 +37,7 @@ func BenchmarkInsert(b *testing.B) { func BenchmarkInsertMutable(b *testing.B) { for n := 1; n <= 1_000_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "Into" + intMap[n] @@ -52,7 +55,7 @@ func BenchmarkDelete(b *testing.B) { ivals := generateIvals(n) probe := ivals[rand.Intn(len(ivals))] - tree := interval.NewTree(ivals...) + tree := tree.Insert(ivals...) name := "DeleteFrom" + intMap[n] b.Run(name, func(b *testing.B) { @@ -69,7 +72,7 @@ func BenchmarkDeleteMutable(b *testing.B) { ivals := generateIvals(n) probe := ivals[rand.Intn(len(ivals))] - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) name := "DeleteFrom" + intMap[n] b.Run(name, func(b *testing.B) { @@ -81,10 +84,24 @@ func BenchmarkDeleteMutable(b *testing.B) { } } +func BenchmarkClone(b *testing.B) { + for n := 10; n <= 10_000; n *= 10 { + tree := tree.Insert(generateIvals(n)...) + name := intMap[n] + + b.Run(name, func(b *testing.B) { + b.ResetTimer() + for n := 0; n < b.N; n++ { + _ = tree.Clone() + } + }) + } +} + func BenchmarkUnionImmutable(b *testing.B) { - this100_000 := interval.NewTree(generateIvals(100_000)...) + this100_000 := tree.Insert(generateIvals(100_000)...) for n := 10; n <= 100_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) name := "size100_000with" + intMap[n] b.Run(name, func(b *testing.B) { @@ -98,8 +115,8 @@ func BenchmarkUnionImmutable(b *testing.B) { func BenchmarkUnionMutable(b *testing.B) { for n := 10; n <= 100_000; n *= 10 { - this100_000 := interval.NewTree(generateIvals(100_000)...) - tree := interval.NewTree(generateIvals(n)...) + this100_000 := tree.Insert(generateIvals(100_000)...) + tree := tree.Insert(generateIvals(n)...) name := "size100_000with" + intMap[n] b.Run(name, func(b *testing.B) { @@ -114,7 +131,7 @@ func BenchmarkUnionMutable(b *testing.B) { func BenchmarkIntersects(b *testing.B) { for n := 1; n <= 1_000_000; n *= 10 { ivals := generateIvals(n) - tree := interval.NewTree(ivals...) + tree := tree.Insert(ivals...) probe := ivals[rand.Intn(len(ivals))] name := "In" + intMap[n] @@ -130,7 +147,7 @@ func BenchmarkIntersects(b *testing.B) { func BenchmarkFind(b *testing.B) { for n := 1; n <= 1_000_000; n *= 10 { ivals := generateIvals(n) - tree := interval.NewTree(ivals...) + tree := tree.Insert(ivals...) probe := ivals[rand.Intn(len(ivals))] name := "In" + intMap[n] @@ -145,7 +162,7 @@ func BenchmarkFind(b *testing.B) { func BenchmarkCoverLCP(b *testing.B) { for n := 100; n <= 1_000_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "In" + intMap[n] @@ -160,7 +177,7 @@ func BenchmarkCoverLCP(b *testing.B) { func BenchmarkCoverSCP(b *testing.B) { for n := 100; n <= 1_000_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "In" + intMap[n] @@ -175,7 +192,7 @@ func BenchmarkCoverSCP(b *testing.B) { func BenchmarkCoveredBy(b *testing.B) { for n := 100; n <= 100_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "In" + intMap[n] @@ -190,7 +207,7 @@ func BenchmarkCoveredBy(b *testing.B) { func BenchmarkCovers(b *testing.B) { for n := 100; n <= 100_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "In" + intMap[n] @@ -205,7 +222,7 @@ func BenchmarkCovers(b *testing.B) { func BenchmarkPrecededBy(b *testing.B) { for m := 100; m <= 10_000; m *= 10 { - tree := interval.NewTree(generateIvals(m)...) + tree := tree.Insert(generateIvals(m)...) probe := generateIvals(1)[0] name := "In" + intMap[m] @@ -220,7 +237,7 @@ func BenchmarkPrecededBy(b *testing.B) { func BenchmarkPrecedes(b *testing.B) { for m := 100; m <= 10_000; m *= 10 { - tree := interval.NewTree(generateIvals(m)...) + tree := tree.Insert(generateIvals(m)...) probe := generateIvals(1)[0] name := "In" + intMap[m] @@ -235,7 +252,7 @@ func BenchmarkPrecedes(b *testing.B) { func BenchmarkIntersections(b *testing.B) { for n := 100; n <= 10_000; n *= 10 { - tree := interval.NewTree(generateIvals(n)...) + tree := tree.Insert(generateIvals(n)...) probe := generateIvals(1)[0] name := "In" + intMap[n] @@ -251,7 +268,7 @@ func BenchmarkIntersections(b *testing.B) { func BenchmarkMin(b *testing.B) { for n := 100; n <= 1_000_000; n *= 10 { ivals := generateIvals(n) - tree := interval.NewTree(ivals...) + tree := tree.Insert(ivals...) name := "In" + intMap[n] b.Run(name, func(b *testing.B) { @@ -266,7 +283,7 @@ func BenchmarkMin(b *testing.B) { func BenchmarkMax(b *testing.B) { for n := 100; n <= 1_000_000; n *= 10 { ivals := generateIvals(n) - tree := interval.NewTree(ivals...) + tree := tree.Insert(ivals...) name := "In" + intMap[n] b.Run(name, func(b *testing.B) { diff --git a/interface.go b/comparer.go similarity index 87% rename from interface.go rename to comparer.go index bab0dd6..e65736e 100644 --- a/interface.go +++ b/comparer.go @@ -1,7 +1,5 @@ package interval -// Interface is the type constraint for generic interval items. -// // There exists thirteen basic relations between any two intervals in one dimension, // see [Allen's Interval Algebra]. // @@ -49,19 +47,6 @@ package interval // -------------------------|---------------------------------------- // // [Allen's Interval Algebra]: https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html -// -type Interface[T any] interface { - // Compare the left (l) and right (r) points of two intervals and returns four integers with values (-1, 0, +1). - // - // The result will be one of the thirteen possibilities in the interval relation table. - // - // ll: left point interval A compared with left point interval B (-1, 0, +1) - // rr: right point interval A compared with right point interval B (-1, 0, +1) - // lr: left point interval A compared with right point interval B (-1, 0, +1) - // rl: right point interval A compared with left point interval B (-1, 0, +1) - // - Compare(T) (ll, rr, lr, rl int) -} // compare is for sorting keys into the BST, the sort key is the left point of the intervals. // If the left point is equal, sort the supersets to the left (definite order). @@ -82,8 +67,8 @@ type Interface[T any] interface { // | B1----B2 | | | | | | // -------------------------|---------------------------------------- // -func compare[T Interface[T]](a, b T) int { - ll, rr, _, _ := a.Compare(b) +func (t Tree[T]) compare(a, b T) int { + ll, rr, _, _ := t.cmp(a, b) switch { case ll == 0: // identical left point, sort supersets to the left, make a definite order @@ -115,8 +100,8 @@ func compare[T Interface[T]](a, b T) int { // | B1----B2 | | | | | | // -------------------------|---------------------------------------- // -func covers[T Interface[T]](a, b T) bool { - ll, rr, _, _ := a.Compare(b) +func (t Tree[T]) covers(a, b T) bool { + ll, rr, _, _ := t.cmp(a, b) return ll <= 0 && rr >= 0 } @@ -134,19 +119,19 @@ func covers[T Interface[T]](a, b T) bool { // | B1---B2 | | | | | | // -------------------------|---------------------------------------| // -func intersects[T Interface[T]](a, b T) bool { - ll, rr, lr, rl := a.Compare(b) +func (t Tree[T]) intersects(a, b T) bool { + ll, rr, lr, rl := t.cmp(a, b) return !((ll == -1 && rr == -1 && lr == -1 && rl == -1) || (ll == 1 && rr == 1 && lr == 1 && rl == 1)) } // cmpRR, compares just the right point of the intervals. -func cmpRR[T Interface[T]](a, b T) int { - _, rr, _, _ := a.Compare(b) +func (t Tree[T]) cmpRR(a, b T) int { + _, rr, _, _ := t.cmp(a, b) return rr } // cmpLR, compares just the left point from a with right point from b. -func cmpLR[T Interface[T]](a, b T) int { - _, _, lr, _ := a.Compare(b) +func (t Tree[T]) cmpLR(a, b T) int { + _, _, lr, _ := t.cmp(a, b) return lr } diff --git a/example_period_test.go b/example_period_test.go index 502ee8b..85b8a29 100644 --- a/example_period_test.go +++ b/example_period_test.go @@ -7,7 +7,18 @@ import ( "github.com/gaissmai/interval" ) -// fmt.Stringer for formattting, not required for interval.Interface +// example interval +type Ival [2]uint + +// example data +var periods = []Ival{ + {3, 4}, + {2, 9}, + {7, 9}, + {3, 5}, +} + +// fmt.Stringer for formattting, not required func (p Ival) String() string { return fmt.Sprintf("%d...%d", p[0], p[1]) } @@ -23,26 +34,16 @@ func cmp(a, b uint) int { return 1 } -// implement interval.Interface -func (p Ival) Compare(q Ival) (ll, rr, lr, rl int) { +// cmp function for type Ival +func compareIval(p, q Ival) (ll, rr, lr, rl int) { return cmp(p[0], q[0]), cmp(p[1], q[1]), cmp(p[0], q[1]), cmp(p[1], q[0]) } -// example interval -type Ival [2]uint - -// example data -var periods = []Ival{ - {3, 4}, - {2, 9}, - {7, 9}, - {3, 5}, -} - -func ExampleInterface_period() { - tree := interval.NewTree(periods...) - tree.Fprint(os.Stdout) +func ExampleNewTree() { + tree1 := interval.NewTree(compareIval) + tree1.InsertMutable(periods...) + tree1.Fprint(os.Stdout) // Output: // ▼ // └─ 2...9 @@ -52,11 +53,11 @@ func ExampleInterface_period() { } func ExampleTree_Max() { - tree := interval.NewTree(periods...) - tree.Fprint(os.Stdout) + tree1 := tree.Insert(periods...) + tree1.Fprint(os.Stdout) fmt.Println("\nInterval with max value in tree:") - fmt.Println(tree.Max()) + fmt.Println(tree1.Max()) // Output: // ▼ @@ -70,12 +71,12 @@ func ExampleTree_Max() { } func ExampleTree_Covers() { - tree := interval.NewTree(periods...) - tree.Fprint(os.Stdout) + tree1 := tree.Insert(periods...) + tree1.Fprint(os.Stdout) item := Ival{3, 4} fmt.Printf("\nCovers for item: %v\n", item) - for _, p := range tree.Covers(item) { + for _, p := range tree1.Covers(item) { fmt.Println(p) } @@ -93,12 +94,12 @@ func ExampleTree_Covers() { } func ExampleTree_CoveredBy() { - tree := interval.NewTree(periods...) - tree.Fprint(os.Stdout) + tree1 := tree.Insert(periods...) + tree1.Fprint(os.Stdout) item := Ival{3, 10} fmt.Printf("\nCoveredBy item: %v\n", item) - for _, p := range tree.CoveredBy(item) { + for _, p := range tree1.CoveredBy(item) { fmt.Println(p) } @@ -116,12 +117,12 @@ func ExampleTree_CoveredBy() { } func ExampleTree_Precedes_period() { - tree := interval.NewTree(periods...) - tree.Fprint(os.Stdout) + tree1 := tree.Insert(periods...) + tree1.Fprint(os.Stdout) item := Ival{6, 6} fmt.Printf("\nPrecedes item: %v\n", item) - for _, p := range tree.Precedes(item) { + for _, p := range tree1.Precedes(item) { fmt.Println(p) } @@ -138,9 +139,9 @@ func ExampleTree_Precedes_period() { } func ExampleTree_Visit() { - tree := interval.NewTree(periods...) + tree1 := tree.Insert(periods...) fmt.Println("parent/child printing") - tree.Fprint(os.Stdout) + tree1.Fprint(os.Stdout) start := Ival{3, 5} stop := Ival{7, 9} @@ -150,10 +151,10 @@ func ExampleTree_Visit() { } fmt.Println("visit ascending") - tree.Visit(start, stop, visitFn) + tree1.Visit(start, stop, visitFn) fmt.Println("visit descending") - tree.Visit(stop, start, visitFn) + tree1.Visit(stop, start, visitFn) // Output: // parent/child printing diff --git a/example_time_test.go b/example_time_test.go index 1898084..02e2d91 100644 --- a/example_time_test.go +++ b/example_time_test.go @@ -34,19 +34,21 @@ type Tval struct { name string } -// String, implements fmt.Stringer for nice formattting, not required for interval.Interface +// String, implements fmt.Stringer for nice formattting func (p Tval) String() string { return fmt.Sprintf("%s...%s (%s)", p.birth.Format("2006"), p.death.Format("2006"), p.name) } -// Compare implements the interval.Interface -func (p Tval) Compare(q Tval) (ll, rr, lr, rl int) { +// cmp func for type Tval +func compareTval(p, q Tval) (ll, rr, lr, rl int) { return cmpTime(p.birth, q.birth), cmpTime(p.death, q.death), cmpTime(p.birth, q.death), cmpTime(p.death, q.birth) } +var timeTree = interval.NewTree(compareTval) + // example data var physicists = []Tval{ mkTval(1473, 1543, "Kopernikus"), @@ -80,11 +82,11 @@ var physicists = []Tval{ } func ExampleTree_Precedes_time() { - tree := interval.NewTree(physicists...) + tree := timeTree.Insert(physicists...) tree.Fprint(os.Stdout) precedes := tree.Precedes(mkTval(1643, 1727, "Newton")) - tree = interval.NewTree(precedes...) + tree = timeTree.Insert(precedes...) fmt.Println("\nPrecedes Newton:") tree.Fprint(os.Stdout) @@ -129,11 +131,11 @@ func ExampleTree_Precedes_time() { } func ExampleTree_PrecededBy_time() { - tree := interval.NewTree(physicists...) + tree := timeTree.Insert(physicists...) tree.Fprint(os.Stdout) precededBy := tree.PrecededBy(mkTval(1643, 1727, "Newton")) - tree = interval.NewTree(precededBy...) + tree = timeTree.Insert(precededBy...) fmt.Println("\nPrecededBy Newton:") tree.Fprint(os.Stdout) diff --git a/go.sum b/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/helpers.go b/helpers.go index 9c9d585..ce9fba6 100644 --- a/helpers.go +++ b/helpers.go @@ -100,7 +100,7 @@ func (t Tree[T]) Fprint(w io.Writer) error { // init map pcm.pcMap = make(map[*node[T]][]*node[T]) - pcm = t.root.buildParentChildsMap(pcm) + pcm = t.root.buildParentChildsMap(pcm, &t) if len(pcm.pcMap) == 0 { return nil @@ -115,7 +115,7 @@ func (t Tree[T]) Fprint(w io.Writer) error { return walkAndStringify(w, pcm, nil, "") } -func walkAndStringify[T Interface[T]](w io.Writer, pcm parentChildsMap[T], parent *node[T], pad string) error { +func walkAndStringify[T any](w io.Writer, pcm parentChildsMap[T], parent *node[T], pad string) error { // the prefix (pad + glyphe) is already printed on the line on upper level if parent != nil { if _, err := fmt.Fprintf(w, "%v\n", parent.item); err != nil { @@ -242,34 +242,38 @@ func (n *node[T]) preorderStringify(w io.Writer, pad string) error { // │ └─ 6...7 // └─ 7...9 // -type parentChildsMap[T Interface[T]] struct { +type parentChildsMap[T any] struct { pcMap map[*node[T]][]*node[T] // parent -> []child map stack []*node[T] // just needed for the algo } // buildParentChildsMap, in-order traversal -func (n *node[T]) buildParentChildsMap(pcm parentChildsMap[T]) parentChildsMap[T] { +// +// The parameter t is needed to access the compare function. +func (n *node[T]) buildParentChildsMap(pcm parentChildsMap[T], t *Tree[T]) parentChildsMap[T] { if n == nil { return pcm } // in-order traversal, left tree - pcm = n.left.buildParentChildsMap(pcm) + pcm = n.left.buildParentChildsMap(pcm, t) // detect parent-child-mapping for this node - pcm = n.pcmForNode(pcm) + pcm = n.pcmForNode(pcm, t) // in-order traversal, right tree - return n.right.buildParentChildsMap(pcm) + return n.right.buildParentChildsMap(pcm, t) } // pcmForNode, find parent in stack, remove items from stack, put this item on stack. -func (n *node[T]) pcmForNode(pcm parentChildsMap[T]) parentChildsMap[T] { +// +// The parameter t is needed to access the compare function. +func (n *node[T]) pcmForNode(pcm parentChildsMap[T], t *Tree[T]) parentChildsMap[T] { // if this item is covered by a prev item on stack for j := len(pcm.stack) - 1; j >= 0; j-- { that := pcm.stack[j] - if covers(that.item, n.item) { + if t.covers(that.item, n.item) { // item in node j is parent to item pcm.pcMap[that] = append(pcm.pcMap[that], n) break @@ -373,36 +377,44 @@ func (t Tree[T]) Visit(start, stop T, visitFn func(item T) bool) { } order := inorder - if compare(start, stop) > 0 { + if t.compare(start, stop) > 0 { start, stop = stop, start order = reverse } // treaps are really cool datastructures!!! - _, mid1, r := t.root.split(start, true) - l, mid2, _ := r.split(stop, true) + _, mid1, r := t.root.split(start, true, &t) + l, mid2, _ := r.split(stop, true, &t) - span := join(mid1, join(l, mid2, true), true) + span := join(mid1, join(l, mid2, true, &t), true, &t) span.traverse(order, 0, func(n *node[T], _ int) bool { return visitFn(n.item) }) } -// Clone, deep cloning of the tree structure, the items are copied. +// Clone, deep cloning of the tree structure. func (t Tree[T]) Clone() Tree[T] { - t.root = t.root.clone() + if t.root != nil { + t.root = t.root.clone(t) + } return t } -func (n *node[T]) clone() *node[T] { - if n == nil { - return n - } +// clone rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) clone(t Tree[T]) *node[T] { n = n.copyNode() - n.left = n.left.clone() - n.right = n.right.clone() + if n.left != nil { + n.left = n.left.clone(t) + } + + if n.right != nil { + n.right = n.right.clone(t) + } + n.recalc(&t) return n } diff --git a/treap.go b/treap.go index 84ae17b..adfe498 100644 --- a/treap.go +++ b/treap.go @@ -14,7 +14,7 @@ import ( ) // node is the basic recursive data structure. -type node[T Interface[T]] struct { +type node[T any] struct { // augment the treap for interval lookups minUpper *node[T] // pointer to node in subtree with min upper value maxUpper *node[T] // pointer to node in subtree with max upper value @@ -26,28 +26,36 @@ type node[T Interface[T]] struct { item T // generic key/value } -// Tree is the public handle to the hidden implementation. -// -// The zero value is useful without initialization, but it may be clearer to use [NewTree] -// because of the possibility of type inference. -type Tree[T Interface[T]] struct { +// Tree must be initialized by [NewTree]. +type Tree[T any] struct { root *node[T] + cmp func(T, T) (ll, rr, lr, rl int) } -// NewTree initializes the interval tree with zero or more items of generic type T. -// The type constraint is defined by the [interval.Interface]. -func NewTree[T Interface[T]](items ...T) Tree[T] { +// NewTree initializes the interval tree with the interval comparison function for type T. +// +// cmp(a, b T) (ll, rr, lr, rl int) +// +// The result of cmp() must be four int values: +// +// ll: left point interval a compared with left point interval b (-1, 0, +1) +// rr: right point interval a compared with right point interval b (-1, 0, +1) +// lr: left point interval a compared with right point interval b (-1, 0, +1) +// rl: right point interval a compared with left point interval b (-1, 0, +1) +// +func NewTree[T any](cmp func(a, b T) (ll, rr, lr, rl int)) Tree[T] { var t Tree[T] - t.InsertMutable(items...) + t.cmp = cmp return t } // makeNode, create new node with item and random priority. -func makeNode[T Interface[T]](item T) *node[T] { +// The parameter t is needed to access the compare function. +func (t Tree[T]) makeNode(item T) *node[T] { n := new(node[T]) n.item = item n.prio = rand.Uint32() - n.recalc() // initial calculation of finger pointers... + n.recalc(&t) // initial calculation of finger pointers... return n } @@ -61,14 +69,8 @@ func (n *node[T]) copyNode() *node[T] { // Insert elements into the tree, returns the new Tree. // If an element is a duplicate, it replaces the previous element. func (t Tree[T]) Insert(items ...T) Tree[T] { - // something to preserve? - immutable := true - if t.root == nil { - immutable = false - } - for i := range items { - t.root = t.root.insert(makeNode(items[i]), immutable) + t.root = t.root.insert(t.makeNode(items[i]), true, &t) } return t @@ -78,12 +80,14 @@ func (t Tree[T]) Insert(items ...T) Tree[T] { // If the original tree does not need to be preserved then this is much faster than the immutable insert. func (t *Tree[T]) InsertMutable(items ...T) { for i := range items { - t.root = t.root.insert(makeNode(items[i]), false) + t.root = t.root.insert(t.makeNode(items[i]), false, t) } } // insert into tree, changing nodes are copied, new treap is returned, old treap is modified if immutable is false. -func (n *node[T]) insert(m *node[T], immutable bool) *node[T] { +// +// The parameter t is needed to access the compare function. +func (n *node[T]) insert(m *node[T], immutable bool, t *Tree[T]) *node[T] { if n == nil { return m } @@ -102,11 +106,11 @@ func (n *node[T]) insert(m *node[T], immutable bool) *node[T] { // / // l // - l, dupe, r := n.split(m.item, immutable) + l, dupe, r := n.split(m.item, immutable, t) // replace dupe with m. m has same key but different prio than dupe, a join() is required if dupe != nil { - return join(l, join(m, r, immutable), immutable) + return join(l, join(m, r, immutable, t), immutable, t) } // no duplicate, take m as new root @@ -116,14 +120,14 @@ func (n *node[T]) insert(m *node[T], immutable bool) *node[T] { // m // m.left, m.right = l, r - m.recalc() + m.recalc(t) return m } - cmp := compare(m.item, n.item) + cmp := t.compare(m.item, n.item) if cmp == 0 { // replace duplicate item with m, but m has different prio, a join() is required - return join(n.left, join(m, n.right, immutable), immutable) + return join(n.left, join(m, n.right, immutable, t), immutable, t) } if immutable { @@ -132,14 +136,14 @@ func (n *node[T]) insert(m *node[T], immutable bool) *node[T] { switch { case cmp < 0: // rec-descent - n.left = n.left.insert(m, immutable) + n.left = n.left.insert(m, immutable, t) // // R // m l r // l r // case cmp > 0: // rec-descent - n.right = n.right.insert(m, immutable) + n.right = n.right.insert(m, immutable, t) // // R // l r m @@ -147,15 +151,15 @@ func (n *node[T]) insert(m *node[T], immutable bool) *node[T] { // } - n.recalc() // node has changed, recalc + n.recalc(t) // node has changed, recalc return n } // Delete removes an item if it exists, returns the new tree and true, false if not found. func (t Tree[T]) Delete(item T) (Tree[T], bool) { // split/join must be immutable - l, m, r := t.root.split(item, true) - t.root = join(l, r, true) + l, m, r := t.root.split(item, true, &t) + t.root = join(l, r, true, &t) ok := m != nil return t, ok @@ -164,8 +168,8 @@ func (t Tree[T]) Delete(item T) (Tree[T], bool) { // DeleteMutable removes an item from tree, returns true if it exists, false otherwise. // If the original tree does not need to be preserved then this is much faster than the immutable delete. func (t *Tree[T]) DeleteMutable(item T) bool { - l, m, r := t.root.split(item, false) - t.root = join(l, r, false) + l, m, r := t.root.split(item, false, t) + t.root = join(l, r, false, t) return m != nil } @@ -178,11 +182,14 @@ func (t *Tree[T]) DeleteMutable(item T) bool { // To create very large trees, it may be time-saving to slice the input data into chunks, // fan out for creation and combine the generated subtrees with non-immutable unions. func (t Tree[T]) Union(other Tree[T], overwrite bool, immutable bool) Tree[T] { - t.root = t.root.union(other.root, overwrite, immutable) + t.root = t.root.union(other.root, overwrite, immutable, &t) return t } -func (n *node[T]) union(b *node[T], overwrite bool, immutable bool) *node[T] { +// union combines to treaps. +// +// The parameter t is needed to access the compare function. +func (n *node[T]) union(b *node[T], overwrite bool, immutable bool, t *Tree[T]) *node[T] { // recursion stop condition if n == nil { return b @@ -203,7 +210,7 @@ func (n *node[T]) union(b *node[T], overwrite bool, immutable bool) *node[T] { } // the treap with the lower priority is split with the root key in the treap with the higher priority - l, dupe, r := b.split(n.item, immutable) + l, dupe, r := b.split(n.item, immutable, t) // the treaps may have duplicate items if overwrite && dupe != nil { @@ -211,9 +218,9 @@ func (n *node[T]) union(b *node[T], overwrite bool, immutable bool) *node[T] { } // rec-descent - n.left = n.left.union(l, overwrite, immutable) - n.right = n.right.union(r, overwrite, immutable) - n.recalc() + n.left = n.left.union(l, overwrite, immutable, t) + n.right = n.right.union(r, overwrite, immutable, t) + n.recalc(t) return n } @@ -222,7 +229,9 @@ func (n *node[T]) union(b *node[T], overwrite bool, immutable bool) *node[T] { // and greater-than the provided item (BST key). The resulting nodes are // properly formed treaps or nil. // If the split must be immutable, first copy concerned nodes. -func (n *node[T]) split(key T, immutable bool) (left, mid, right *node[T]) { +// +// The parameter t is needed to access the compare function. +func (n *node[T]) split(key T, immutable bool, t *Tree[T]) (left, mid, right *node[T]) { // recursion stop condition if n == nil { return nil, nil, nil @@ -232,12 +241,12 @@ func (n *node[T]) split(key T, immutable bool) (left, mid, right *node[T]) { n = n.copyNode() } - cmp := compare(n.item, key) + cmp := t.compare(n.item, key) switch { case cmp < 0: - l, m, r := n.right.split(key, immutable) + l, m, r := n.right.split(key, immutable, t) n.right = l - n.recalc() // node has changed, recalc + n.recalc(t) // node has changed, recalc return n, m, r // // (k) @@ -246,9 +255,9 @@ func (n *node[T]) split(key T, immutable bool) (left, mid, right *node[T]) { // l r // case cmp > 0: - l, m, r := n.left.split(key, immutable) + l, m, r := n.left.split(key, immutable, t) n.left = r - n.recalc() // node has changed, recalc + n.recalc(t) // node has changed, recalc return l, m, n // // (k) @@ -259,7 +268,7 @@ func (n *node[T]) split(key T, immutable bool) (left, mid, right *node[T]) { default: l, r := n.left, n.right n.left, n.right = nil, nil - n.recalc() // node has changed, recalc + n.recalc(t) // node has changed, recalc return l, n, r // // (k) @@ -279,7 +288,7 @@ func (t Tree[T]) Find(item T) (result T, ok bool) { return } - cmp := compare(item, n.item) + cmp := t.compare(item, n.item) switch { case cmp == 0: return n.item, true @@ -350,43 +359,45 @@ func (t Tree[T]) Find(item T) (result T, ok bool) { // tree.CoverLCP("2001:7c0:3100::/40") returns "2000::/3", true // func (t Tree[T]) CoverLCP(item T) (result T, ok bool) { - return t.root.lcp(item) + return t.root.lcp(item, &t) } -// lcp -func (n *node[T]) lcp(item T) (result T, ok bool) { +// lcp rec-descent. +// +// The parameter t is needed to access the compare function. +func (n *node[T]) lcp(item T, t *Tree[T]) (result T, ok bool) { if n == nil { return } // fast exit, node has too small max upper interval value (augmented value) - if cmpRR(item, n.maxUpper.item) > 0 { + if t.cmpRR(item, n.maxUpper.item) > 0 { return } - cmp := compare(n.item, item) + cmp := t.compare(n.item, item) switch { case cmp > 0: // left rec-descent - return n.left.lcp(item) + return n.left.lcp(item, t) case cmp == 0: // equality is always the shortest containing hull return n.item, true } // right backtracking - result, ok = n.right.lcp(item) + result, ok = n.right.lcp(item, t) if ok { return result, ok } // not found in right subtree, try this node - if covers(n.item, item) { + if t.covers(n.item, item) { return n.item, true } // left rec-descent - return n.left.lcp(item) + return n.left.lcp(item, t) } // CoverSCP returns the interval with the shortest-common-prefix that covers the item. @@ -426,147 +437,159 @@ func (n *node[T]) lcp(item T) (result T, ok bool) { // tree.CoverSCP(ival{6,9}) returns ival{}, false // func (t Tree[T]) CoverSCP(item T) (result T, ok bool) { - return t.root.scp(item) + return t.root.scp(item, &t) } -// scp -func (n *node[T]) scp(item T) (result T, ok bool) { +// scp rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) scp(item T, t *Tree[T]) (result T, ok bool) { if n == nil { return } // fast exit, node has too small max upper interval value (augmented value) - if cmpRR(item, n.maxUpper.item) > 0 { + if t.cmpRR(item, n.maxUpper.item) > 0 { return } // left backtracking - if result, ok = n.left.scp(item); ok { + if result, ok = n.left.scp(item, t); ok { return result, ok } // this item - if covers(n.item, item) { + if t.covers(n.item, item) { return n.item, true } // right rec-descent - return n.right.scp(item) + return n.right.scp(item, t) } // Covers returns all intervals that cover the item. // The returned intervals are in sorted order. func (t Tree[T]) Covers(item T) []T { - return t.root.covers(item) + return t.root.covers(item, &t) } -// covers -func (n *node[T]) covers(item T) (result []T) { +// covers rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) covers(item T, t *Tree[T]) (result []T) { if n == nil { return } // nope, subtree has too small upper interval value - if cmpRR(item, n.maxUpper.item) > 0 { + if t.cmpRR(item, n.maxUpper.item) > 0 { return } // in-order traversal for supersets, recursive call to left tree - result = append(result, n.left.covers(item)...) + result = append(result, n.left.covers(item, t)...) // n.item covers item - if covers(n.item, item) { + if t.covers(n.item, item) { result = append(result, n.item) } // recursive call to right tree - return append(result, n.right.covers(item)...) + return append(result, n.right.covers(item, t)...) } // CoveredBy returns all intervals that are covered by item. // The returned intervals are in sorted order. func (t Tree[T]) CoveredBy(item T) []T { - return t.root.coveredBy(item) + return t.root.coveredBy(item, &t) } -// coveredBy -func (n *node[T]) coveredBy(item T) (result []T) { +// coveredBy rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) coveredBy(item T, t *Tree[T]) (result []T) { if n == nil { return } // nope, subtree has too big upper interval value - if cmpRR(item, n.minUpper.item) < 0 { + if t.cmpRR(item, n.minUpper.item) < 0 { return } // in-order traversal for subsets, recursive call to left tree - result = append(result, n.left.coveredBy(item)...) + result = append(result, n.left.coveredBy(item, t)...) // item covers n.item - if covers(item, n.item) { + if t.covers(item, n.item) { result = append(result, n.item) } // recursive call to right tree - return append(result, n.right.coveredBy(item)...) + return append(result, n.right.coveredBy(item, t)...) } // Intersects returns true if any interval intersects item. func (t Tree[T]) Intersects(item T) bool { - return t.root.intersects(item) + return t.root.intersects(item, &t) } -func (n *node[T]) intersects(item T) bool { +// intersetcs rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) intersects(item T, t *Tree[T]) bool { if n == nil { return false } // nope, subtree has too small upper value for intersection - if cmpLR(item, n.maxUpper.item) > 0 { + if t.cmpLR(item, n.maxUpper.item) > 0 { return false } // recursive call to left tree - if n.left.intersects(item) { + if n.left.intersects(item, t) { return true } // this n.item - if intersects(n.item, item) { + if t.intersects(n.item, item) { return true } // recursive call to right tree - return n.right.intersects(item) + return n.right.intersects(item, t) } // Intersections returns all intervals that intersect with item. // The returned intervals are in sorted order. func (t Tree[T]) Intersections(item T) []T { - return t.root.isections(item) + return t.root.isections(item, &t) } -func (n *node[T]) isections(item T) (result []T) { +// isections rec-descent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) isections(item T, t *Tree[T]) (result []T) { if n == nil { return } // nope, subtree has too small upper value for intersection - if cmpLR(item, n.maxUpper.item) > 0 { + if t.cmpLR(item, n.maxUpper.item) > 0 { return } // in-order traversal for intersections, recursive call to left tree - result = append(result, n.left.isections(item)...) + result = append(result, n.left.isections(item, t)...) // this n.item - if intersects(n.item, item) { + if t.intersects(n.item, item) { result = append(result, n.item) } // recursive call to right tree - return append(result, n.right.isections(item)...) + return append(result, n.right.isections(item, t)...) } // Precedes returns all intervals that precedes the item. @@ -584,30 +607,33 @@ func (n *node[T]) isections(item T) (result []T) { // Precedes(item) => [D, B] // func (t Tree[T]) Precedes(item T) []T { - l, _, _ := t.root.split(item, true) - return l.precedes(item) + l, _, _ := t.root.split(item, true, &t) + return l.precedes(item, &t) } -func (n *node[T]) precedes(item T) (result []T) { +// precedes rec-desent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) precedes(item T, t *Tree[T]) (result []T) { if n == nil { return } // nope, all intervals in this subtree intersects with item - if cmpLR(item, n.minUpper.item) <= 0 { + if t.cmpLR(item, n.minUpper.item) <= 0 { return } // recursive call to ... - result = append(result, n.left.precedes(item)...) + result = append(result, n.left.precedes(item, t)...) // this n.item - if !intersects(n.item, item) { + if !t.intersects(n.item, item) { result = append(result, n.item) } // recursive call to right tree - return append(result, n.right.precedes(item)...) + return append(result, n.right.precedes(item, t)...) } // PrecededBy returns all intervals that are preceded by the item. @@ -625,37 +651,42 @@ func (n *node[T]) precedes(item T) (result []T) { // PrecededBy(item) => [B, D] // func (t Tree[T]) PrecededBy(item T) []T { - _, _, r := t.root.split(item, true) - return r.precededby(item) + _, _, r := t.root.split(item, true, &t) + return r.precededby(item, &t) } -func (n *node[T]) precededby(item T) (result []T) { +// precededBy rec-desent +// +// The parameter t is needed to access the compare function. +func (n *node[T]) precededby(item T, t *Tree[T]) (result []T) { if n == nil { return } // skip some left wings if n.left != nil { - if intersects(item, n.item) { + if t.intersects(item, n.item) { // skip left, proceed instead with left.right - result = append(result, n.left.right.precededby(item)...) + result = append(result, n.left.right.precededby(item, t)...) } else { - result = append(result, n.left.precededby(item)...) + result = append(result, n.left.precededby(item, t)...) } } // this n.item - if !intersects(n.item, item) { + if !t.intersects(n.item, item) { result = append(result, n.item) } // recursive call to right - return append(result, n.right.precededby(item)...) + return append(result, n.right.precededby(item, t)...) } // join combines two disjunct treaps. All nodes in treap n have keys <= that of treap m // for this algorithm to work correctly. If the join must be immutable, first copy concerned nodes. -func join[T Interface[T]](n, m *node[T], immutable bool) *node[T] { +// +// The parameter t is needed to access the compare function. +func join[T any](n, m *node[T], immutable bool, t *Tree[T]) *node[T] { // recursion stop condition if n == nil { return m @@ -672,8 +703,8 @@ func join[T Interface[T]](n, m *node[T], immutable bool) *node[T] { if immutable { n = n.copyNode() } - n.right = join(n.right, m, immutable) - n.recalc() + n.right = join(n.right, m, immutable, t) + n.recalc(t) return n } else { // m @@ -683,15 +714,17 @@ func join[T Interface[T]](n, m *node[T], immutable bool) *node[T] { if immutable { m = m.copyNode() } - m.left = join(n, m.left, immutable) - m.recalc() + m.left = join(n, m.left, immutable, t) + m.recalc(t) return m } } // recalc the augmented fields in treap node after each creation/modification with values in descendants. // Only one level deeper must be considered. The treap datastructure is very easy to augment. -func (n *node[T]) recalc() { +// +// The parameter t is needed to access the compare function. +func (n *node[T]) recalc(t *Tree[T]) { if n == nil { return } @@ -701,21 +734,21 @@ func (n *node[T]) recalc() { n.maxUpper = n if n.right != nil { - if cmpRR(n.minUpper.item, n.right.minUpper.item) > 0 { + if t.cmpRR(n.minUpper.item, n.right.minUpper.item) > 0 { n.minUpper = n.right.minUpper } - if cmpRR(n.maxUpper.item, n.right.maxUpper.item) < 0 { + if t.cmpRR(n.maxUpper.item, n.right.maxUpper.item) < 0 { n.maxUpper = n.right.maxUpper } } if n.left != nil { - if cmpRR(n.minUpper.item, n.left.minUpper.item) > 0 { + if t.cmpRR(n.minUpper.item, n.left.minUpper.item) > 0 { n.minUpper = n.left.minUpper } - if cmpRR(n.maxUpper.item, n.left.maxUpper.item) < 0 { + if t.cmpRR(n.maxUpper.item, n.left.maxUpper.item) < 0 { n.maxUpper = n.left.maxUpper } } diff --git a/treap_test.go b/treap_test.go index 721402b..4c78274 100644 --- a/treap_test.go +++ b/treap_test.go @@ -48,11 +48,17 @@ func equals(a, b Ival) bool { return a[0] == b[0] && a[1] == b[1] } +func equalStatistics(t1, t2 interval.Tree[Ival]) bool { + a1, b1, c1, d1 := t1.Statistics() + a2, b2, c2, d2 := t2.Statistics() + return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2 +} + func TestNewTree(t *testing.T) { t.Parallel() var zeroItem Ival - var zeroTree interval.Tree[Ival] + zeroTree := interval.NewTree(compareIval) if zeroTree.String() != "" { t.Errorf("String() = %v, want \"\"", "") @@ -96,10 +102,6 @@ func TestNewTree(t *testing.T) { t.Errorf("Insert(), got: %v, want: 1", size) } - if size, _, _, _ := zeroTree.Clone().Statistics(); size != 0 { - t.Errorf("Clone(), got: %v, want: 0", size) - } - if s := zeroTree.CoveredBy(zeroItem); s != nil { t.Errorf("CoveredBy(), got: %v, want: nil", s) } @@ -171,8 +173,8 @@ func TestTreeWithDups(t *testing.T) { {3, 13}, } - tree := interval.NewTree(is...) - if size, _, _, _ := tree.Statistics(); size != 5 { + tree1 := tree.Insert(is...) + if size, _, _, _ := tree1.Statistics(); size != 5 { t.Errorf("Size() = %v, want 5", size) } @@ -183,72 +185,37 @@ func TestTreeWithDups(t *testing.T) { └─ 42...67 └─ 48...50 ` - if tree.String() != asStr { - t.Errorf("Fprint()\nwant:\n%sgot:\n%s", asStr, tree.String()) + if tree1.String() != asStr { + t.Errorf("Fprint()\nwant:\n%sgot:\n%s", asStr, tree1.String()) } } func TestImmutable(t *testing.T) { t.Parallel() - tree1 := interval.NewTree(ps...) - tree2 := tree1.Clone() + tree1 := tree.Insert(ps...) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("cloned tree is not deep equal to original") - } - - if _, ok := tree1.Delete(tree2.Min()); !ok { + if _, ok := tree1.Delete(tree1.Min()); !ok { t.Fatal("Delete, could not delete min item") } - if !reflect.DeepEqual(tree1, tree2) { + if _, ok := tree1.Delete(tree1.Min()); !ok { t.Fatal("Delete changed receiver") } item := Ival{111, 666} _ = tree1.Insert(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("Insert changed receiver") - } - _, _ = tree1.CoverLCP(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("CoverLCP changed receiver") - } - - _, _ = tree1.CoverSCP(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("CoverSCP changed receiver") - } - - _ = tree1.CoveredBy(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("Covered changed receiver") - } - - _ = tree1.CoveredBy(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("Covers changed receiver") - } - - _ = tree1.Intersections(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("Intersections changed receiver") - } - - _ = tree1.Precedes(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("Precedes changed receiver") - } - - _ = tree1.PrecededBy(item) - if !reflect.DeepEqual(tree1, tree2) { - t.Fatal("PrecededBy changed receiver") + if _, ok := tree1.Find(item); ok { + t.Fatal("Insert changed receiver") } } func TestMutable(t *testing.T) { - tree1 := interval.NewTree(ps...) - tree2 := tree1.Clone() + tree1 := tree.Insert(ps...) + clone := tree1.Clone() + + if !equalStatistics(tree1, clone) { + t.Fatalf("Clone, something wrong, statistics differs") + } min := tree1.Min() @@ -257,22 +224,27 @@ func TestMutable(t *testing.T) { t.Fatal("DeleteMutable, could not delete min item") } - if reflect.DeepEqual(tree1, tree2) { + if equalStatistics(tree1, clone) { + t.Fatal("DeleteMutable didn't change receiver") + } + + if ok = (&tree1).DeleteMutable(min); ok { t.Fatal("DeleteMutable didn't change receiver") } - // reset tree1, tree2 - tree1 = interval.NewTree(ps...) - tree2 = tree1.Clone() + // reset + tree1 = tree.Insert(ps...) + clone = tree1.Clone() + + if !equalStatistics(tree1, clone) { + t.Fatalf("Clone, something wrong, statistics differs") + } item := Ival{111, 666} (&tree1).InsertMutable(item) - if reflect.DeepEqual(tree1, tree2) { - t.Fatal("InsertMutable didn't change receiver") - } - if _, ok := tree1.Delete(item); !ok { - t.Fatal("InsertMutable didn't change receiver") + if _, ok := tree1.Find(item); !ok { + t.Fatal("InsertMutable didn't changed receiver") } } @@ -280,14 +252,14 @@ func TestFind(t *testing.T) { t.Parallel() ivals := generateIvals(100_00) - tree := interval.NewTree(ivals...) + tree1 := tree.Insert(ivals...) for _, ival := range ivals { - item, ok := tree.Find(ival) + item, ok := tree1.Find(ival) if ok != true { t.Errorf("Find(%v) = %v, want %v", item, ok, true) } - ll, rr, _, _ := item.Compare(ival) + ll, rr, _, _ := compareIval(item, ival) if ll != 0 || rr != 0 { t.Errorf("Find(%v) = %v, want %v", ival, item, ival) } @@ -299,7 +271,7 @@ func TestLookup(t *testing.T) { for i := 0; i < 100; i++ { // bring some variance into the Treap due to the prio randomness - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) // ▼ // ├─ 0...6 @@ -315,58 +287,58 @@ func TestLookup(t *testing.T) { // └─ 7...9 item := Ival{0, 5} - if got, _ := tree.CoverLCP(item); got != item { + if got, _ := tree1.CoverLCP(item); got != item { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, item) } item = Ival{5, 5} want := Ival{4, 8} - if got, _ := tree.CoverLCP(item); got != want { + if got, _ := tree1.CoverLCP(item); got != want { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, want) } item = Ival{8, 9} want = Ival{7, 9} - if got, _ := tree.CoverLCP(item); got != want { + if got, _ := tree1.CoverLCP(item); got != want { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, want) } item = Ival{3, 8} want = Ival{2, 8} - if got, _ := tree.CoverLCP(item); got != want { + if got, _ := tree1.CoverLCP(item); got != want { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, want) } item = Ival{19, 55} - if got, ok := tree.CoverLCP(item); ok { + if got, ok := tree1.CoverLCP(item); ok { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, !ok) } item = Ival{0, 19} - if got, ok := tree.CoverLCP(item); ok { + if got, ok := tree1.CoverLCP(item); ok { t.Errorf("CoverLCP(%v) = %v, want %v", item, got, !ok) } item = Ival{7, 7} want = Ival{1, 8} - if got, _ := tree.CoverSCP(item); got != want { + if got, _ := tree1.CoverSCP(item); got != want { t.Errorf("CoverSCP(%v) = %v, want %v", item, got, want) } item = Ival{3, 6} want = Ival{0, 6} - if got, _ := tree.CoverSCP(item); got != want { + if got, _ := tree1.CoverSCP(item); got != want { t.Errorf("CoverSCP(%v) = %v, want %v", item, got, want) } item = Ival{3, 7} want = Ival{1, 8} - if got, _ := tree.CoverSCP(item); got != want { + if got, _ := tree1.CoverSCP(item); got != want { t.Errorf("CoverSCP(%v) = %v, want %v", item, got, want) } item = Ival{0, 7} - if _, ok := tree.CoverSCP(item); ok { + if _, ok := tree1.CoverSCP(item); ok { t.Errorf("CoverSCP(%v) = %v, want %v", item, ok, false) } @@ -376,7 +348,7 @@ func TestLookup(t *testing.T) { func TestCoveredBy(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var want []Ival // ▼ @@ -394,7 +366,7 @@ func TestCoveredBy(t *testing.T) { item := Ival{0, 6} want = []Ival{{0, 6}, {0, 5}, {1, 5}, {1, 4}} - covered := tree.CoveredBy(item) + covered := tree1.CoveredBy(item) if !reflect.DeepEqual(covered, want) { t.Fatalf("Covered, got: %v, want: %v", covered, want) @@ -403,7 +375,7 @@ func TestCoveredBy(t *testing.T) { // ### item = Ival{3, 6} want = nil - covered = tree.CoveredBy(item) + covered = tree1.CoveredBy(item) if !reflect.DeepEqual(covered, want) { t.Fatalf("Covered, got: %v, want: %v", covered, want) @@ -412,7 +384,7 @@ func TestCoveredBy(t *testing.T) { // ### item = Ival{3, 11} want = []Ival{{4, 8}, {6, 7}, {7, 9}} - covered = tree.CoveredBy(item) + covered = tree1.CoveredBy(item) if !reflect.DeepEqual(covered, want) { t.Fatalf("Covered(%v), got: %+v, want: %+v", item, covered, want) @@ -422,7 +394,7 @@ func TestCoveredBy(t *testing.T) { func TestCovers(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var want []Ival // ▼ @@ -440,7 +412,7 @@ func TestCovers(t *testing.T) { item := Ival{0, 6} want = []Ival{{0, 6}} - covers := tree.Covers(item) + covers := tree1.Covers(item) if !reflect.DeepEqual(covers, want) { t.Fatalf("Covers(%v), got: %v, want: %v", item, covers, want) @@ -449,7 +421,7 @@ func TestCovers(t *testing.T) { // ### item = Ival{3, 7} want = []Ival{{1, 8}, {1, 7}, {2, 8}, {2, 7}} - covers = tree.Covers(item) + covers = tree1.Covers(item) if !reflect.DeepEqual(covers, want) { t.Fatalf("Covers(%v), got: %v, want: %v", item, covers, want) @@ -458,7 +430,7 @@ func TestCovers(t *testing.T) { // ### item = Ival{3, 11} want = nil - covers = tree.Covers(item) + covers = tree1.Covers(item) if !reflect.DeepEqual(covers, want) { t.Fatalf("Covers(%v), got: %+v, want: %+v", item, covers, want) @@ -468,7 +440,7 @@ func TestCovers(t *testing.T) { func TestIntersects(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) // ▼ // ├─ 0...6 @@ -485,7 +457,7 @@ func TestIntersects(t *testing.T) { item := Ival{7, 7} want := true - got := tree.Intersects(item) + got := tree1.Intersects(item) if got != want { t.Fatalf("Intersects(%v), got: %v, want: %v", item, got, want) @@ -493,7 +465,7 @@ func TestIntersects(t *testing.T) { item = Ival{9, 17} want = true - got = tree.Intersects(item) + got = tree1.Intersects(item) if got != want { t.Fatalf("Intersects(%v), got: %v, want: %v", item, got, want) @@ -501,7 +473,7 @@ func TestIntersects(t *testing.T) { item = Ival{1, 1} want = true - got = tree.Intersects(item) + got = tree1.Intersects(item) if got != want { t.Fatalf("Intersects(%v), got: %v, want: %v", item, got, want) @@ -509,7 +481,7 @@ func TestIntersects(t *testing.T) { item = Ival{10, 12} want = false - got = tree.Intersects(item) + got = tree1.Intersects(item) if got != want { t.Fatalf("Intersects(%v), got: %v, want: %v", item, got, want) @@ -519,7 +491,7 @@ func TestIntersects(t *testing.T) { func TestIntersections(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var want []Ival // ▼ @@ -537,7 +509,7 @@ func TestIntersections(t *testing.T) { item := Ival{7, 7} want = []Ival{{1, 8}, {1, 7}, {2, 8}, {2, 7}, {4, 8}, {6, 7}, {7, 9}} - intersections := tree.Intersections(item) + intersections := tree1.Intersections(item) if !reflect.DeepEqual(intersections, want) { t.Fatalf("Intersections(%v), got: %v, want: %v", item, intersections, want) @@ -546,7 +518,7 @@ func TestIntersections(t *testing.T) { // ### item = Ival{8, 10} want = []Ival{{1, 8}, {2, 8}, {4, 8}, {7, 9}} - intersections = tree.Intersections(item) + intersections = tree1.Intersections(item) if !reflect.DeepEqual(intersections, want) { t.Fatalf("Intersections(%v), got: %v, want: %v", item, intersections, want) @@ -555,7 +527,7 @@ func TestIntersections(t *testing.T) { // ### item = Ival{10, 15} want = nil - intersections = tree.Intersections(item) + intersections = tree1.Intersections(item) if !reflect.DeepEqual(intersections, want) { t.Fatalf("Intersections(%v), got: %+v, want: %+v", item, intersections, want) @@ -565,7 +537,7 @@ func TestIntersections(t *testing.T) { func TestPrecedes(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var want []Ival // ▼ @@ -583,7 +555,7 @@ func TestPrecedes(t *testing.T) { item := Ival{7, 7} want = []Ival{{0, 6}, {0, 5}, {1, 5}, {1, 4}} - precedes := tree.Precedes(item) + precedes := tree1.Precedes(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("Precedes(%v), got: %v, want: %v", item, precedes, want) @@ -592,7 +564,7 @@ func TestPrecedes(t *testing.T) { // ### item = Ival{5, 10} want = []Ival{{1, 4}} - precedes = tree.Precedes(item) + precedes = tree1.Precedes(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("Precedes(%v), got: %v, want: %v", item, precedes, want) @@ -601,7 +573,7 @@ func TestPrecedes(t *testing.T) { // ### item = Ival{0, 9} want = nil - precedes = tree.Precedes(item) + precedes = tree1.Precedes(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("Precedes(%v), got: %+v, want: %+v", item, precedes, want) @@ -611,7 +583,7 @@ func TestPrecedes(t *testing.T) { func TestPrecededBy(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var want []Ival // ▼ @@ -629,7 +601,7 @@ func TestPrecededBy(t *testing.T) { item := Ival{4, 4} want = []Ival{{6, 7}, {7, 9}} - precedes := tree.PrecededBy(item) + precedes := tree1.PrecededBy(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("PrecededBy(%v), got: %v, want: %v", item, precedes, want) @@ -638,7 +610,7 @@ func TestPrecededBy(t *testing.T) { // ### item = Ival{1, 2} want = []Ival{{4, 8}, {6, 7}, {7, 9}} - precedes = tree.PrecededBy(item) + precedes = tree1.PrecededBy(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("PrecededBy(%v), got: %v, want: %v", item, precedes, want) @@ -647,7 +619,7 @@ func TestPrecededBy(t *testing.T) { // ### item = Ival{0, 7} want = nil - precedes = tree.PrecededBy(item) + precedes = tree1.PrecededBy(item) if !reflect.DeepEqual(precedes, want) { t.Fatalf("PrecededBy(%v), got: %+v, want: %+v", item, precedes, want) @@ -656,11 +628,11 @@ func TestPrecededBy(t *testing.T) { func TestVisit(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) var collect []Ival want := 4 - tree.Visit(tree.Min(), tree.Max(), func(item Ival) bool { + tree1.Visit(tree1.Min(), tree1.Max(), func(item Ival) bool { collect = append(collect, item) return len(collect) != want }) @@ -671,19 +643,19 @@ func TestVisit(t *testing.T) { collect = nil want = 9 - tree.Visit(tree.Max(), tree.Min(), func(item Ival) bool { + tree1.Visit(tree1.Max(), tree1.Min(), func(item Ival) bool { collect = append(collect, item) return true }) - want, _, _, _ = tree.Statistics() + want, _, _, _ = tree1.Statistics() if len(collect) != want { t.Fatalf("Visit() descending, want: %d got: %v, %v", want, len(collect), collect) } collect = nil want = 2 - tree.Visit(tree.Max(), tree.Min(), func(item Ival) bool { + tree1.Visit(tree1.Max(), tree1.Min(), func(item Ival) bool { collect = append(collect, item) return len(collect) != want }) @@ -691,25 +663,25 @@ func TestVisit(t *testing.T) { func TestMinMax(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) want := Ival{0, 6} - if tree.Min() != want { - t.Fatalf("Min(), want: %v, got: %v", want, tree.Min()) + if tree1.Min() != want { + t.Fatalf("Min(), want: %v, got: %v", want, tree1.Min()) } want = Ival{7, 9} - if tree.Max() != want { - t.Fatalf("Max(), want: %v, got: %v", want, tree.Max()) + if tree1.Max() != want { + t.Fatalf("Max(), want: %v, got: %v", want, tree1.Max()) } } func TestUnion(t *testing.T) { t.Parallel() - tree := interval.NewTree[Ival]() + tree1 := tree.Insert() for i := range ps { - b := interval.NewTree(ps[i]) - tree = tree.Union(b, false, true) + b := tree1.Insert(ps[i]) + tree1 = tree1.Union(b, false, true) } asStr := `▼ @@ -726,18 +698,18 @@ func TestUnion(t *testing.T) { └─ 7...9 ` - if tree.String() != asStr { - t.Errorf("Fprint()\nwant:\n%sgot:\n%s", asStr, tree.String()) + if tree1.String() != asStr { + t.Errorf("Fprint()\nwant:\n%sgot:\n%s", asStr, tree1.String()) } // now with dupe overwrite for i := range ps { - b := interval.NewTree(ps[i]) - tree = tree.Union(b, true, true) + b := tree1.Insert(ps[i]) + tree1 = tree1.Union(b, true, true) } - if tree.String() != asStr { - t.Errorf("String()\nwant:\n%sgot:\n%s", asStr, tree.String()) + if tree1.String() != asStr { + t.Errorf("String()\nwant:\n%sgot:\n%s", asStr, tree1.String()) } ps2 := []Ival{ @@ -754,8 +726,8 @@ func TestUnion(t *testing.T) { {7, 9}, } - tree2 := interval.NewTree(ps2...) - tree = tree.Union(tree2, false, false) + tree2 := tree1.Insert(ps2...) + tree1 = tree1.Union(tree2, false, false) asStr = `▼ ├─ 0...6 @@ -777,8 +749,8 @@ func TestUnion(t *testing.T) { └─ 9...40 ` - if tree.String() != asStr { - t.Errorf("String()\nwant:\n%sgot:\n%s", asStr, tree.String()) + if tree1.String() != asStr { + t.Errorf("String()\nwant:\n%sgot:\n%s", asStr, tree1.String()) } } @@ -788,9 +760,9 @@ func TestStatistics(t *testing.T) { for n := 10_000; n <= 1_000_000; n *= 10 { count := strconv.Itoa(n) t.Run(count, func(t *testing.T) { - tree := interval.NewTree(generateIvals(n)...) + tree1 := tree.Insert(generateIvals(n)...) - size, _, averageDepth, deviation := tree.Statistics() + size, _, averageDepth, deviation := tree1.Statistics() if size != n { t.Fatalf("size, got: %d, want: %d", size, n) } @@ -812,10 +784,10 @@ func TestStatistics(t *testing.T) { func TestPrintBST(t *testing.T) { t.Parallel() - tree := interval.NewTree(ps...) + tree1 := tree.Insert(ps...) w := new(strings.Builder) - _ = tree.FprintBST(w) + _ = tree1.FprintBST(w) lc := len(strings.Split(w.String(), "\n")) want := 12 @@ -826,14 +798,14 @@ func TestPrintBST(t *testing.T) { func TestMatch(t *testing.T) { t.Parallel() - tree := interval.NewTree(generateIvals(100_000)...) + tree1 := tree.Insert(generateIvals(100_000)...) n := 100 for i := 0; i < n; i++ { probe := generateIvals(100_000)[0] t.Run(probe.String(), func(t *testing.T) { - tree1 := tree.Insert(probe) + tree1 := tree1.Insert(probe) if _, ok := tree1.Find(probe); !ok { t.Fatalf("inserted item not found in tree: %v", probe) @@ -877,14 +849,14 @@ func TestMatch(t *testing.T) { func TestMissing(t *testing.T) { t.Parallel() - tree := interval.NewTree(generateIvals(100_000)...) + tree1 := tree.Insert(generateIvals(100_000)...) n := 100 for i := 0; i < n; i++ { probe := generateIvals(100_000)[0] t.Run(probe.String(), func(t *testing.T) { - tree1 := tree.Insert(probe) + tree1 := tree1.Insert(probe) var ok bool if _, ok = tree1.Find(probe); !ok {