-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
deque: new util package replacing prepend
- Loading branch information
1 parent
3a04261
commit 1d1dca0
Showing
6 changed files
with
204 additions
and
203 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Package deque provides a slice-backed double-ended queue. | ||
package deque | ||
|
||
import "slices" | ||
|
||
// Deque is a slice-backed double-ended queue. | ||
type Deque[Elem any] struct { | ||
el []Elem | ||
// left is the position of the leftmost valid element in el. | ||
// left >= len(el) implies the ring is empty. | ||
left int | ||
} | ||
|
||
// Len returns the number of elements in the deque. | ||
func (d Deque[Elem]) Len() int { | ||
return len(d.el) - d.left | ||
} | ||
|
||
// Append adds elements tot he end of the deque. | ||
func (d Deque[Elem]) Append(ee ...Elem) Deque[Elem] { | ||
d.el = append(d.el, ee...) | ||
return d | ||
} | ||
|
||
// Prepend adds elements to the front of the deque. | ||
func (d Deque[Elem]) Prepend(ee ...Elem) Deque[Elem] { | ||
d = d.GrowFront(len(ee)) | ||
d.left -= len(ee) | ||
copy(d.Slice(), ee) | ||
return d | ||
} | ||
|
||
// GrowFront ensures there is space to [Prepend] at least n elements. | ||
func (d Deque[Elem]) GrowFront(n int) Deque[Elem] { | ||
if d.left >= n { | ||
return d | ||
} | ||
// Grow the slice, then slide the existing elements to the end. | ||
k := d.Len() | ||
d.el = slices.Grow(d.el, n) | ||
copy(d.el[cap(d.el)-k:cap(d.el)], d.Slice()) | ||
d.el = d.el[:cap(d.el)] | ||
d.left = cap(d.el) - k | ||
return d | ||
} | ||
|
||
// GrowEnd ensures there is space to [Append] at least n elements. | ||
func (d Deque[Elem]) GrowEnd(n int) Deque[Elem] { | ||
d.el = slices.Grow(d.el, n) | ||
return d | ||
} | ||
|
||
// DropEnd removes n elements from the end of the deque. | ||
// If n is negative, there is no change. | ||
// If n is larger than the deque's size, the result is empty. | ||
func (d Deque[Elem]) DropEnd(n int) Deque[Elem] { | ||
if n <= 0 { | ||
return d | ||
} | ||
if n >= d.Len() { | ||
return d.Reset() | ||
} | ||
d.el = d.el[:len(d.el)-n] | ||
return d | ||
} | ||
|
||
// DropEndWhile removes elements from the end of the deque until the predicate | ||
// returns false. | ||
// The pointer passed to the predicate is a view into the deque's memory. | ||
func (d Deque[Elem]) DropEndWhile(pred func(Elem) bool) Deque[Elem] { | ||
for len(d.el) > d.left { | ||
if !pred(d.el[len(d.el)-1]) { | ||
break | ||
} | ||
d.el = d.el[:len(d.el)-1] | ||
} | ||
return d | ||
} | ||
|
||
// Reset removes all elements from the deque. | ||
func (d Deque[Elem]) Reset() Deque[Elem] { | ||
d.left = len(d.el) | ||
return d | ||
} | ||
|
||
// Slice returns a view into the deque's memory. | ||
// Elements prepended to the deque appear at the beginning of the slice. | ||
func (d Deque[Elem]) Slice() []Elem { | ||
return d.el[d.left:] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package deque_test | ||
|
||
import ( | ||
"slices" | ||
"testing" | ||
|
||
"github.com/zephyrtronium/robot/deque" | ||
) | ||
|
||
func TestDeque(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
append []int | ||
prepend []int | ||
want []int | ||
}{ | ||
{ | ||
name: "empty", | ||
append: nil, | ||
prepend: nil, | ||
want: nil, | ||
}, | ||
{ | ||
name: "append", | ||
append: []int{1, 2}, | ||
prepend: nil, | ||
want: []int{1, 2}, | ||
}, | ||
{ | ||
name: "prepend", | ||
append: nil, | ||
prepend: []int{1, 2}, | ||
want: []int{1, 2}, | ||
}, | ||
{ | ||
name: "both", | ||
append: []int{1, 2}, | ||
prepend: []int{3, 4}, | ||
want: []int{3, 4, 1, 2}, | ||
}, | ||
} | ||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
t.Parallel() | ||
var d deque.Deque[int] | ||
invariants := func() { | ||
if d.Len() != len(d.Slice()) { | ||
t.Errorf("lens disagree: d.Len gave %d, len(d.Slice) gave %d", d.Len(), len(d.Slice())) | ||
} | ||
} | ||
invariants() | ||
d = d.Append(c.append...) | ||
invariants() | ||
d = d.Prepend(c.prepend...) | ||
invariants() | ||
if !slices.Equal(d.Slice(), c.want) { | ||
t.Errorf("wrong result: want %v, got %v", c.want, d.Slice()) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestDropEndWhile(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
start []bool | ||
want []bool | ||
}{ | ||
{ | ||
name: "empty", | ||
start: nil, | ||
want: nil, | ||
}, | ||
{ | ||
name: "none", | ||
start: []bool{false, false}, | ||
want: []bool{false, false}, | ||
}, | ||
{ | ||
name: "one", | ||
start: []bool{false, true}, | ||
want: []bool{false}, | ||
}, | ||
{ | ||
name: "end", | ||
start: []bool{true, false}, | ||
want: []bool{true, false}, | ||
}, | ||
{ | ||
name: "all", | ||
start: []bool{true, true}, | ||
want: nil, | ||
}, | ||
} | ||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
d := deque.Deque[bool]{}.Append(c.start...) | ||
d = d.DropEndWhile(func(b bool) bool { return b }) | ||
if !slices.Equal(d.Slice(), c.want) { | ||
t.Errorf("wrong result: want %v, got %v", c.want, d.Slice()) | ||
} | ||
}) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.