Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generic queue #1

Merged
merged 2 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ linters:
- gosimple # Code simplification
- govet # Official Go tool
- ineffassign # Detects when assignments to existing variables are not used
- interfacer # Top-level interface suggestions
- nakedret # Finds naked/bare returns and requires change them
- nilerr # Requires explicit returns
- nilnil # Requires explicit returns
Expand Down
14 changes: 7 additions & 7 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func benchmarkPush(b *testing.B, items []*mockItem) {
b.Helper()

for i := 0; i < b.N; i++ {
q := NewQueue()
q := NewQueue[*mockItem]()
for _, item := range items {
q.Push(item)
}
Expand All @@ -32,12 +32,12 @@ func BenchmarkHeap_Push100(b *testing.B) {
func benchmarkPop(
b *testing.B,
items []*mockItem,
popCallback func(q *Queue),
popCallback func(q *Queue[*mockItem]),
) {
b.Helper()

for i := 0; i < b.N; i++ {
q := NewQueue()
q := NewQueue[*mockItem]()

b.StopTimer()

Expand All @@ -57,7 +57,7 @@ func BenchmarkHeap_PopFront10(b *testing.B) {
items := generateRandomItems(10)

b.ResetTimer()
benchmarkPop(b, items, func(q *Queue) {
benchmarkPop(b, items, func(q *Queue[*mockItem]) {
q.PopFront()
})
}
Expand All @@ -66,7 +66,7 @@ func BenchmarkHeap_PopFront100(b *testing.B) {
items := generateRandomItems(100)

b.ResetTimer()
benchmarkPop(b, items, func(q *Queue) {
benchmarkPop(b, items, func(q *Queue[*mockItem]) {
q.PopFront()
})
}
Expand All @@ -75,7 +75,7 @@ func BenchmarkHeap_PopBack10(b *testing.B) {
items := generateRandomItems(10)

b.ResetTimer()
benchmarkPop(b, items, func(q *Queue) {
benchmarkPop(b, items, func(q *Queue[*mockItem]) {
q.PopBack()
})
}
Expand All @@ -84,7 +84,7 @@ func BenchmarkHeap_PopBack100(b *testing.B) {
items := generateRandomItems(100)

b.ResetTimer()
benchmarkPop(b, items, func(q *Queue) {
benchmarkPop(b, items, func(q *Queue[*mockItem]) {
q.PopBack()
})
}
20 changes: 10 additions & 10 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,47 @@ type number struct {
value int
}

func (i number) Less(other Item) bool {
return i.value < other.(number).value
func (i number) Less(other number) bool {
return i.value < other.value
}

func ExampleQueue_Push() {
q := NewQueue()
q := NewQueue[number]()

q.Push(number{1}) // 1
q.Push(number{2}) // 2
q.Push(number{3}) // 3

fmt.Println(q[0].(number).value)
fmt.Println(q[0].value)
// Output: 1
}

func ExampleQueue_PopFront() {
q := NewQueue()
q := NewQueue[number]()

q.Push(number{1}) // 1
q.Push(number{2}) // 2
q.Push(number{3}) // 3

popped := q.PopFront()
fmt.Println(popped.(number).value)
fmt.Println(popped.value)
// Output: 1
}

func ExampleQueue_PopBack() {
q := NewQueue()
q := NewQueue[number]()

q.Push(number{1}) // 1
q.Push(number{2}) // 2
q.Push(number{3}) // 3

popped := q.PopBack()
fmt.Println(popped.(number).value)
fmt.Println(popped.value)
// Output: 3
}

func ExampleQueue_Fix() {
q := NewQueue()
q := NewQueue[number]()

q.Push(number{1}) // 1
q.Push(number{2}) // 2
Expand All @@ -55,6 +55,6 @@ func ExampleQueue_Fix() {
q[0] = number{4} // 1 -> 4
q.Fix()

fmt.Println(q[0].(number).value)
fmt.Println(q[0].value)
// Output: 2
}
11 changes: 3 additions & 8 deletions mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package queue

import "fmt"

type lessDelegate func(Item) bool
type lessDelegate func(item *mockItem) bool

// mockItem is a mockable Item
type mockItem struct {
Expand All @@ -11,17 +11,12 @@ type mockItem struct {
value int
}

func (m *mockItem) Less(i Item) bool {
func (m *mockItem) Less(i *mockItem) bool {
if m.lessFn != nil {
return m.lessFn(i)
}

other, ok := i.(*mockItem)
if !ok {
return false
}

return m.value < other.value
return m.value <= i.value
}

func (m *mockItem) String() string {
Expand Down
22 changes: 11 additions & 11 deletions queue.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
package queue

// Queue is the priority queue based on insertion sort
type Queue []Item
type Queue[T Item[T]] []T

// NewQueue creates an instance of the priority queue
func NewQueue() Queue {
return make(Queue, 0)
func NewQueue[T Item[T]]() Queue[T] {
return make(Queue[T], 0)
}

// Len returns the length of the queue
func (q *Queue) Len() int {
func (q *Queue[T]) Len() int {
return len(*q)
}

// Index returns the element at the specified index, if any.
// NOTE: panics if out of bounds
func (q *Queue) Index(index int) Item {
func (q *Queue[T]) Index(index int) T {
return (*q)[index]
}

// Push adds a new element to the priority queue
func (q *Queue) Push(item Item) {
func (q *Queue[T]) Push(item T) {
*q = append(*q, item)
for i := len(*q) - 1; i > 0; i-- {
if (*q)[i].Less((*q)[i-1]) {
Expand All @@ -33,7 +33,7 @@ func (q *Queue) Push(item Item) {
}

// Fix makes sure the priority queue is properly sorted
func (q *Queue) Fix() {
func (q *Queue[T]) Fix() {
for i := 1; i < len(*q); i++ {
for j := i - 1; j >= 0; j-- {
if (*q)[j].Less((*q)[j+1]) {
Expand All @@ -46,25 +46,25 @@ func (q *Queue) Fix() {
}

// PopFront removes the first element in the queue, if any
func (q *Queue) PopFront() Item {
func (q *Queue[T]) PopFront() *T {
if len(*q) == 0 {
return nil
}

el := (*q)[0]
*q = (*q)[1:]

return el
return &el
}

// PopBack removes the last element in the queue, if any
func (q *Queue) PopBack() Item {
func (q *Queue[T]) PopBack() *T {
if len(*q) == 0 {
return nil
}

el := (*q)[len(*q)-1]
*q = (*q)[:len(*q)-1]

return el
return &el
}
36 changes: 12 additions & 24 deletions queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,9 @@ import (
)

// isSorted checks if the queue is sorted
func isSorted(q Queue, ascending bool) bool {
cmp := func(a, b *mockItem) bool {
return a.value <= b.value
}

if !ascending {
cmp = func(a, b *mockItem) bool {
return a.value >= b.value
}
}

func isSorted[T Item[T]](q Queue[T]) bool {
for i := 0; i < len(q)-1; i++ {
if !cmp(q[i].(*mockItem), q[i+1].(*mockItem)) {
if !q[i].Less(q[i+1]) {
return false
}
}
Expand Down Expand Up @@ -76,26 +66,24 @@ func TestQueue_Insert(t *testing.T) {
)

// Create a new queue
q := NewQueue()
q := NewQueue[*mockItem]()

// Push items
for _, item := range items {
item := item

if !testCase.ascending {
// Prep the items if needed (min / max)
item.lessFn = func(i Item) bool {
other, _ := i.(*mockItem)

return item.value >= other.value
item.lessFn = func(i *mockItem) bool {
return item.value >= i.value
}
}

q.Push(item)
}

assert.Len(t, q, numItems)
assert.True(t, isSorted(q, testCase.ascending))
assert.True(t, isSorted(q))
})
}
}
Expand All @@ -109,7 +97,7 @@ func TestQueue_PopFront(t *testing.T) {
)

// Create a new queue
q := NewQueue()
q := NewQueue[*mockItem]()

// Push items
for _, item := range items {
Expand All @@ -127,7 +115,7 @@ func TestQueue_PopFront(t *testing.T) {
for _, item := range items {
popped := q.PopFront()

assert.Equal(t, item, popped)
assert.Equal(t, item, *popped)
}

assert.Len(t, q, 0)
Expand All @@ -143,7 +131,7 @@ func TestQueue_PopBack(t *testing.T) {
)

// Create a new queue
q := NewQueue()
q := NewQueue[*mockItem]()

// Push items
for _, item := range items {
Expand All @@ -161,7 +149,7 @@ func TestQueue_PopBack(t *testing.T) {
for i := len(items) - 1; i >= 0; i-- {
popped := q.PopBack()

assert.Equal(t, items[i], popped)
assert.Equal(t, items[i], *popped)
}

assert.Len(t, q, 0)
Expand All @@ -177,7 +165,7 @@ func TestQueue_Fix(t *testing.T) {
)

// Create a new queue
q := NewQueue()
q := NewQueue[*mockItem]()

// Push items
for _, item := range items {
Expand All @@ -197,6 +185,6 @@ func TestQueue_Fix(t *testing.T) {
q.Fix()

assert.Equal(t, q.Len(), numItems)
assert.True(t, isSorted(q, true))
assert.True(t, isSorted(q))
assert.Equal(t, newItem, q.Index(0))
}
4 changes: 2 additions & 2 deletions types.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package queue

// Item represents a single queue item
type Item interface {
type Item[T any] interface {
// Less returns a flag indicating if the element
// has a lower value than another element.
// For max-priority queue implementations, Less should return true if A > B
Less(b Item) bool
Less(b T) bool
}
Loading