first commit

This commit is contained in:
Josh Baker 2017-05-01 12:53:51 -07:00
commit fc4cef0a81
4 changed files with 171 additions and 0 deletions

15
LICENSE Normal file
View File

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2017, Vladimir Agafonkin
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# tinyqueue
<a href="https://godoc.org/github.com/tidwall/tinyqueue"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
tinyqueue is a Go package for binary heap priority queues.
Ported from the [tinyqueue](https://github.com/mourner/tinyqueue) Javascript library.

84
tinyqueue.go Normal file
View File

@ -0,0 +1,84 @@
package tinyqueue
type Queue struct {
length int
data []Item
}
type Item interface {
Less(Item) bool
}
func New(data []Item) *Queue {
q := &Queue{}
q.data = data
q.length = len(data)
if q.length > 0 {
i := q.length >> 1
for ; i >= 0; i-- {
q.down(i)
}
}
return q
}
func (q *Queue) Push(item Item) {
q.data = append(q.data, item)
q.length++
q.up(q.length - 1)
}
func (q *Queue) Pop() Item {
if q.length == 0 {
return nil
}
top := q.data[0]
q.length--
if q.length > 0 {
q.data[0] = q.data[q.length]
q.down(0)
}
q.data = q.data[:len(q.data)-1]
return top
}
func (q *Queue) Peek() Item {
if q.length == 0 {
return nil
}
return q.data[0]
}
func (q *Queue) down(pos int) {
data := q.data
halfLength := q.length >> 1
item := data[pos]
for pos < halfLength {
left := (pos << 1) + 1
right := left + 1
best := data[left]
if right < q.length && data[right].Less(best) {
left = right
best = data[right]
}
if !best.Less(item) {
break
}
data[pos] = best
pos = left
}
data[pos] = item
}
func (q *Queue) up(pos int) {
data := q.data
item := data[pos]
for pos > 0 {
parent := (pos - 1) >> 1
current := data[parent]
if !item.Less(current) {
break
}
data[pos] = current
pos = parent
}
data[pos] = item
}

65
tinyqueue_test.go Normal file
View File

@ -0,0 +1,65 @@
package tinyqueue
import (
"math/rand"
"sort"
"testing"
"time"
"github.com/json-iterator/go/assert"
)
type floatValue float64
func (a floatValue) Less(b Item) bool {
return a < b.(floatValue)
}
var data, sorted = func() ([]Item, []Item) {
rand.Seed(time.Now().UnixNano())
var data []Item
for i := 0; i < 100; i++ {
data = append(data, floatValue(rand.Float64()*100))
}
sorted := make([]Item, len(data))
copy(sorted, data)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Less(sorted[j])
})
return data, sorted
}()
func TestMaintainsPriorityQueue(t *testing.T) {
q := New(nil)
for i := 0; i < len(data); i++ {
q.Push(data[i])
}
assert.Equal(t, q.Peek(), sorted[0])
var result []Item
for q.length > 0 {
result = append(result, q.Pop())
}
assert.Equal(t, result, sorted)
}
func TestAcceptsDataInConstructor(t *testing.T) {
q := New(data)
var result []Item
for q.length > 0 {
result = append(result, q.Pop())
}
assert.Equal(t, result, sorted)
}
func TestHandlesEdgeCasesWithFewElements(t *testing.T) {
q := New(nil)
q.Push(floatValue(2))
q.Push(floatValue(1))
q.Pop()
q.Pop()
q.Pop()
q.Push(floatValue(2))
q.Push(floatValue(1))
assert.Equal(t, float64(q.Pop().(floatValue)), 1.0)
assert.Equal(t, float64(q.Pop().(floatValue)), 2.0)
assert.Equal(t, q.Pop(), nil)
}