Skip to content

Commit

Permalink
API change: cmp function instead of method
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Jan 27, 2023
1 parent 7c331fc commit bcb84aa
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 362 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
55 changes: 36 additions & 19 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand All @@ -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]

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

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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]

Expand All @@ -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) {
Expand All @@ -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) {
Expand Down
35 changes: 10 additions & 25 deletions interface.go → comparer.go
Original file line number Diff line number Diff line change
@@ -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].
//
Expand Down Expand Up @@ -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).
Expand All @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}
Loading

0 comments on commit bcb84aa

Please sign in to comment.