Skip to content

Commit

Permalink
Copy sort.Search to avoid an import
Browse files Browse the repository at this point in the history
  • Loading branch information
b97tsk committed Jan 21, 2024
1 parent 9fbe6ff commit 916be2c
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 34 deletions.
6 changes: 2 additions & 4 deletions difference.go
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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]...)
Expand All @@ -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 {
Expand Down
6 changes: 2 additions & 4 deletions intersection.go
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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)
Expand Down
36 changes: 26 additions & 10 deletions intervalset.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 │
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 │
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
6 changes: 2 additions & 4 deletions issubsetof.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
6 changes: 2 additions & 4 deletions overlap.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
Expand Down
6 changes: 2 additions & 4 deletions symdiff.go
Original file line number Diff line number Diff line change
@@ -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] {
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 2 additions & 4 deletions union.go
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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:]

Expand All @@ -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 {
Expand Down

0 comments on commit 916be2c

Please sign in to comment.