From aebc43ec1df77073b835de058366a944f66c8052 Mon Sep 17 00:00:00 2001 From: Karl Gaissmaier Date: Sat, 28 Jan 2023 20:43:30 +0100 Subject: [PATCH] fine tuning --- README.md | 10 +++++----- comparer.go | 6 ++++++ treap.go | 53 ++++++++++++++++++++++++++++++++++----------------- treap_test.go | 22 +++++++++++++++++++++ 4 files changed, 69 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 91bd09a..e659bcb 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ Intersects() O(log(n)) CoverLCP() O(log(n)) CoverSCP() O(log(n)) -Covers() O(k*log(n)) -CoveredBy() O(k*log(n)) -Precedes() O(k*log(n)) -PrecededBy() O(k*log(n)) -Intersections() O(k*log(n)) +Covers() O(k+log(n)) +CoveredBy() O(k+log(n)) +Precedes() O(k+log(n)) +PrecededBy() O(k+log(n)) +Intersections() O(k+log(n)) ``` The author is propably the first (in december 2022) using augmented treaps diff --git a/comparer.go b/comparer.go index c95328c..b503684 100644 --- a/comparer.go +++ b/comparer.go @@ -135,3 +135,9 @@ func (t *Tree[T]) cmpLR(a, b T) int { _, _, lr, _ := t.cmp(a, b) return lr } + +// cmpRL, compares just the right point from a with left point from b. +func (t *Tree[T]) cmpRL(a, b T) int { + _, _, _, rl := t.cmp(a, b) + return rl +} diff --git a/treap.go b/treap.go index b1a5557..a8f95bd 100644 --- a/treap.go +++ b/treap.go @@ -372,14 +372,14 @@ func (t *Tree[T]) lcp(n *node[T], item T) (result T, ok bool) { switch cmp := t.compare(n.item, item); { case cmp > 0: - // left rec-descent + // too big, left rec-descent return t.lcp(n.left, item) case cmp == 0: // equality is always the shortest containing hull return n.item, true } - // right backtracking + // LCP => right backtracking result, ok = t.lcp(n.right, item) if ok { return result, ok @@ -452,7 +452,7 @@ func (t *Tree[T]) scp(n *node[T], item T) (result T, ok bool) { return } - // left backtracking + // SCP => left backtracking if result, ok = t.scp(n.left, item); ok { return result, ok } @@ -533,61 +533,80 @@ func (t *Tree[T]) coveredBy(n *node[T], item T) (result []T) { // Intersects returns true if any interval intersects item. func (t Tree[T]) Intersects(item T) bool { - return t.isects(t.root, item) + return t.intersects(t.root, item) } // intersects rec-descent -func (t *Tree[T]) isects(n *node[T], item T) bool { +func (t *Tree[T]) intersects(n *node[T], item T) bool { if n == nil { return false } - // nope, subtree has too small upper value for intersection + // this n.item, fast exit + if t.cmpIntersects(n.item, item) { + return true + } + + // don't traverse this subtree, subtree has too small upper value for intersection + // item -> |------| + // |-------------| <- maxUpper if t.cmpLR(item, n.maxUpper.item) > 0 { return false } // recursive call to left tree - if t.isects(n.left, item) { + // fast return if true + if t.intersects(n.left, item) { return true } - // this n.item - if t.cmpIntersects(n.item, item) { - return true + // don't traverse right subtree, subtree has too small left value for intersection. + // |---------| <- item + // n.item |-------------| + if t.cmpRL(item, n.item) < 0 { + return false } // recursive call to right tree - return t.isects(n.right, item) + return t.intersects(n.right, item) } // 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.isections(t.root, item) + return t.intersections(t.root, item) } -// isections rec-descent -func (t *Tree[T]) isections(n *node[T], item T) (result []T) { +// intersections rec-descent +func (t *Tree[T]) intersections(n *node[T], item T) (result []T) { if n == nil { return } - // nope, subtree has too small upper value for intersection + // don't traverse this subtree, subtree has too small upper value for intersection + // item -> |------| + // |-------------| <- maxUpper if t.cmpLR(item, n.maxUpper.item) > 0 { return } // in-order traversal for intersections, recursive call to left tree - result = append(result, t.isections(n.left, item)...) + result = append(result, t.intersections(n.left, item)...) // this n.item if t.cmpIntersects(n.item, item) { result = append(result, n.item) } + // don't traverse right subtree, subtree has too small left value for intersection. + // |---------| <- item + // n.item |-------------| + if t.cmpRL(item, n.item) < 0 { + return + } + // recursive call to right tree - return append(result, t.isections(n.right, item)...) + return append(result, t.intersections(n.right, item)...) } // Precedes returns all intervals that precedes the item. diff --git a/treap_test.go b/treap_test.go index c98ce2b..f0faf6d 100644 --- a/treap_test.go +++ b/treap_test.go @@ -560,6 +560,28 @@ func TestIntersects(t *testing.T) { } } +func FuzzIntersects(f *testing.F) { + ivals := genUintIvals(10_000) + tree := interval.NewTree(cmpUintInterval, ivals...) + + for i := 0; i < 10; i++ { + a := ivals[i][0] + b := ivals[i][1] + f.Add(a, b) + } + + f.Fuzz(func(t *testing.T, a, b uint) { + probe := makeUintIval(a, b) + + gotBool := tree.Intersects(probe) + gotSlice := tree.Intersections(probe) + + if gotBool && gotSlice == nil || !gotBool && gotSlice != nil { + t.Fatalf("Intersects(%v) and Intersections(%v) mismatch", probe, probe) + } + }) +} + func TestIntersections(t *testing.T) { t.Parallel()