Skip to content

Commit

Permalink
feat: add support for point intervals
Browse files Browse the repository at this point in the history
PR #2
  • Loading branch information
rdleal committed Oct 27, 2023
1 parent 680126f commit fee6a24
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 48 deletions.
10 changes: 6 additions & 4 deletions interval/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ func (st *SearchTree[V, T]) Delete(start, end T) error {
}

intervl := interval[V, T]{
start: start,
end: end,
start: start,
end: end,
allowPoint: st.config.allowIntervalPoint,
}

if intervl.isInvalid(st.cmp) {
Expand Down Expand Up @@ -142,8 +143,9 @@ func (st *MultiValueSearchTree[V, T]) Delete(start, end T) error {
}

intervl := interval[V, T]{
start: start,
end: end,
start: start,
end: end,
allowPoint: st.config.allowIntervalPoint,
}

if intervl.isInvalid(st.cmp) {
Expand Down
83 changes: 75 additions & 8 deletions interval/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ func TestSearchTree_Delete(t *testing.T) {
}
}

func TestSearchTree_Delete_PointInterval(t *testing.T) {
cmpFunc := func(x, y int) int { return x - y }
st := NewSearchTreeWithOptions[int](cmpFunc, TreeWithIntervalPoint())

start, end := 17, 17
if err := st.Insert(start, end, 0); err != nil {
t.Fatalf("st.Insert(%v, %v): got unexpected error: %v", start, end, err)
}

if err := st.Delete(start, end); err != nil {
t.Errorf("st.Delete(%v, %v): got unexpected error: %v", start, end, err)
}
}

func TestSearchTree_Delete_EmptyTree(t *testing.T) {
st := NewSearchTree[any](func(x, y int) int { return x - y })

Expand All @@ -83,10 +97,30 @@ func TestSearchTree_Delete_Error(t *testing.T) {
st := NewSearchTree[any](func(x, y int) int { return x - y })
st.Insert(5, 10, nil)

start, end := 10, 4
err := st.Delete(start, end)
if err == nil {
t.Errorf("st.Delete(%v, %v): got nil error", start, end)
testCases := []struct {
name string
start, end int
}{
{
name: "EndSmallerThenStart",
start: 10,
end: 5,
},
{
name: "PointInterval",
start: 10,
end: 10,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

err := st.Delete(tc.start, tc.end)
if err == nil {
t.Errorf("st.Delete(%v, %v): got nil error", tc.start, tc.end)
}
})
}
})
}
Expand Down Expand Up @@ -225,6 +259,20 @@ func TestMultiValueSearchTree_Delete(t *testing.T) {
}
}

func TestMultiSearchSearchTree_Delete_PointInterval(t *testing.T) {
cmpFunc := func(x, y int) int { return x - y }
st := NewMultiValueSearchTreeWithOptions[int](cmpFunc, TreeWithIntervalPoint())

start, end := 17, 17
if err := st.Insert(start, end, 0); err != nil {
t.Fatalf("st.Insert(%v, %v): got unexpected error: %v", start, end, err)
}

if err := st.Delete(start, end); err != nil {
t.Errorf("st.Delete(%v, %v): got unexpected error: %v", start, end, err)
}
}

func TestMultiValueSearchTree_Delete_EmptyTree(t *testing.T) {
st := NewMultiValueSearchTree[any](func(x, y int) int { return x - y })

Expand All @@ -249,10 +297,29 @@ func TestMultiValueSearchTree_Delete_Error(t *testing.T) {
st := NewMultiValueSearchTree[any](func(x, y int) int { return x - y })
st.Insert(5, 10, nil)

start, end := 10, 4
err := st.Delete(start, end)
if err == nil {
t.Errorf("st.Delete(%v, %v): got nil error", start, end)
testCases := []struct {
name string
start, end int
}{
{
name: "EndSmallerThanStart",
start: 10,
end: 4,
},
{
name: "PointInterval",
start: 10,
end: 10,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := st.Delete(tc.start, tc.end)
if err == nil {
t.Errorf("st.Delete(%v, %v): got nil error", tc.start, tc.end)
}
})
}
})
}
Expand Down
23 changes: 23 additions & 0 deletions interval/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,26 @@ func ExampleMultiValueSearchTree_Upsert() {
// Output:
// [event4 event5] true
}

func ExampleTreeWithIntervalPoint() {
cmpFn := func(start, end time.Time) int {
switch {
case start.After(end):
return 1
case start.Before(end):
return -1
default:
return 0
}
}

st := interval.NewSearchTreeWithOptions[string](cmpFn, interval.TreeWithIntervalPoint())

pointInerval := time.Now()
st.Insert(pointInerval, pointInerval, "event")

vals, ok := st.Find(pointInerval, pointInerval)
fmt.Println(vals, ok)
// Output:
// event true
}
21 changes: 12 additions & 9 deletions interval/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ func (st *SearchTree[V, T]) Insert(start, end T, val V) error {
defer st.mu.Unlock()

intervl := interval[V, T]{
start: start,
end: end,
val: val,
start: start,
end: end,
val: val,
allowPoint: st.config.allowIntervalPoint,
}

if intervl.isInvalid(st.cmp) {
Expand Down Expand Up @@ -73,9 +74,10 @@ func newEmptyValueListError[V, T any](it interval[V, T], action string) error {
// or an EmptyValueListError if vals is an empty list.
func (st *MultiValueSearchTree[V, T]) Insert(start, end T, vals ...V) error {
intervl := interval[V, T]{
start: start,
end: end,
vals: vals,
start: start,
end: end,
vals: vals,
allowPoint: st.config.allowIntervalPoint,
}

if intervl.isInvalid(st.cmp) {
Expand Down Expand Up @@ -123,9 +125,10 @@ func insert[V, T any](n *node[V, T], intervl interval[V, T], cmp CmpFunc[T]) *no
// or an EmptyValueListError if vals is an empty list.
func (st *MultiValueSearchTree[V, T]) Upsert(start, end T, vals ...V) error {
intervl := interval[V, T]{
start: start,
end: end,
vals: vals,
start: start,
end: end,
vals: vals,
allowPoint: st.config.allowIntervalPoint,
}

if intervl.isInvalid(st.cmp) {
Expand Down
159 changes: 143 additions & 16 deletions interval/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,57 @@ func TestSearchTree_Insert_UpdateValue(t *testing.T) {
}
}

func TestSearchTree_Insert_PointInterval(t *testing.T) {
cmpFunc := func(x, y int) int { return x - y }
st := NewSearchTreeWithOptions[string, int](cmpFunc, TreeWithIntervalPoint())

start, end := 16, 16
val := "point-interval"

err := st.Insert(start, end, val)
if err != nil {
t.Fatalf("st.Insert(%v, %v): got unexpected error: %v", start, end, err)
}

got, ok := st.Find(start, end)
if !ok {
t.Errorf("st.Find(%v, %v): got not interval", start, end)
}

if want := val; got != want {
t.Errorf("st.Find(%v, %v): got unexpected value %v; want %v", start, end, got, want)
}
}

func TestSearchTree_Insert_Error(t *testing.T) {
t.Run("InvalidRange", func(t *testing.T) {
t.Run("InvalidInterval", func(t *testing.T) {
st := NewSearchTree[int](timeCmp)

start, end := time.Now(), time.Now().Add(-(1 * time.Hour))
err := st.Insert(start, end, 0)
if err == nil {
t.Errorf("st.Insert(%v, %v): got nil error", start, end)
now := time.Now()
testCases := []struct {
name string
start, end time.Time
}{
{
name: "EndBeforeStart",
start: now,
end: now.Add(-(1 * time.Hour)),
},
{
name: "PointInterval",
start: now,
end: now,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := st.Insert(tc.start, tc.end, 0)
var wantErr InvalidIntervalError
if !errors.As(err, &wantErr) {
t.Errorf("st.Insert(%v, %v, 0): got error type %T; want it to be %T", tc.start, tc.end, err, wantErr)
}
})
}
})
}
Expand Down Expand Up @@ -80,16 +123,58 @@ func TestMultiValueSearchTree_Insert(t *testing.T) {
}
}

func TestMultiValueSearchTree_Insert_PointInterval(t *testing.T) {
cmpFunc := func(x, y int) int { return x - y }
st := NewMultiValueSearchTreeWithOptions[string, int](cmpFunc, TreeWithIntervalPoint())

vals := []string{"value1", "value2"}
start, end := 17, 17

err := st.Insert(start, end, vals...)
if err != nil {
t.Fatalf("MultiValueSearchTree.Insert(%v, %v): got unexpected error: %v", start, end, err)
}

got, ok := st.Find(start, end)
if !ok {
t.Fatalf("st.Find(%v, %v): got no interval value; want %v", start, end, vals)
}

if want := vals; !reflect.DeepEqual(got, want) {
t.Errorf("st.Find(%v, %v): got unexpected value %q; want %q", start, end, got, want)
}
}

func TestMultiValueSearchTree_Insert_Error(t *testing.T) {
t.Run("InvalidRange", func(t *testing.T) {
t.Run("InvalidInterval", func(t *testing.T) {
st := NewMultiValueSearchTree[int](timeCmp)

start, end := time.Now(), time.Now().Add(-(1 * time.Hour))
err := st.Insert(start, end, 0)
now := time.Now()
testCases := []struct {
name string
start, end time.Time
}{
{
name: "EndBeforeStart",
start: now,
end: now.Add(-(1 * time.Hour)),
},
{
name: "PointInterval",
start: now,
end: now,
},
}

var wantErr InvalidIntervalError
if !errors.As(err, &wantErr) {
t.Errorf("st.Insert(%v, %v, 0): got error type %T; want it to be %T", start, end, err, wantErr)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := st.Insert(tc.start, tc.end, 0)

var wantErr InvalidIntervalError
if !errors.As(err, &wantErr) {
t.Errorf("st.Insert(%v, %v, 0): got error type %T; want it to be %T", tc.start, tc.end, err, wantErr)
}
})
}
})

Expand Down Expand Up @@ -137,16 +222,58 @@ func TestMultiValueSearchTree_Upsert(t *testing.T) {
}
}

func TestMultiValueSearchTree_Upsert_PointInterval(t *testing.T) {
cmpFunc := func(x, y int) int { return x - y }
st := NewMultiValueSearchTreeWithOptions[string, int](cmpFunc, TreeWithIntervalPoint())

vals := []string{"value1", "value2"}
start, end := 17, 17

err := st.Upsert(start, end, vals...)
if err != nil {
t.Fatalf("MultiValueSearchTree.Upsert(%v, %v): got unexpected error: %v", start, end, err)
}

got, ok := st.Find(start, end)
if !ok {
t.Fatalf("st.Find(%v, %v): got no interval value; want %v", start, end, vals)
}

if want := vals; !reflect.DeepEqual(got, want) {
t.Errorf("st.Find(%v, %v): got unexpected value %q; want %q", start, end, got, want)
}
}

func TestMultiValueSearchTree_Upsert_Error(t *testing.T) {
t.Run("InvalidRange", func(t *testing.T) {
st := NewMultiValueSearchTree[int](timeCmp)

start, end := time.Now(), time.Now().Add(-(1 * time.Hour))
err := st.Upsert(start, end, 0)
now := time.Now()
testCases := []struct {
name string
start, end time.Time
}{
{
name: "EndBeforeStart",
start: now,
end: now.Add(-(1 * time.Hour)),
},
{
name: "PointInterval",
start: now,
end: now,
},
}

var wantErr InvalidIntervalError
if !errors.As(err, &wantErr) {
t.Errorf("st.Upsert(%v, %v, 0): got error type %T; want it to be %T", start, end, err, wantErr)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := st.Upsert(tc.start, tc.end, 0)

var wantErr InvalidIntervalError
if !errors.As(err, &wantErr) {
t.Errorf("st.Upsert(%v, %v, 0): got error type %T; want it to be %T", tc.start, tc.end, err, wantErr)
}
})
}
})

Expand Down
Loading

0 comments on commit fee6a24

Please sign in to comment.