Merge pull request #1 from gammazero/insertremove

Add Insert and Remove.  No negative indexing.
This commit is contained in:
Andrew J. Gillis 2018-04-29 08:13:34 -07:00 committed by GitHub
commit 09ca60094b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 291 additions and 61 deletions

View file

@ -49,7 +49,7 @@ func main() {
// Print: hello bar world
for i := 0; i < q.Len(); i++ {
fmt.Print(q.PeekAt(i), " ")
fmt.Print(q.Get(i), " ")
}
fmt.Println()
}

108
deque.go
View file

@ -123,23 +123,109 @@ func (q *Deque) Back() interface{} {
return q.buf[(q.tail-1)&(len(q.buf)-1)]
}
// PeekAt returns the element at index i in the queue. This method accepts
// both positive and negative index values. PeekAt(0) refers to the first
// (earliest added) element and is the same as Front(). PeekAt(-1) refers to
// the last (latest added) element and is the same as Back(). If the index
// is invalid, the call panics.
func (q *Deque) PeekAt(i int) interface{} {
// If indexing backwards, convert to positive index.
if i < 0 {
i += q.count
}
// Get returns the element at index i in the queue. This method accepts only
// non-negative index values. Get(0) refers to the first element and is the
// same as Front(). Get(Len()-1) refers to the last element and is the same
// as Back(). If the index is invalid, the call panics.
func (q *Deque) Get(i int) interface{} {
if i < 0 || i >= q.count {
panic("deque: PeekAt() called with index out of range")
panic("deque: Get() called with index out of range")
}
// bitwise modulus
return q.buf[(q.head+i)&(len(q.buf)-1)]
}
// Insert is used to insert an element into the middle of the queue.
// Insert(0,e) is the same as PushFront(e) and Insert(Len(),e) is the same as
// PushBack(e). Accepts only non-negative index values, and panics if index is
// out of range.
func (q *Deque) Insert(i int, elem interface{}) {
if i < 0 || i > q.count {
panic("deque: Insert() called with index out of range")
}
if i == 0 {
q.PushFront(elem)
return
}
if i == q.count {
q.PushBack(elem)
return
}
if i <= q.count/2 {
// If inserting closer to front, rotate front to back i places, put
// element at front, then rotate back to front i places.
for j := 0; j < i; j++ {
q.PushBack(q.PopFront())
}
q.PushFront(elem)
for j := 0; j < i; j++ {
q.PushFront(q.PopBack())
}
} else {
// If inserting closer to back, rotate back to front Len() - i places,
// put element at back, then rotate front to back same amount.
rots := q.count - i
for j := 0; j < rots; j++ {
q.PushFront(q.PopBack())
}
q.PushBack(elem)
for j := 0; j < rots; j++ {
q.PushBack(q.PopFront())
}
}
}
// Remove removes and returns an element from the middle of the queue.
// Remove(0) is the same as PopFront() and Remove(Len()-1) is the same as
// PopBack(). Accepts only non-negative index values, and panics if index is
// out of range.
func (q *Deque) Remove(i int) interface{} {
if i < 0 || i >= q.count {
panic("deque: Remove() called with index out of range")
}
if i == 0 {
return q.PopFront()
}
if i == q.count-1 {
return q.PopBack()
}
var elem interface{}
if i <= q.count/2 {
// If removing closer to front, rotate front to back i places, remove
// element at front, then rotate back to front i places.
for j := 0; j < i; j++ {
q.PushBack(q.PopFront())
}
elem = q.PopFront()
for j := 0; j < i; j++ {
q.PushFront(q.PopBack())
}
} else {
// If removing closer to back, rotate back to front Len() - 1 - i
// places, remove element at back, then rotate front to back same
// amount.
rots := (q.count - 1) - i
for j := 0; j < rots; j++ {
q.PushFront(q.PopBack())
}
elem = q.PopBack()
for j := 0; j < rots; j++ {
q.PushBack(q.PopFront())
}
}
return elem
}
// Replace replaces the element at position i with the given element. Accepts
// only non-negative index values, and panics if index is out of range
func (q *Deque) Replace(i int, elem interface{}) {
if i < 0 || i >= q.count {
panic("deque: Remove() called with index out of range")
}
// bitwise modulus
q.buf[(q.head+i)&(len(q.buf)-1)] = elem
}
// Clear removes all elements from the queue, but retains the current capacity.
// The queue will not be resized smaller as long as items are only added.
// Only when items are removed is the queue subject to getting resized smaller.

View file

@ -52,8 +52,8 @@ func TestGrowShrink(t *testing.T) {
}
// Check that all values are as expected.
for i := 0; i < minCapacity*2; i++ {
if q.PeekAt(i) != i {
t.Errorf("q.PeekAt(%d) = %d, expected %d", i, q.PeekAt(i), i)
if q.Get(i) != i {
t.Errorf("q.Get(%d) = %d, expected %d", i, q.Get(i), i)
}
}
bufLen := len(q.buf)
@ -102,8 +102,8 @@ func TestGrowShrink(t *testing.T) {
}
}
func TestDequeSimple(t *testing.T) {
q := new(Deque)
func TestSimple(t *testing.T) {
var q Deque
for i := 0; i < minCapacity; i++ {
q.PushBack(i)
@ -130,8 +130,8 @@ func TestDequeSimple(t *testing.T) {
}
}
func TestDequeWrap(t *testing.T) {
q := new(Deque)
func TestBufferWrap(t *testing.T) {
var q Deque
for i := 0; i < minCapacity; i++ {
q.PushBack(i)
@ -150,8 +150,8 @@ func TestDequeWrap(t *testing.T) {
}
}
func TestDequeWrapReverse(t *testing.T) {
q := new(Deque)
func TestBufferWrapReverse(t *testing.T) {
var q Deque
for i := 0; i < minCapacity; i++ {
q.PushFront(i)
@ -169,8 +169,8 @@ func TestDequeWrapReverse(t *testing.T) {
}
}
func TestDequeLen(t *testing.T) {
q := new(Deque)
func TestLen(t *testing.T) {
var q Deque
if q.Len() != 0 {
t.Error("empty queue length not 0")
@ -190,34 +190,30 @@ func TestDequeLen(t *testing.T) {
}
}
func TestDequePeekAt(t *testing.T) {
q := new(Deque)
func TestGet(t *testing.T) {
var q Deque
for i := 0; i < 1000; i++ {
q.PushBack(i)
for j := 0; j < q.Len(); j++ {
if q.PeekAt(j).(int) != j {
t.Errorf("index %d doesn't contain %d", j, j)
}
}
// Front to back.
for j := 0; j < q.Len(); j++ {
if q.Get(j).(int) != j {
t.Errorf("index %d doesn't contain %d", j, j)
}
}
// Back to front
for j := 1; j <= q.Len(); j++ {
if q.Get(q.Len()-j).(int) != q.Len()-j {
t.Errorf("index %d doesn't contain %d", q.Len()-j, q.Len()-j)
}
}
}
func TestDequePeekAtNegative(t *testing.T) {
q := new(Deque)
for i := 0; i < 1000; i++ {
q.PushBack(i)
for j := 1; j <= q.Len(); j++ {
if q.PeekAt(-j).(int) != q.Len()-j {
t.Errorf("index %d doesn't contain %d", -j, q.Len()-j)
}
}
}
}
func TestDequeBack(t *testing.T) {
q := new(Deque)
func TestBack(t *testing.T) {
var q Deque
for i := 0; i < minCapacity+5; i++ {
q.PushBack(i)
@ -228,7 +224,7 @@ func TestDequeBack(t *testing.T) {
}
func TestCopy(t *testing.T) {
q := new(Deque)
var q Deque
a := make([]interface{}, minCapacity)
if q.Copy(a) != 0 {
t.Error("Copied wrong size, expected 0")
@ -265,7 +261,7 @@ func TestCopy(t *testing.T) {
}
func TestRotate(t *testing.T) {
q := new(Deque)
var q Deque
for i := 0; i < 10; i++ {
q.PushBack(i)
}
@ -300,8 +296,8 @@ func TestRotate(t *testing.T) {
}
}
func TestDequeClear(t *testing.T) {
q := new(Deque)
func TestClear(t *testing.T) {
var q Deque
for i := 0; i < 100; i++ {
q.PushBack(i)
@ -327,25 +323,119 @@ func TestDequeClear(t *testing.T) {
}
}
func TestPeekAtOutOfRangePanics(t *testing.T) {
q := new(Deque)
func TestInsert(t *testing.T) {
var q Deque
q.PushBack("A")
q.PushBack("B")
q.PushBack("C")
q.PushBack("D")
q.PushBack("E")
q.PushBack("F")
q.PushBack("G")
q.Insert(4, "x")
if q.Get(4) != "x" {
t.Error("expected x at position 4")
}
q.Insert(2, "y")
if q.Get(2) != "y" {
t.Error("expected y at position 2")
}
if q.Get(5) != "x" {
t.Error("expected x at position 5")
}
q.Insert(0, "b")
if q.Front() != "b" {
t.Error("expected b inserted at front")
}
q.Insert(q.Len(), "e")
if q.Back() != "e" {
t.Error("expected e inserted at back")
}
}
func TestRemove(t *testing.T) {
var q Deque
q.PushBack("A")
q.PushBack("B")
q.PushBack("C")
q.PushBack("D")
q.PushBack("E")
q.PushBack("F")
q.PushBack("G")
if q.Remove(4) != "E" {
t.Error("expected E from position 4")
}
if q.Get(4) != "F" {
t.Error("expected F at position 4")
}
if q.Remove(2) != "C" {
t.Error("expected C at position 2")
}
if q.Get(2) != "D" {
t.Error("expected D at position 4")
}
if q.Get(4) != "G" {
t.Error("expected G at position 4")
}
if q.Remove(0) != "A" {
t.Error("expected to remove A from front")
}
if q.Remove(q.Len()-1) != "G" {
t.Error("expected to remove G from back")
}
}
func TestReplace(t *testing.T) {
var q Deque
q.PushBack("a")
q.PushBack("b")
q.PushBack("c")
q.Replace(0, "A")
if q.Front() != "A" {
t.Error("expected A at front")
}
q.Replace(q.Len()-1, "C")
if q.Back() != "C" {
t.Error("expected C at back")
}
q.Replace(1, "-")
if q.Get(1) != "-" {
t.Error("expected - at position 1")
}
}
func TestGetOutOfRangePanics(t *testing.T) {
var q Deque
q.PushBack(1)
q.PushBack(2)
q.PushBack(3)
assertPanics(t, "should panic when negative index", func() {
q.PeekAt(-4)
q.Get(-4)
})
assertPanics(t, "should panic when index greater than length", func() {
q.PeekAt(4)
q.Get(4)
})
}
func TestFrontBackOutOfRangePanics(t *testing.T) {
const msg = "should panic when peeking empty queue"
q := new(Deque)
var q Deque
assertPanics(t, msg, func() {
q.Front()
})
@ -365,7 +455,7 @@ func TestFrontBackOutOfRangePanics(t *testing.T) {
}
func TestPopFrontOutOfRangePanics(t *testing.T) {
q := new(Deque)
var q Deque
assertPanics(t, "should panic when removing empty queue", func() {
q.PopFront()
@ -380,7 +470,7 @@ func TestPopFrontOutOfRangePanics(t *testing.T) {
}
func TestPopBackOutOfRangePanics(t *testing.T) {
q := new(Deque)
var q Deque
assertPanics(t, "should panic when removing empty queue", func() {
q.PopBack()
@ -394,6 +484,60 @@ func TestPopBackOutOfRangePanics(t *testing.T) {
})
}
func TestInsertOutOfRangePanics(t *testing.T) {
var q Deque
assertPanics(t, "should panic when inserting out of range", func() {
q.Insert(1, "X")
})
q.PushBack("A")
assertPanics(t, "should panic when inserting at negative index", func() {
q.Insert(-1, "Y")
})
assertPanics(t, "should panic when inserting out of range", func() {
q.Insert(2, "B")
})
}
func TestRemoveOutOfRangePanics(t *testing.T) {
var q Deque
assertPanics(t, "should panic when removing from empty queue", func() {
q.Remove(0)
})
q.PushBack("A")
assertPanics(t, "should panic when removing at negative index", func() {
q.Remove(-1)
})
assertPanics(t, "should panic when removing out of range", func() {
q.Remove(1)
})
}
func TestReplaceOutOfRangePanics(t *testing.T) {
var q Deque
assertPanics(t, "should panic when replacing in empty queue", func() {
q.Replace(0, "x")
})
q.PushBack("A")
assertPanics(t, "should panic when replacing at negative index", func() {
q.Replace(-1, "Z")
})
assertPanics(t, "should panic when replacing out of range", func() {
q.Replace(1, "Y")
})
}
func assertPanics(t *testing.T, name string, f func()) {
defer func() {
if r := recover(); r == nil {
@ -456,7 +600,7 @@ func BenchmarkSerialReverse(b *testing.B) {
}
func BenchmarkRotate(b *testing.B) {
q := new(Deque)
var q Deque
for i := 0; i < size; i++ {
q.PushBack(i)
}
@ -470,7 +614,7 @@ func BenchmarkRotate(b *testing.B) {
}
func BenchmarkRotateReverse(b *testing.B) {
q := new(Deque)
var q Deque
for i := 0; i < size; i++ {
q.PushBack(i)
}
@ -483,21 +627,21 @@ func BenchmarkRotateReverse(b *testing.B) {
}
}
func BenchmarkDequePeekAt(b *testing.B) {
q := new(Deque)
func BenchmarkDequeGet(b *testing.B) {
var q Deque
for i := 0; i < size; i++ {
q.PushBack(i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < size; j++ {
q.PeekAt(j)
q.Get(j)
}
}
}
func BenchmarkDequePushPop(b *testing.B) {
q := new(Deque)
var q Deque
for i := 0; i < b.N; i++ {
for n := 0; n < size; n++ {
q.PushBack(nil)

View file

@ -4,7 +4,7 @@ set -e
echo "" > coverage.txt
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic $d
go test -coverprofile=profile.out -covermode=atomic $d
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out