Skip to content

Commit

Permalink
impl. Precedes and PrecededBy
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Jan 19, 2023
1 parent a988cda commit a2094bc
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 19 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ 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))
```

Expand Down Expand Up @@ -88,7 +90,11 @@ type Interface[T any] interface {
func (t Tree[T]) Intersects(item T) bool

func (t Tree[T]) Covers(item T) []T
func (t Tree[T]) Precedes(item T) []T

func (t Tree[T]) CoveredBy(item T) []T
func (t Tree[T]) PrecededBy(item T) []T

func (t Tree[T]) Intersections(item T) []T

func (t Tree[T]) Clone() Tree[T]
Expand Down
32 changes: 31 additions & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,38 @@ func BenchmarkCovers(b *testing.B) {
}
}

func BenchmarkPrecededBy(b *testing.B) {
for m := 100; m <= 10_000; m *= 10 {
tree := interval.NewTree(generateIvals(m)...)
probe := generateIvals(1)[0]
name := "In" + intMap[m]

b.Run(name, func(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = tree.PrecededBy(probe)
}
})
}
}

func BenchmarkPrecedes(b *testing.B) {
for m := 100; m <= 10_000; m *= 10 {
tree := interval.NewTree(generateIvals(m)...)
probe := generateIvals(1)[0]
name := "In" + intMap[m]

b.Run(name, func(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = tree.Precedes(probe)
}
})
}
}

func BenchmarkIntersections(b *testing.B) {
for n := 100; n <= 100_000; n *= 10 {
for n := 100; n <= 10_000; n *= 10 {
tree := interval.NewTree(generateIvals(n)...)
probe := generateIvals(1)[0]
name := "In" + intMap[n]
Expand Down
22 changes: 22 additions & 0 deletions example_period_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ func ExampleTree_CoveredBy() {
// 7...9
}

func ExampleTree_Precedes_period() {
tree := interval.NewTree(periods...)
tree.Fprint(os.Stdout)

item := Ival{6, 6}
fmt.Printf("\nPrecedes item: %v\n", item)
for _, p := range tree.Precedes(item) {
fmt.Println(p)
}

// Output:
// ▼
// └─ 2...9
// ├─ 3...5
// │ └─ 3...4
// └─ 7...9
//
// Precedes item: 6...6
// 3...5
// 3...4
}

func ExampleTree_Visit() {
tree := interval.NewTree(periods...)
fmt.Println("parent/child printing")
Expand Down
107 changes: 91 additions & 16 deletions example_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import (
"github.com/gaissmai/interval"
)

// ############################################################################
// little helpers

// little helper
func mkTval(i, j int, s string) Tval {
t1, _ := time.Parse("2006", strconv.Itoa(i))
t2, _ := time.Parse("2006", strconv.Itoa(j))
return Tval{t: [2]time.Time{t1, t2}, name: s}
return Tval{birth: t1, death: t2, name: s}
}

// little helper
func cmpTime(a, b time.Time) int {
if a.Before(b) {
return -1
Expand All @@ -28,25 +27,24 @@ func cmpTime(a, b time.Time) int {
return 0
}

// ############################################################################

// example time period interval
type Tval struct {
t [2]time.Time
name string
birth time.Time
death time.Time
name string
}

// fmt.Stringer for formattting, not required for interval.Interface
// String, implements fmt.Stringer for nice formattting, not required for interval.Interface
func (p Tval) String() string {
return fmt.Sprintf("%s...%s (%s)", p.t[0].Format("2006"), p.t[1].Format("2006"), p.name)
return fmt.Sprintf("%s...%s (%s)", p.birth.Format("2006"), p.death.Format("2006"), p.name)
}

// implement the interval.Interface
// Compare implements the interval.Interface
func (p Tval) Compare(q Tval) (ll, rr, lr, rl int) {
return cmpTime(p.t[0], q.t[0]),
cmpTime(p.t[1], q.t[1]),
cmpTime(p.t[0], q.t[1]),
cmpTime(p.t[1], q.t[0])
return cmpTime(p.birth, q.birth),
cmpTime(p.death, q.death),
cmpTime(p.birth, q.death),
cmpTime(p.death, q.birth)
}

// example data
Expand Down Expand Up @@ -81,10 +79,65 @@ var physicists = []Tval{
mkTval(1831, 1879, "Maxwell"),
}

func ExampleInterface_time() {
func ExampleTree_Precedes_time() {
tree := interval.NewTree(physicists...)
tree.Fprint(os.Stdout)

precedes := tree.Precedes(mkTval(1643, 1727, "Newton"))
tree = interval.NewTree(precedes...)

fmt.Println("\nPrecedes Newton:")
tree.Fprint(os.Stdout)

// Output:
// ▼
// ├─ 1473...1543 (Kopernikus)
// ├─ 1544...1603 (Gilbert)
// ├─ 1564...1642 (Galilei)
// │ └─ 1571...1630 (Kepler)
// ├─ 1623...1662 (Pascal)
// ├─ 1629...1695 (Huygens)
// ├─ 1643...1727 (Newton)
// ├─ 1700...1782 (Bernoulli)
// ├─ 1707...1783 (Euler)
// ├─ 1731...1810 (Cavendish)
// ├─ 1736...1813 (Lagrange)
// │ └─ 1736...1806 (Coulomb)
// ├─ 1745...1827 (Volta)
// │ └─ 1749...1827 (Laplace)
// ├─ 1768...1830 (Fourier)
// │ └─ 1773...1829 (Young)
// ├─ 1775...1836 (Ampère)
// ├─ 1777...1855 (Gauss)
// │ └─ 1788...1827 (Fresnel)
// ├─ 1791...1867 (Faraday)
// │ ├─ 1796...1832 (Carnot)
// │ └─ 1805...1865 (Hamilton)
// ├─ 1818...1889 (Joule)
// ├─ 1821...1894 (Helholtz)
// │ └─ 1822...1888 (Clausius)
// └─ 1824...1907 (Kelvin)
// └─ 1824...1887 (Kirchhoff)
// └─ 1831...1879 (Maxwell)
//
// Precedes Newton:
// ▼
// ├─ 1473...1543 (Kopernikus)
// ├─ 1544...1603 (Gilbert)
// └─ 1564...1642 (Galilei)
// └─ 1571...1630 (Kepler)
}

func ExampleTree_PrecededBy_time() {
tree := interval.NewTree(physicists...)
tree.Fprint(os.Stdout)

precededBy := tree.PrecededBy(mkTval(1643, 1727, "Newton"))
tree = interval.NewTree(precededBy...)

fmt.Println("\nPrecededBy Newton:")
tree.Fprint(os.Stdout)

// Output:
// ▼
// ├─ 1473...1543 (Kopernikus)
Expand Down Expand Up @@ -115,4 +168,26 @@ func ExampleInterface_time() {
// └─ 1824...1907 (Kelvin)
// └─ 1824...1887 (Kirchhoff)
// └─ 1831...1879 (Maxwell)
//
// PrecededBy Newton:
// ▼
// ├─ 1731...1810 (Cavendish)
// ├─ 1736...1813 (Lagrange)
// │ └─ 1736...1806 (Coulomb)
// ├─ 1745...1827 (Volta)
// │ └─ 1749...1827 (Laplace)
// ├─ 1768...1830 (Fourier)
// │ └─ 1773...1829 (Young)
// ├─ 1775...1836 (Ampère)
// ├─ 1777...1855 (Gauss)
// │ └─ 1788...1827 (Fresnel)
// ├─ 1791...1867 (Faraday)
// │ ├─ 1796...1832 (Carnot)
// │ └─ 1805...1865 (Hamilton)
// ├─ 1818...1889 (Joule)
// ├─ 1821...1894 (Helholtz)
// │ └─ 1822...1888 (Clausius)
// └─ 1824...1907 (Kelvin)
// └─ 1824...1887 (Kirchhoff)
// └─ 1831...1879 (Maxwell)
}
88 changes: 86 additions & 2 deletions treap.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (
// node is the basic recursive data structure.
type node[T Interface[T]] struct {
// augment the treap for interval lookups
minUpper *node[T] // pointer to node in subtree with min upper value, just needed for CoveredBy()
maxUpper *node[T] // pointer to node in subtree with max upper value, needed for all other 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
//
// base treap fields, in memory efficient order
left *node[T]
Expand Down Expand Up @@ -585,6 +585,90 @@ func (n *node[T]) isections(item T) (result []T) {
return append(result, n.right.isections(item)...)
}

// Precedes returns all intervals that precedes the item.
// The returned intervals are in sorted order.
//
// example:
//
// Item |-----------------|
//
// A |---------------------------------------|
// B |-----|
// C |-----------------|
// D |-----------------|
//
// Precedes(item) => [D, B]
//
func (t Tree[T]) Precedes(item T) []T {
l, _, _ := t.root.split(item, true)
return l.precedes(item)
}

func (n *node[T]) precedes(item T) (result []T) {
if n == nil {
return
}

// nope, all intervals in this subtree intersects with item
if cmpLR(item, n.minUpper.item) <= 0 {
return
}

// recursive call to ...
result = append(result, n.left.precedes(item)...)

// this n.item
if !intersects(n.item, item) {
result = append(result, n.item)
}

// recursive call to right tree
return append(result, n.right.precedes(item)...)
}

// PrecededBy returns all intervals that are preceded by the item.
// The returned intervals are in sorted order.
//
// example:
//
// Item |-----|
//
// A |---------------------------------------|
// B |-----|
// C |-----------------|
// D |-----------------|
//
// PrecededBy(item) => [B, D]
//
func (t Tree[T]) PrecededBy(item T) []T {
_, _, r := t.root.split(item, true)
return r.precededby(item)
}

func (n *node[T]) precededby(item T) (result []T) {
if n == nil {
return
}

// skip some left wings
if n.left != nil {
if intersects(item, n.item) {
// skip left, proceed instead with left.right
result = append(result, n.left.right.precededby(item)...)
} else {
result = append(result, n.left.precededby(item)...)
}
}

// this n.item
if !intersects(n.item, item) {
result = append(result, n.item)
}

// recursive call to right
return append(result, n.right.precededby(item)...)
}

// 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] {
Expand Down
18 changes: 18 additions & 0 deletions treap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ func TestNewTree(t *testing.T) {
t.Errorf("Intersections(), got: %v, want: nil", s)
}

if s := zeroTree.Precedes(zeroItem); s != nil {
t.Errorf("Precedes(), got: %v, want: nil", s)
}

if s := zeroTree.PrecededBy(zeroItem); s != nil {
t.Errorf("PrecededBy(), got: %v, want: nil", s)
}

if s := zeroTree.Min(); s != zeroItem {
t.Errorf("Min(), got: %v, want: %v", s, zeroItem)
}
Expand Down Expand Up @@ -226,6 +234,16 @@ func TestImmutable(t *testing.T) {
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")
}
}

func TestMutable(t *testing.T) {
Expand Down

0 comments on commit a2094bc

Please sign in to comment.