pax_global_header00006660000000000000000000000064145442612500014515gustar00rootroot0000000000000052 comment=381e2afe978032e26c65e067987437b4f227b990 golang-github-gammazero-deque-0.2.1/000077500000000000000000000000001454426125000173255ustar00rootroot00000000000000golang-github-gammazero-deque-0.2.1/.github/000077500000000000000000000000001454426125000206655ustar00rootroot00000000000000golang-github-gammazero-deque-0.2.1/.github/workflows/000077500000000000000000000000001454426125000227225ustar00rootroot00000000000000golang-github-gammazero-deque-0.2.1/.github/workflows/go.yml000066400000000000000000000010651454426125000240540ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.18 - name: Vet run: go vet ./... - name: Build run: go build -v ./... - name: Test run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: files: coverage.txt golang-github-gammazero-deque-0.2.1/.github/workflows/main.yml000066400000000000000000000004471454426125000243760ustar00rootroot00000000000000name: golangci-lint on: push: tags: - v* branches: - master - main pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 golang-github-gammazero-deque-0.2.1/.gitignore000066400000000000000000000004161454426125000213160ustar00rootroot00000000000000*~ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof golang-github-gammazero-deque-0.2.1/LICENSE000066400000000000000000000020611454426125000203310ustar00rootroot00000000000000MIT License Copyright (c) 2018 Andrew J. Gillis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-gammazero-deque-0.2.1/README.md000066400000000000000000000074651454426125000206200ustar00rootroot00000000000000# deque [![GoDoc](https://pkg.go.dev/badge/github.com/gammazero/deque)](https://pkg.go.dev/github.com/gammazero/deque) [![Build Status](https://github.com/gammazero/deque/actions/workflows/go.yml/badge.svg)](https://github.com/gammazero/deque/actions/workflows/go.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/gammazero/deque)](https://goreportcard.com/report/github.com/gammazero/deque) [![codecov](https://codecov.io/gh/gammazero/deque/branch/master/graph/badge.svg)](https://codecov.io/gh/gammazero/deque) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) Fast ring-buffer deque ([double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue)) implementation. For a pictorial description, see the [Deque diagram](https://github.com/gammazero/deque/wiki) ## Installation ``` $ go get github.com/gammazero/deque ``` ## Deque data structure Deque generalizes a queue and a stack, to efficiently add and remove items at either end with O(1) performance. [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) (FIFO) operations are supported using `PushBack` and `PopFront`. [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) (LIFO) operations are supported using `PushBack` and `PopBack`. ## Ring-buffer Performance This deque implementation is optimized for CPU and GC performance. The circular buffer automatically re-sizes by powers of two, growing when additional capacity is needed and shrinking when only a quarter of the capacity is used, and uses bitwise arithmetic for all calculations. Since growth is by powers of two, adding elements will only cause O(log n) allocations. A minimum capacity can be set so that there is no resizing at or below that specified amount. The ring-buffer implementation improves memory and time performance with fewer GC pauses, compared to implementations based on slices and linked lists. By wrapping around the buffer, previously used space is reused, making allocation unnecessary until all buffer capacity is used. If the deque is only filled and then completely emptied before being filled again, then the ring structure offers little benefit for memory reuse over a slice. For maximum speed, this deque implementation leaves concurrency safety up to the application to provide, however the application chooses, if needed at all. ## Reading Empty Deque Since it is OK for the deque to contain a `nil` value, it is necessary to either panic or return a second boolean value to indicate the deque is empty, when reading or removing an element. This deque panics when reading from an empty deque. This is a run-time check to help catch programming errors, which may be missed if a second return value is ignored. Simply check `Deque.Len()` before reading from the deque. ## Generics Deque uses generics to create a Deque that contains items of the type specified. To create a Deque that holds a specific type, provide a type argument to New or with the variable declaration. For example: ```go stringDeque := deque.New[string]() var intDeque deque.Deque[int] ``` ## Example ```go package main import ( "fmt" "github.com/gammazero/deque" ) func main() { var q deque.Deque[string] q.PushBack("foo") q.PushBack("bar") q.PushBack("baz") fmt.Println(q.Len()) // Prints: 3 fmt.Println(q.Front()) // Prints: foo fmt.Println(q.Back()) // Prints: baz q.PopFront() // remove "foo" q.PopBack() // remove "baz" q.PushFront("hello") q.PushBack("world") // Consume deque and print elements. for q.Len() != 0 { fmt.Println(q.PopFront()) } } ``` ## Uses Deque can be used as both a: - [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) using `PushBack` and `PopFront` - [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) using `PushBack` and `PopBack` golang-github-gammazero-deque-0.2.1/deque.go000066400000000000000000000267011454426125000207650ustar00rootroot00000000000000package deque import "fmt" // minCapacity is the smallest capacity that deque may have. Must be power of 2 // for bitwise modulus: x % n == x & (n - 1). const minCapacity = 16 // Deque represents a single instance of the deque data structure. A Deque // instance contains items of the type specified by the type argument. type Deque[T any] struct { buf []T head int tail int count int minCap int } // New creates a new Deque, optionally setting the current and minimum capacity // when non-zero values are given for these. The Deque instance returns // operates on items of the type specified by the type argument. For example, // to create a Deque that contains strings, // // stringDeque := deque.New[string]() // // To create a Deque with capacity to store 2048 ints without resizing, and // that will not resize below space for 32 items when removing items: // // d := deque.New[int](2048, 32) // // To create a Deque that has not yet allocated memory, but after it does will // never resize to have space for less than 64 items: // // d := deque.New[int](0, 64) // // Any size values supplied here are rounded up to the nearest power of 2. func New[T any](size ...int) *Deque[T] { var capacity, minimum int if len(size) >= 1 { capacity = size[0] if len(size) >= 2 { minimum = size[1] } } minCap := minCapacity for minCap < minimum { minCap <<= 1 } var buf []T if capacity != 0 { bufSize := minCap for bufSize < capacity { bufSize <<= 1 } buf = make([]T, bufSize) } return &Deque[T]{ buf: buf, minCap: minCap, } } // Cap returns the current capacity of the Deque. If q is nil, q.Cap() is zero. func (q *Deque[T]) Cap() int { if q == nil { return 0 } return len(q.buf) } // Len returns the number of elements currently stored in the queue. If q is // nil, q.Len() is zero. func (q *Deque[T]) Len() int { if q == nil { return 0 } return q.count } // PushBack appends an element to the back of the queue. Implements FIFO when // elements are removed with PopFront, and LIFO when elements are removed with // PopBack. func (q *Deque[T]) PushBack(elem T) { q.growIfFull() q.buf[q.tail] = elem // Calculate new tail position. q.tail = q.next(q.tail) q.count++ } // PushFront prepends an element to the front of the queue. func (q *Deque[T]) PushFront(elem T) { q.growIfFull() // Calculate new head position. q.head = q.prev(q.head) q.buf[q.head] = elem q.count++ } // PopFront removes and returns the element from the front of the queue. // Implements FIFO when used with PushBack. If the queue is empty, the call // panics. func (q *Deque[T]) PopFront() T { if q.count <= 0 { panic("deque: PopFront() called on empty queue") } ret := q.buf[q.head] var zero T q.buf[q.head] = zero // Calculate new head position. q.head = q.next(q.head) q.count-- q.shrinkIfExcess() return ret } // PopBack removes and returns the element from the back of the queue. // Implements LIFO when used with PushBack. If the queue is empty, the call // panics. func (q *Deque[T]) PopBack() T { if q.count <= 0 { panic("deque: PopBack() called on empty queue") } // Calculate new tail position q.tail = q.prev(q.tail) // Remove value at tail. ret := q.buf[q.tail] var zero T q.buf[q.tail] = zero q.count-- q.shrinkIfExcess() return ret } // Front returns the element at the front of the queue. This is the element // that would be returned by PopFront. This call panics if the queue is empty. func (q *Deque[T]) Front() T { if q.count <= 0 { panic("deque: Front() called when empty") } return q.buf[q.head] } // Back returns the element at the back of the queue. This is the element that // would be returned by PopBack. This call panics if the queue is empty. func (q *Deque[T]) Back() T { if q.count <= 0 { panic("deque: Back() called when empty") } return q.buf[q.prev(q.tail)] } // At returns the element at index i in the queue without removing the element // from the queue. This method accepts only non-negative index values. At(0) // refers to the first element and is the same as Front(). At(Len()-1) refers // to the last element and is the same as Back(). If the index is invalid, the // call panics. // // The purpose of At is to allow Deque to serve as a more general purpose // circular buffer, where items are only added to and removed from the ends of // the deque, but may be read from any place within the deque. Consider the // case of a fixed-size circular log buffer: A new entry is pushed onto one end // and when full the oldest is popped from the other end. All the log entries // in the buffer must be readable without altering the buffer contents. func (q *Deque[T]) At(i int) T { if i < 0 || i >= q.count { panic(outOfRangeText(i, q.Len())) } // bitwise modulus return q.buf[(q.head+i)&(len(q.buf)-1)] } // Set assigns the item to index i in the queue. Set indexes the deque the same // as At but perform the opposite operation. If the index is invalid, the call // panics. func (q *Deque[T]) Set(i int, item T) { if i < 0 || i >= q.count { panic(outOfRangeText(i, q.Len())) } // bitwise modulus q.buf[(q.head+i)&(len(q.buf)-1)] = item } // Clear removes all elements from the queue, but retains the current capacity. // This is useful when repeatedly reusing the queue at high frequency to avoid // GC during reuse. 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. func (q *Deque[T]) Clear() { var zero T modBits := len(q.buf) - 1 h := q.head for i := 0; i < q.Len(); i++ { q.buf[(h+i)&modBits] = zero } q.head = 0 q.tail = 0 q.count = 0 } // Rotate rotates the deque n steps front-to-back. If n is negative, rotates // back-to-front. Having Deque provide Rotate avoids resizing that could happen // if implementing rotation using only Pop and Push methods. If q.Len() is one // or less, or q is nil, then Rotate does nothing. func (q *Deque[T]) Rotate(n int) { if q.Len() <= 1 { return } // Rotating a multiple of q.count is same as no rotation. n %= q.count if n == 0 { return } modBits := len(q.buf) - 1 // If no empty space in buffer, only move head and tail indexes. if q.head == q.tail { // Calculate new head and tail using bitwise modulus. q.head = (q.head + n) & modBits q.tail = q.head return } var zero T if n < 0 { // Rotate back to front. for ; n < 0; n++ { // Calculate new head and tail using bitwise modulus. q.head = (q.head - 1) & modBits q.tail = (q.tail - 1) & modBits // Put tail value at head and remove value at tail. q.buf[q.head] = q.buf[q.tail] q.buf[q.tail] = zero } return } // Rotate front to back. for ; n > 0; n-- { // Put head value at tail and remove value at head. q.buf[q.tail] = q.buf[q.head] q.buf[q.head] = zero // Calculate new head and tail using bitwise modulus. q.head = (q.head + 1) & modBits q.tail = (q.tail + 1) & modBits } } // Index returns the index into the Deque of the first item satisfying f(item), // or -1 if none do. If q is nil, then -1 is always returned. Search is linear // starting with index 0. func (q *Deque[T]) Index(f func(T) bool) int { if q.Len() > 0 { modBits := len(q.buf) - 1 for i := 0; i < q.count; i++ { if f(q.buf[(q.head+i)&modBits]) { return i } } } return -1 } // RIndex is the same as Index, but searches from Back to Front. The index // returned is from Front to Back, where index 0 is the index of the item // returned by Front(). func (q *Deque[T]) RIndex(f func(T) bool) int { if q.Len() > 0 { modBits := len(q.buf) - 1 for i := q.count - 1; i >= 0; i-- { if f(q.buf[(q.head+i)&modBits]) { return i } } } return -1 } // Insert is used to insert an element into the middle of the queue, before the // element at the specified index. 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. // // Important: Deque is optimized for O(1) operations at the ends of the queue, // not for operations in the the middle. Complexity of this function is // constant plus linear in the lesser of the distances between the index and // either of the ends of the queue. func (q *Deque[T]) Insert(at int, item T) { if at < 0 || at > q.count { panic(outOfRangeText(at, q.Len())) } if at*2 < q.count { q.PushFront(item) front := q.head for i := 0; i < at; i++ { next := q.next(front) q.buf[front], q.buf[next] = q.buf[next], q.buf[front] front = next } return } swaps := q.count - at q.PushBack(item) back := q.prev(q.tail) for i := 0; i < swaps; i++ { prev := q.prev(back) q.buf[back], q.buf[prev] = q.buf[prev], q.buf[back] back = prev } } // Remove removes and returns an element from the middle of the queue, at the // specified index. 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. // // Important: Deque is optimized for O(1) operations at the ends of the queue, // not for operations in the the middle. Complexity of this function is // constant plus linear in the lesser of the distances between the index and // either of the ends of the queue. func (q *Deque[T]) Remove(at int) T { if at < 0 || at >= q.Len() { panic(outOfRangeText(at, q.Len())) } rm := (q.head + at) & (len(q.buf) - 1) if at*2 < q.count { for i := 0; i < at; i++ { prev := q.prev(rm) q.buf[prev], q.buf[rm] = q.buf[rm], q.buf[prev] rm = prev } return q.PopFront() } swaps := q.count - at - 1 for i := 0; i < swaps; i++ { next := q.next(rm) q.buf[rm], q.buf[next] = q.buf[next], q.buf[rm] rm = next } return q.PopBack() } // SetMinCapacity sets a minimum capacity of 2^minCapacityExp. If the value of // the minimum capacity is less than or equal to the minimum allowed, then // capacity is set to the minimum allowed. This may be called at anytime to set // a new minimum capacity. // // Setting a larger minimum capacity may be used to prevent resizing when the // number of stored items changes frequently across a wide range. func (q *Deque[T]) SetMinCapacity(minCapacityExp uint) { if 1< minCapacity { q.minCap = 1 << minCapacityExp } else { q.minCap = minCapacity } } // prev returns the previous buffer position wrapping around buffer. func (q *Deque[T]) prev(i int) int { return (i - 1) & (len(q.buf) - 1) // bitwise modulus } // next returns the next buffer position wrapping around buffer. func (q *Deque[T]) next(i int) int { return (i + 1) & (len(q.buf) - 1) // bitwise modulus } // growIfFull resizes up if the buffer is full. func (q *Deque[T]) growIfFull() { if q.count != len(q.buf) { return } if len(q.buf) == 0 { if q.minCap == 0 { q.minCap = minCapacity } q.buf = make([]T, q.minCap) return } q.resize() } // shrinkIfExcess resize down if the buffer 1/4 full. func (q *Deque[T]) shrinkIfExcess() { if len(q.buf) > q.minCap && (q.count<<2) == len(q.buf) { q.resize() } } // resize resizes the deque to fit exactly twice its current contents. This is // used to grow the queue when it is full, and also to shrink it when it is // only a quarter full. func (q *Deque[T]) resize() { newBuf := make([]T, q.count<<1) if q.tail > q.head { copy(newBuf, q.buf[q.head:q.tail]) } else { n := copy(newBuf, q.buf[q.head:]) copy(newBuf[n:], q.buf[:q.tail]) } q.head = 0 q.tail = q.count q.buf = newBuf } func outOfRangeText(i, len int) string { return fmt.Sprintf("deque: index out of range %d with length %d", i, len) } golang-github-gammazero-deque-0.2.1/deque_test.go000066400000000000000000000401421454426125000220170ustar00rootroot00000000000000package deque import ( "fmt" "testing" "unicode" ) func TestEmpty(t *testing.T) { q := New[string]() if q.Len() != 0 { t.Error("q.Len() =", q.Len(), "expect 0") } if q.Cap() != 0 { t.Error("expected q.Cap() == 0") } idx := q.Index(func(item string) bool { return true }) if idx != -1 { t.Error("should return -1 index for nil deque") } idx = q.RIndex(func(item string) bool { return true }) if idx != -1 { t.Error("should return -1 index for nil deque") } } func TestNil(t *testing.T) { var q *Deque[int] if q.Len() != 0 { t.Error("expected q.Len() == 0") } if q.Cap() != 0 { t.Error("expected q.Cap() == 0") } q.Rotate(5) idx := q.Index(func(item int) bool { return true }) if idx != -1 { t.Error("should return -1 index for nil deque") } idx = q.RIndex(func(item int) bool { return true }) if idx != -1 { t.Error("should return -1 index for nil deque") } } func TestFrontBack(t *testing.T) { var q Deque[string] q.PushBack("foo") q.PushBack("bar") q.PushBack("baz") if q.Front() != "foo" { t.Error("wrong value at front of queue") } if q.Back() != "baz" { t.Error("wrong value at back of queue") } if q.PopFront() != "foo" { t.Error("wrong value removed from front of queue") } if q.Front() != "bar" { t.Error("wrong value remaining at front of queue") } if q.Back() != "baz" { t.Error("wrong value remaining at back of queue") } if q.PopBack() != "baz" { t.Error("wrong value removed from back of queue") } if q.Front() != "bar" { t.Error("wrong value remaining at front of queue") } if q.Back() != "bar" { t.Error("wrong value remaining at back of queue") } } func TestGrowShrinkBack(t *testing.T) { var q Deque[int] size := minCapacity * 2 for i := 0; i < size; i++ { if q.Len() != i { t.Error("q.Len() =", q.Len(), "expected", i) } q.PushBack(i) } bufLen := len(q.buf) // Remove from back. for i := size; i > 0; i-- { if q.Len() != i { t.Error("q.Len() =", q.Len(), "expected", i) } x := q.PopBack() if x != i-1 { t.Error("q.PopBack() =", x, "expected", i-1) } } if q.Len() != 0 { t.Error("q.Len() =", q.Len(), "expected 0") } if len(q.buf) == bufLen { t.Error("queue buffer did not shrink") } } func TestGrowShrinkFront(t *testing.T) { var q Deque[int] size := minCapacity * 2 for i := 0; i < size; i++ { if q.Len() != i { t.Error("q.Len() =", q.Len(), "expected", i) } q.PushBack(i) } bufLen := len(q.buf) // Remove from Front for i := 0; i < size; i++ { if q.Len() != size-i { t.Error("q.Len() =", q.Len(), "expected", minCapacity*2-i) } x := q.PopFront() if x != i { t.Error("q.PopBack() =", x, "expected", i) } } if q.Len() != 0 { t.Error("q.Len() =", q.Len(), "expected 0") } if len(q.buf) == bufLen { t.Error("queue buffer did not shrink") } } func TestSimple(t *testing.T) { var q Deque[int] for i := 0; i < minCapacity; i++ { q.PushBack(i) } if q.Front() != 0 { t.Fatalf("expected 0 at front, got %d", q.Front()) } if q.Back() != minCapacity-1 { t.Fatalf("expected %d at back, got %d", minCapacity-1, q.Back()) } for i := 0; i < minCapacity; i++ { if q.Front() != i { t.Error("peek", i, "had value", q.Front()) } x := q.PopFront() if x != i { t.Error("remove", i, "had value", x) } } q.Clear() for i := 0; i < minCapacity; i++ { q.PushFront(i) } for i := minCapacity - 1; i >= 0; i-- { x := q.PopFront() if x != i { t.Error("remove", i, "had value", x) } } } func TestBufferWrap(t *testing.T) { var q Deque[int] for i := 0; i < minCapacity; i++ { q.PushBack(i) } for i := 0; i < 3; i++ { q.PopFront() q.PushBack(minCapacity + i) } for i := 0; i < minCapacity; i++ { if q.Front() != i+3 { t.Error("peek", i, "had value", q.Front()) } q.PopFront() } } func TestBufferWrapReverse(t *testing.T) { var q Deque[int] for i := 0; i < minCapacity; i++ { q.PushFront(i) } for i := 0; i < 3; i++ { q.PopBack() q.PushFront(minCapacity + i) } for i := 0; i < minCapacity; i++ { if q.Back() != i+3 { t.Error("peek", i, "had value", q.Front()) } q.PopBack() } } func TestLen(t *testing.T) { var q Deque[int] if q.Len() != 0 { t.Error("empty queue length not 0") } for i := 0; i < 1000; i++ { q.PushBack(i) if q.Len() != i+1 { t.Error("adding: queue with", i, "elements has length", q.Len()) } } for i := 0; i < 1000; i++ { q.PopFront() if q.Len() != 1000-i-1 { t.Error("removing: queue with", 1000-i-i, "elements has length", q.Len()) } } } func TestBack(t *testing.T) { var q Deque[int] for i := 0; i < minCapacity+5; i++ { q.PushBack(i) if q.Back() != i { t.Errorf("Back returned wrong value") } } } func TestNew(t *testing.T) { minCap := 64 q := New[string](0, minCap) if q.Cap() != 0 { t.Fatal("should not have allowcated mem yet") } q.PushBack("foo") q.PopFront() if q.Len() != 0 { t.Fatal("Len() should return 0") } if q.Cap() != minCap { t.Fatalf("worng capactiy expected %d, got %d", minCap, q.Cap()) } curCap := 128 q = New[string](curCap, minCap) if q.Cap() != curCap { t.Fatalf("Cap() should return %d, got %d", curCap, q.Cap()) } if q.Len() != 0 { t.Fatalf("Len() should return 0") } q.PushBack("foo") if q.Cap() != curCap { t.Fatalf("Cap() should return %d, got %d", curCap, q.Cap()) } } func checkRotate(t *testing.T, size int) { var q Deque[int] for i := 0; i < size; i++ { q.PushBack(i) } for i := 0; i < q.Len(); i++ { x := i for n := 0; n < q.Len(); n++ { if q.At(n) != x { t.Fatalf("a[%d] != %d after rotate and copy", n, x) } x++ if x == q.Len() { x = 0 } } q.Rotate(1) if q.Back() != i { t.Fatal("wrong value during rotation") } } for i := q.Len() - 1; i >= 0; i-- { q.Rotate(-1) if q.Front() != i { t.Fatal("wrong value during reverse rotation") } } } func TestRotate(t *testing.T) { checkRotate(t, 10) checkRotate(t, minCapacity) checkRotate(t, minCapacity+minCapacity/2) var q Deque[int] for i := 0; i < 10; i++ { q.PushBack(i) } q.Rotate(11) if q.Front() != 1 { t.Error("rotating 11 places should have been same as one") } q.Rotate(-21) if q.Front() != 0 { t.Error("rotating -21 places should have been same as one -1") } q.Rotate(q.Len()) if q.Front() != 0 { t.Error("should not have rotated") } q.Clear() q.PushBack(0) q.Rotate(13) if q.Front() != 0 { t.Error("should not have rotated") } } func TestAt(t *testing.T) { var q Deque[int] for i := 0; i < 1000; i++ { q.PushBack(i) } // Front to back. for j := 0; j < q.Len(); j++ { if q.At(j) != j { t.Errorf("index %d doesn't contain %d", j, j) } } // Back to front for j := 1; j <= q.Len(); j++ { if q.At(q.Len()-j) != q.Len()-j { t.Errorf("index %d doesn't contain %d", q.Len()-j, q.Len()-j) } } } func TestSet(t *testing.T) { var q Deque[int] for i := 0; i < 1000; i++ { q.PushBack(i) q.Set(i, i+50) } // Front to back. for j := 0; j < q.Len(); j++ { if q.At(j) != j+50 { t.Errorf("index %d doesn't contain %d", j, j+50) } } } func TestClear(t *testing.T) { var q Deque[int] for i := 0; i < 100; i++ { q.PushBack(i) } if q.Len() != 100 { t.Error("push: queue with 100 elements has length", q.Len()) } cap := len(q.buf) q.Clear() if q.Len() != 0 { t.Error("empty queue length not 0 after clear") } if len(q.buf) != cap { t.Error("queue capacity changed after clear") } // Check that there are no remaining references after Clear() for i := 0; i < len(q.buf); i++ { if q.buf[i] != 0 { t.Error("queue has non-nil deleted elements after Clear()") break } } for i := 0; i < 128; i++ { q.PushBack(i) } q.Clear() // Check that there are no remaining references after Clear() for i := 0; i < len(q.buf); i++ { if q.buf[i] != 0 { t.Error("queue has non-nil deleted elements after Clear()") break } } } func TestIndex(t *testing.T) { var q Deque[rune] for _, x := range "Hello, 世界" { q.PushBack(x) } idx := q.Index(func(item rune) bool { c := item return unicode.Is(unicode.Han, c) }) if idx != 7 { t.Fatal("Expected index 7, got", idx) } idx = q.Index(func(item rune) bool { c := item return c == 'H' }) if idx != 0 { t.Fatal("Expected index 0, got", idx) } idx = q.Index(func(item rune) bool { return false }) if idx != -1 { t.Fatal("Expected index -1, got", idx) } } func TestRIndex(t *testing.T) { var q Deque[rune] for _, x := range "Hello, 世界" { q.PushBack(x) } idx := q.RIndex(func(item rune) bool { c := item return unicode.Is(unicode.Han, c) }) if idx != 8 { t.Fatal("Expected index 8, got", idx) } idx = q.RIndex(func(item rune) bool { c := item return c == 'H' }) if idx != 0 { t.Fatal("Expected index 0, got", idx) } idx = q.RIndex(func(item rune) bool { return false }) if idx != -1 { t.Fatal("Expected index -1, got", idx) } } func TestInsert(t *testing.T) { q := new(Deque[rune]) for _, x := range "ABCDEFG" { q.PushBack(x) } q.Insert(4, 'x') // ABCDxEFG if q.At(4) != 'x' { t.Error("expected x at position 4, got", q.At(4)) } q.Insert(2, 'y') // AByCDxEFG if q.At(2) != 'y' { t.Error("expected y at position 2") } if q.At(5) != 'x' { t.Error("expected x at position 5") } q.Insert(0, 'b') // bAByCDxEFG if q.Front() != 'b' { t.Error("expected b inserted at front, got", q.Front()) } q.Insert(q.Len(), 'e') // bAByCDxEFGe for i, x := range "bAByCDxEFGe" { if q.PopFront() != x { t.Error("expected", x, "at position", i) } } qs := New[string](16) for i := 0; i < qs.Cap(); i++ { qs.PushBack(fmt.Sprint(i)) } // deque: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // buffer: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] for i := 0; i < qs.Cap()/2; i++ { qs.PopFront() } // deque: 8 9 10 11 12 13 14 15 // buffer: [_,_,_,_,_,_,_,_,8,9,10,11,12,13,14,15] for i := 0; i < qs.Cap()/4; i++ { qs.PushBack(fmt.Sprint(qs.Cap() + i)) } // deque: 8 9 10 11 12 13 14 15 16 17 18 19 // buffer: [16,17,18,19,_,_,_,_,8,9,10,11,12,13,14,15] at := qs.Len() - 2 qs.Insert(at, "x") // deque: 8 9 10 11 12 13 14 15 16 17 x 18 19 // buffer: [16,17,x,18,19,_,_,_,8,9,10,11,12,13,14,15] if qs.At(at) != "x" { t.Error("expected x at position", at) } if qs.At(at) != "x" { t.Error("expected x at position", at) } qs.Insert(2, "y") // deque: 8 9 y 10 11 12 13 14 15 16 17 x 18 19 // buffer: [16,17,x,18,19,_,_,8,9,y,10,11,12,13,14,15] if qs.At(2) != "y" { t.Error("expected y at position 2") } if qs.At(at+1) != "x" { t.Error("expected x at position 5") } qs.Insert(0, "b") // deque: b 8 9 y 10 11 12 13 14 15 16 17 x 18 19 // buffer: [16,17,x,18,19,_,b,8,9,y,10,11,12,13,14,15] if qs.Front() != "b" { t.Error("expected b inserted at front, got", qs.Front()) } qs.Insert(qs.Len(), "e") if qs.Cap() != qs.Len() { t.Fatal("Expected full buffer") } // deque: b 8 9 y 10 11 12 13 14 15 16 17 x 18 19 e // buffer: [16,17,x,18,19,e,b,8,9,y,10,11,12,13,14,15] for i, x := range []string{"16", "17", "x", "18", "19", "e", "b", "8", "9", "y", "10", "11", "12", "13", "14", "15"} { if qs.buf[i] != x { t.Error("expected", x, "at buffer position", i) } } for i, x := range []string{"b", "8", "9", "y", "10", "11", "12", "13", "14", "15", "16", "17", "x", "18", "19", "e"} { if qs.Front() != x { t.Error("expected", x, "at position", i, "got", qs.Front()) } qs.PopFront() } } func TestRemove(t *testing.T) { q := new(Deque[rune]) for _, x := range "ABCDEFG" { q.PushBack(x) } if q.Remove(4) != 'E' { // ABCDFG t.Error("expected E from position 4") } if q.Remove(2) != 'C' { // ABDFG t.Error("expected C at position 2") } if q.Back() != 'G' { t.Error("expected G at back") } if q.Remove(0) != 'A' { // BDFG t.Error("expected to remove A from front") } if q.Front() != 'B' { t.Error("expected G at back") } if q.Remove(q.Len()-1) != 'G' { // BDF t.Error("expected to remove G from back") } if q.Back() != 'F' { t.Error("expected F at back") } if q.Len() != 3 { t.Error("wrong length") } } func TestFrontBackOutOfRangePanics(t *testing.T) { const msg = "should panic when peeking empty queue" var q Deque[int] assertPanics(t, msg, func() { q.Front() }) assertPanics(t, msg, func() { q.Back() }) q.PushBack(1) q.PopFront() assertPanics(t, msg, func() { q.Front() }) assertPanics(t, msg, func() { q.Back() }) } func TestPopFrontOutOfRangePanics(t *testing.T) { var q Deque[int] assertPanics(t, "should panic when removing empty queue", func() { q.PopFront() }) q.PushBack(1) q.PopFront() assertPanics(t, "should panic when removing emptied queue", func() { q.PopFront() }) } func TestPopBackOutOfRangePanics(t *testing.T) { var q Deque[int] assertPanics(t, "should panic when removing empty queue", func() { q.PopBack() }) q.PushBack(1) q.PopBack() assertPanics(t, "should panic when removing emptied queue", func() { q.PopBack() }) } func TestAtOutOfRangePanics(t *testing.T) { var q Deque[int] q.PushBack(1) q.PushBack(2) q.PushBack(3) assertPanics(t, "should panic when negative index", func() { q.At(-4) }) assertPanics(t, "should panic when index greater than length", func() { q.At(4) }) } func TestSetOutOfRangePanics(t *testing.T) { var q Deque[int] q.PushBack(1) q.PushBack(2) q.PushBack(3) assertPanics(t, "should panic when negative index", func() { q.Set(-4, 1) }) assertPanics(t, "should panic when index greater than length", func() { q.Set(4, 1) }) } func TestInsertOutOfRangePanics(t *testing.T) { q := new(Deque[string]) 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) { q := new(Deque[string]) 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 TestSetMinCapacity(t *testing.T) { var q Deque[string] exp := uint(8) q.SetMinCapacity(exp) q.PushBack("A") if q.minCap != 1<