Skip to content

Commit

Permalink
Add v2 with generics (#2)
Browse files Browse the repository at this point in the history
* Add version 2 with generics

* Add another benchmark inspired by the original

* Remove New in favor of variable initialization

* Add golang version constraint in readme

* Update README example
  • Loading branch information
invisiblefunnel committed Jun 4, 2022
1 parent 0262637 commit b01cbdd
Show file tree
Hide file tree
Showing 8 changed files with 441 additions and 22 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ jobs:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: '>=1.18.0'
- uses: actions/checkout@v2
- run: go test -v -bench .
- run: |
cd v2
go test -v -bench .
49 changes: 28 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
## flatqueue-go [![Tests](https://github.com/invisiblefunnel/flatqueue-go/actions/workflows/go.yml/badge.svg)](https://github.com/invisiblefunnel/flatqueue-go/actions/workflows/go.yml)

A Go port of the [flatqueue](https://github.com/mourner/flatqueue) priority queue library. Push items by identifier (`int`) and value (`float64`).
A Go 1.18+ port of the [flatqueue](https://github.com/mourner/flatqueue) priority queue library using generics.

`Peek`, `PeekValue`, and `Pop` will panic if called on an empty queue. You must check `Len` accordingly.

```go
q := flatqueue.New()
package main

for i, item := range items {
q.Push(i, item.Value)
import "github.com/invisiblefunnel/flatqueue-go/v2"

type Item struct {
Name string
Value int
}

var (
id int
value float64
)
func main() {
items := []Item{
{"X", 5},
{"Y", 2},
{"Z", 3},
}

id = q.Peek() // top item index
value = q.PeekValue() // top item value
id = q.Pop() // remove and return the top item index
var q flatqueue.FlatQueue[Item, int]

// loop while queue is not empty
for q.Len() > 0 {
q.Pop()
}
```
for _, item := range items {
q.Push(item, item.Value)
}

Specifying an initial capacity for the underlying slices may improve the performance of `Push` if you know, or can estimate, the maximum length of the queue. This does not limit the length of the queue.
var (
item Item
value int
)

```go
q := flatqueue.NewWithCapacity(len(items))
item = q.Peek() // top item
value = q.PeekValue() // top item value
item = q.Pop() // remove and return the top item

for i, item := range items {
q.Push(i, item.Value)
// loop while the queue is not empty
for q.Len() > 0 {
q.Pop()
}
}
```
37 changes: 37 additions & 0 deletions README_v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## flatqueue-go [![Tests](https://github.com/invisiblefunnel/flatqueue-go/actions/workflows/go.yml/badge.svg)](https://github.com/invisiblefunnel/flatqueue-go/actions/workflows/go.yml)

A Go port of the [flatqueue](https://github.com/mourner/flatqueue) priority queue library. Push items by identifier (`int`) and value (`float64`).

`Peek`, `PeekValue`, and `Pop` will panic if called on an empty queue. You must check `Len` accordingly.

```go
q := flatqueue.New()

for i, item := range items {
q.Push(i, item.Value)
}

var (
id int
value float64
)

id = q.Peek() // top item index
value = q.PeekValue() // top item value
id = q.Pop() // remove and return the top item index

// loop while queue is not empty
for q.Len() > 0 {
q.Pop()
}
```

Specifying an initial capacity for the underlying slices may improve the performance of `Push` if you know, or can estimate, the maximum length of the queue. This does not limit the length of the queue.

```go
q := flatqueue.NewWithCapacity(len(items))

for i, item := range items {
q.Push(i, item.Value)
}
```
Empty file added go.sum
Empty file.
92 changes: 92 additions & 0 deletions v2/flatqueue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package flatqueue

import (
"golang.org/x/exp/constraints"
)

type FlatQueue[T any, V constraints.Ordered] struct {
items []T
values []V
length int
}

func (q *FlatQueue[_, _]) Len() int {
return q.length
}

func (q *FlatQueue[T, V]) Push(item T, value V) {
pos := q.length
q.length++

q.items = append(q.items, item)
q.values = append(q.values, value)

for pos > 0 {
parent := (pos - 1) >> 1
parentValue := q.values[parent]

if value > parentValue {
break
}

q.items[pos] = q.items[parent]
q.values[pos] = parentValue

pos = parent
}

q.items[pos] = item
q.values[pos] = value
}

func (q *FlatQueue[T, _]) Pop() T {
top := q.items[0]
q.length--

if q.length > 0 {
id := q.items[q.length]
value := q.values[q.length]

q.items[0] = id
q.values[0] = value

halfLength := q.length >> 1
pos := 0

for pos < halfLength {
left := (pos << 1) + 1
right := left + 1

bestIndex := q.items[left]
bestValue := q.values[left]
rightValue := q.values[right]

if right < q.length && rightValue < bestValue {
left = right
bestIndex = q.items[right]
bestValue = rightValue
}

if bestValue > value {
break
}

q.items[pos] = bestIndex
q.values[pos] = bestValue
pos = left
}

q.items[pos] = id
q.values[pos] = value
}

return top
}

func (q *FlatQueue[T, _]) Peek() T {
return q.items[0]
}

func (q *FlatQueue[_, V]) PeekValue() V {
return q.values[0]
}
Loading

0 comments on commit b01cbdd

Please sign in to comment.