diff --git a/difference.go b/difference.go index 69be237..1420201 100644 --- a/difference.go +++ b/difference.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // Difference returns the set of elements that are in x, but not in y. func (x Set[E]) Difference(y Set[E]) Set[E] { return difference(x, y, nil) @@ -40,7 +38,7 @@ func difference[E Elem[E]](x, y, out Set[E]) Set[E] { r := y[0] y = y[1:] - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) if !inv { z = append(z, x[:i]...) @@ -49,7 +47,7 @@ func difference[E Elem[E]](x, y, out Set[E]) Set[E] { x = x[i:] Again: - j := sort.Search(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) if j == 0 { if inv { diff --git a/intersection.go b/intersection.go index efb07b9..f89251e 100644 --- a/intersection.go +++ b/intersection.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // Intersection returns the set of elements that are in both x and y. func (x Set[E]) Intersection(y Set[E]) Set[E] { return intersection(x, y, nil) @@ -31,9 +29,9 @@ func intersection[E Elem[E]](x, y, out Set[E]) Set[E] { r := y[0] y = y[1:] - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) x = x[i:] - j := sort.Search(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) if j > 0 { start := len(z) diff --git a/intervalset.go b/intervalset.go index 04bc298..91156c7 100644 --- a/intervalset.go +++ b/intervalset.go @@ -1,8 +1,6 @@ // Package intervals is a library for manipulating sets of intervals. package intervals -import "sort" - // Elem is the type set containing all supported element types. type Elem[E any] interface { Compare(E) int @@ -114,8 +112,8 @@ func (x *Set[E]) Add(r Interval[E]) { func (x *Set[E]) AddRange(lo, hi E) { s := *x - i := sort.Search(len(s), func(i int) bool { return s[i].Low.Compare(lo) > 0 }) - // j := sort.Search(len(s), func(i int) bool { return s[i].High.Compare(hi) > 0 }) + i := sortSearch(len(s), func(i int) bool { return s[i].Low.Compare(lo) > 0 }) + // j := sortSearch(len(s), func(i int) bool { return s[i].High.Compare(hi) > 0 }) // ┌────────┬─────────────────────────────────────────┐ // │ │ j-1 j i-1 i │ @@ -145,7 +143,7 @@ func (x *Set[E]) AddRange(lo, hi E) { // Optimized: j >= i-1. off := max(0, i-1) t := s[off:] - j := off + sort.Search(len(t), func(i int) bool { return t[i].High.Compare(hi) > 0 }) + j := off + sortSearch(len(t), func(i int) bool { return t[i].High.Compare(hi) > 0 }) if i > j { // Case 1 and 2. return @@ -192,8 +190,8 @@ func (x *Set[E]) Delete(r Interval[E]) { func (x *Set[E]) DeleteRange(lo, hi E) { s := *x - i := sort.Search(len(s), func(i int) bool { return s[i].High.Compare(lo) > 0 }) - // j := sort.Search(len(s), func(i int) bool { return s[i].Low.Compare(hi) > 0 }) + i := sortSearch(len(s), func(i int) bool { return s[i].High.Compare(lo) > 0 }) + // j := sortSearch(len(s), func(i int) bool { return s[i].Low.Compare(hi) > 0 }) // ┌────────┬─────────────────────────────────────────┐ // │ │ j-1 j i-1 i │ @@ -222,7 +220,7 @@ func (x *Set[E]) DeleteRange(lo, hi E) { // Optimized: j >= i. t := s[i:] - j := i + sort.Search(len(t), func(i int) bool { return t[i].Low.Compare(hi) > 0 }) + j := i + sortSearch(len(t), func(i int) bool { return t[i].Low.Compare(hi) > 0 }) if i == j { // Case 1, 2 and 3. return @@ -276,13 +274,13 @@ func (x Set[E]) Contains(r Interval[E]) bool { // ContainsOne reports whether x contains a single element v. func (x Set[E]) ContainsOne(v E) bool { - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(v) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(v) > 0 }) return i < len(x) && x[i].Low.Compare(v) <= 0 } // ContainsRange reports whether x contains every element in range [lo, hi). func (x Set[E]) ContainsRange(lo, hi E) bool { - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(lo) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(lo) > 0 }) return i < len(x) && x[i].Low.Compare(lo) <= 0 && x[i].High.Compare(hi) >= 0 && lo.Compare(hi) < 0 } @@ -314,3 +312,21 @@ func (x Set[E]) Extent() Interval[E] { High: x[len(x)-1].High, } } + +// sortSearch is like sort.Search, avoiding an import. +func sortSearch(n int, f func(int) bool) int { + // Define f(-1) == false and f(n) == true. + // Invariant: f(i-1) == false, f(j) == true. + i, j := 0, n + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + // i ≤ h < j + if !f(h) { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + return i +} diff --git a/issubsetof.go b/issubsetof.go index be7b64f..db3f046 100644 --- a/issubsetof.go +++ b/issubsetof.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // IsSubsetOf reports whether elements in x are all in y. func (x Set[E]) IsSubsetOf(y Set[E]) bool { inv := false @@ -25,7 +23,7 @@ func (x Set[E]) IsSubsetOf(y Set[E]) bool { y = y[1:] if inv { - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) x = x[i:] if len(x) == 0 || x[0].Low.Compare(r.Low) > 0 || x[0].High.Compare(r.High) < 0 { @@ -39,7 +37,7 @@ func (x Set[E]) IsSubsetOf(y Set[E]) bool { return false } - j := sort.Search(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) if j > 0 { if x[j-1].High.Compare(r.High) > 0 { diff --git a/overlap.go b/overlap.go index 071b4d2..fee6ad0 100644 --- a/overlap.go +++ b/overlap.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // Overlaps reports whether the intersection of x and y is not empty. func (x Set[E]) Overlaps(y Set[E]) bool { for { @@ -20,9 +18,9 @@ func (x Set[E]) Overlaps(y Set[E]) bool { r := y[0] y = y[1:] - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) x = x[i:] - j := sort.Search(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) if j > 0 { return true diff --git a/symdiff.go b/symdiff.go index 8bfffd6..f1aaec6 100644 --- a/symdiff.go +++ b/symdiff.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // SymmetricDifference returns the set of elements that are in one of x and y, // but not in both. func (x Set[E]) SymmetricDifference(y Set[E]) Set[E] { @@ -34,12 +32,12 @@ func symmetricDifference[E Elem[E]](x, y, out Set[E]) Set[E] { r := y[0] y = y[1:] - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) > 0 }) z = appendIntervals(z, x[:i]...) x = x[i:] Again: - j := sort.Search(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].Low.Compare(r.High) >= 0 }) if j == 0 { z = appendInterval(z, r) diff --git a/union.go b/union.go index cf5bf12..615039f 100644 --- a/union.go +++ b/union.go @@ -1,7 +1,5 @@ package intervals -import "sort" - // Union returns the set of elements that are in either x, or y, or both. func (x Set[E]) Union(y Set[E]) Set[E] { return union(x, y, nil) @@ -31,7 +29,7 @@ func union[E Elem[E]](x, y, out Set[E]) Set[E] { r := y[0] y = y[1:] - i := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.Low) >= 0 }) + i := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.Low) >= 0 }) z = append(z, x[:i]...) x = x[i:] @@ -40,7 +38,7 @@ func union[E Elem[E]](x, y, out Set[E]) Set[E] { } Again: - j := sort.Search(len(x), func(i int) bool { return x[i].High.Compare(r.High) > 0 }) + j := sortSearch(len(x), func(i int) bool { return x[i].High.Compare(r.High) > 0 }) x = x[j:] if len(x) != 0 && x[0].Low.Compare(r.High) <= 0 {