From 010ac6eed7cecf2f6bac94fd367754668f9f4bb6 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 09:58:57 -0600 Subject: [PATCH 1/8] wip --- .gitignore | 2 + LICENSE | 20 -- Readme.md | 32 +-- UNLICENSE | 24 +++ btree.go | 106 ++++++++++ btree_test.go | 26 +++ collection.go | 28 +++ collections.go | 141 ++++++++++--- go.mod | 14 ++ go.sum | 13 ++ grid/grid.go | 52 ----- map.go | 93 +++++++++ map_test.go | 17 ++ point.go | 7 - queue/queue.go | 55 ----- queue/queue_test.go | 50 ----- set/set.go | 106 ---------- set/set_test.go | 74 ------- skip/skip.go | 171 ---------------- skip/skip_test.go | 38 ---- slice.go | 187 +++++++++++++++++ slice_test.go | 36 ++++ splay/splay.go | 487 -------------------------------------------- splay/splay_test.go | 27 --- stack/stack.go | 44 ---- stack/stack_test.go | 42 ---- trie/trie.go | 148 -------------- trie/trie_test.go | 31 --- tst/tst.go | 306 ---------------------------- tst/tst_test.go | 83 -------- tuples.go | 12 ++ 31 files changed, 680 insertions(+), 1792 deletions(-) create mode 100644 .gitignore delete mode 100644 LICENSE create mode 100644 UNLICENSE create mode 100644 btree.go create mode 100644 btree_test.go create mode 100644 collection.go create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 grid/grid.go create mode 100644 map.go create mode 100644 map_test.go delete mode 100644 point.go delete mode 100644 queue/queue.go delete mode 100644 queue/queue_test.go delete mode 100755 set/set.go delete mode 100644 set/set_test.go delete mode 100644 skip/skip.go delete mode 100644 skip/skip_test.go create mode 100644 slice.go create mode 100644 slice_test.go delete mode 100644 splay/splay.go delete mode 100644 splay/splay_test.go delete mode 100644 stack/stack.go delete mode 100644 stack/stack_test.go delete mode 100644 trie/trie.go delete mode 100644 trie/trie_test.go delete mode 100644 tst/tst.go delete mode 100644 tst/tst_test.go create mode 100644 tuples.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66f8fb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.vscode/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 863a984..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2012 Caleb Doxsey - -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. \ No newline at end of file diff --git a/Readme.md b/Readme.md index 8e32ec6..7911ca7 100644 --- a/Readme.md +++ b/Readme.md @@ -1,26 +1,10 @@ # Badgerodon Collections -Maps and slices go a long way in Go, but sometimes you need more. This is a collection of collections that may be useful. - -## Queue -A [queue](http://en.wikipedia.org/wiki/Queue_(data_structure%29) is a first-in first-out data structure. - -## Set -A [set](http://en.wikipedia.org/wiki/Set_(computer_science%29) is an unordered collection of unique values typically used for testing membership. - -## Skip list -A [skip list](http://en.wikipedia.org/wiki/Skip_list) is a data structure that stores nodes in a hierarchy of linked lists. It gives performance similar to binary search trees by using a random number of forward links to skip parts of the list. - -## Splay Tree - -A [splay tree](http://en.wikipedia.org/wiki/Splay_tree) is a type of binary search tree where every access to the tree results in the tree being rearranged so that the current node gets put on top. - -## Stack -A [stack](http://en.wikipedia.org/wiki/Stack_(abstract_data_type%29) is a last-in last-out data structure. - -## Trie -A [trie](http://en.wikipedia.org/wiki/Trie) is a type of tree where each node represents one byte of a key. - -## Ternary Search Tree - -A [ternary search tree](http://en.wikipedia.org/wiki/Ternary_search_tree) is similar to a trie in that nodes store the letters of the key, but instead of either using a list or hash at each node a binary tree is used. Ternary search trees have the performance benefits of a trie without the usual memory costs. \ No newline at end of file +Useful collections: + +- Deque +- List +- Map +- Queue +- Set +- Stack diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..f50ef62 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/btree.go b/btree.go new file mode 100644 index 0000000..7cb0d40 --- /dev/null +++ b/btree.go @@ -0,0 +1,106 @@ +package collections + +import "github.com/tidwall/btree" + +type dictionaryViaBTree[TKey, TValue any] struct { + b *btree.Generic[Pair[TKey, TValue]] + less func(TKey, TKey) bool +} + +var _ interface{ Dictionary[int, int] } = (*dictionaryViaBTree[int, int])(nil) + +func newDictionaryViaBTree[TKey, TValue any](less func(TKey, TKey) bool) *dictionaryViaBTree[TKey, TValue] { + return &dictionaryViaBTree[TKey, TValue]{ + b: btree.NewGeneric(func(a, b Pair[TKey, TValue]) bool { + return less(a.First, b.First) + }), + less: less, + } +} + +func (d *dictionaryViaBTree[TKey, TValue]) Clear() { + less := d.less + d.b = btree.NewGeneric(func(a, b Pair[TKey, TValue]) bool { + return less(a.First, b.First) + }) + d.less = less +} + +func (d *dictionaryViaBTree[TKey, TValue]) Delete(key TKey) { + var zero TValue + d.b.Delete(NewPair(key, zero)) +} + +func (d *dictionaryViaBTree[TKey, TValue]) ForEach(callback func(Pair[TKey, TValue]) bool) { + d.b.Scan(func(item Pair[TKey, TValue]) bool { + return callback(item) + }) +} + +func (d *dictionaryViaBTree[TKey, TValue]) Get(key TKey) (value TValue, ok bool) { + item, ok := d.b.Get(NewPair(key, value)) + if !ok { + return value, ok + } + + return item.Second, true +} + +func (d *dictionaryViaBTree[TKey, TValue]) Keys() Collection[TKey] { + return Map[Pair[TKey, TValue]](d, func(pair Pair[TKey, TValue]) TKey { + return pair.First + }) +} + +func (d *dictionaryViaBTree[TKey, TValue]) Set(key TKey, value TValue) { + d.b.Set(NewPair(key, value)) +} + +func (d *dictionaryViaBTree[TKey, TValue]) Size() int { + return d.b.Len() +} + +func (d *dictionaryViaBTree[TKey, TValue]) Values() Collection[TValue] { + return Map[Pair[TKey, TValue]](d, func(pair Pair[TKey, TValue]) TValue { + return pair.Second + }) +} + +type setViaBTree[T any] struct { + b *btree.Generic[T] + less func(T, T) bool +} + +var _ interface{ Set[int] } = (*setViaBTree[int])(nil) + +func newSetViaBTree[T any](less func(T, T) bool) *setViaBTree[T] { + return &setViaBTree[T]{ + b: btree.NewGeneric(less), + less: less, + } +} + +func (s *setViaBTree[T]) Add(value T) { + s.b.Set(value) +} + +func (s *setViaBTree[T]) Clear() { + s.b = btree.NewGeneric(s.less) +} + +func (s *setViaBTree[T]) Delete(value T) { + s.b.Delete(value) +} + +func (s *setViaBTree[T]) ForEach(callback func(T) bool) { + s.b.Scan(callback) +} + +func (s *setViaBTree[T]) Has(value T) bool { + _, ok := s.b.Get(value) + return ok +} + +func (s *setViaBTree[T]) Size() int { + return s.b.Len() +} diff --git a/btree_test.go b/btree_test.go new file mode 100644 index 0000000..00374fb --- /dev/null +++ b/btree_test.go @@ -0,0 +1,26 @@ +package collections + +import ( + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMapViaBTree(t *testing.T) { + m := newDictionaryViaBTree[int, int](func(a, b int) bool { + return a < b + }) + + ordered := rand.Perm(50) + sort.Ints(ordered) + unordered := rand.Perm(50) + + for _, i := range unordered { + m.Set(i, i) + } + + s := NewSlice(m.Keys()) + assert.Equal(t, ordered, s) +} diff --git a/collection.go b/collection.go new file mode 100644 index 0000000..2923038 --- /dev/null +++ b/collection.go @@ -0,0 +1,28 @@ +package collections + +type collectionImpl[T any] struct { + forEach func(callback func(T) bool) + size func() int +} + +func (c collectionImpl[T]) ForEach(callback func(T) bool) { + c.forEach(callback) +} + +func (c collectionImpl[T]) Size() int { + return c.size() +} + +// Map maps all the values in a collection to a new collection. +func Map[TFrom, TTo any](c Collection[TFrom], f func(TFrom) TTo) Collection[TTo] { + return collectionImpl[TTo]{ + forEach: func(callback func(TTo) bool) { + c.ForEach(func(v TFrom) bool { + return callback(f(v)) + }) + }, + size: func() int { + return c.Size() + }, + } +} diff --git a/collections.go b/collections.go index 5922f4f..a8f8347 100644 --- a/collections.go +++ b/collections.go @@ -1,27 +1,114 @@ -package collections - -type ( - Collection interface { - Do(func(interface{})bool) - } -) - -func GetRange(c Collection, start, length int) []interface{} { - end := start + length - items := make([]interface{}, length) - i := 0 - j := 0 - c.Do(func(item interface{})bool{ - if i >= start { - if i < end { - items[j] = item - j++ - } else { - return false - } - } - i++ - return true - }) - return items[:j] -} +package collections + +type ( + Collection[T any] interface { + ForEach(callback func(T) bool) + Size() int + } + + // A Deque is a double-ended queue. + Deque[T any] interface { + Clear() + PeekBack() (value T, ok bool) + PeekFront() (value T, ok bool) + PopBack() (value T, ok bool) + PopFront() (value T, ok bool) + PushBack(value T) + PushFront(value T) + Size() int + } + + // A Dictionary is a collection of key value pairs. + Dictionary[TKey, TValue any] interface { + Clear() + Delete(key TKey) + ForEach(callback func(Pair[TKey, TValue]) bool) + Get(key TKey) (value TValue, ok bool) + Keys() Collection[TKey] + Set(key TKey, value TValue) + Size() int + Values() Collection[TValue] + } + + // A Queue is a collection that supports FIFO operations. + Queue[T any] interface { + Clear() + Peek() (value T, ok bool) + Pop() (value T, ok bool) + Push(value T) + Size() int + } + + // A Set is a unique collection of values. + Set[T any] interface { + Add(value T) + Clear() + Delete(value T) + ForEach(callback func(T) bool) + Has(value T) bool + Size() int + } + + // A Stack is a collection that supports LIFO operations. + Stack[T any] interface { + Clear() + Peek() (value T, ok bool) + Pop() (value T, ok bool) + Push(value T) + Size() int + } +) + +// NewDeque creates a new Deque implemented using a slice as a ring buffer. +func NewDeque[T any]() Deque[T] { + return newDequeViaRingSlice[T]() +} + +// NewDictionary creates a new Dictionary implemented using a map. +func NewDictionary[TKey comparable, TValue any]() Dictionary[TKey, TValue] { + return newDictionaryViaMap[TKey, TValue]() +} + +// NewQueue creates a new Queue implemented using a slice. +func NewQueue[T any]() Queue[T] { + return newQueueViaSlice[T]() +} + +// NewSet creates a new Set implemented using a map. +func NewSet[T comparable]() Set[T] { + return newSetViaMap[T]() +} + +// NewSlice creates a new slice from a collection. +func NewSlice[T any](collection Collection[T]) []T { + arr := make([]T, 0, collection.Size()) + collection.ForEach(func(item T) bool { + arr = append(arr, item) + return true + }) + return arr +} + +// NewSortedDictionary creates a new Dictionary implemented using a btree, providing sorted iteration by the less function. +func NewSortedDictionary[TKey, TValue any](less func(TKey, TKey) bool) Dictionary[TKey, TValue] { + return newDictionaryViaBTree[TKey, TValue](less) +} + +// NewSortedSet creates a new Set implemented using a btree, providing sorted iteration by the less function. +func NewSortedSet[T any](less func(T, T) bool) Set[T] { + return newSetViaBTree(less) +} + +// NewStack creates a new Stack implemented using a slice. +func NewStack[T any]() Stack[T] { + return newStackViaSlice[T]() +} + +type integer interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +func mod[T integer](x, y T) T { + return (x%y + y) % y +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4aa810b --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module github.com/badgerodon/collections + +go 1.18 + +require ( + github.com/stretchr/testify v1.7.1 + github.com/tidwall/btree v1.2.0 +) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bea72a5 --- /dev/null +++ b/go.sum @@ -0,0 +1,13 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/btree v1.2.0 h1:cXmC8TB0xxua4tmOf+cLhi4KE59e2075IXB/KRjEhYQ= +github.com/tidwall/btree v1.2.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/grid/grid.go b/grid/grid.go deleted file mode 100644 index 81ef9d0..0000000 --- a/grid/grid.go +++ /dev/null @@ -1,52 +0,0 @@ -package grid - -import ( - . "github.com/badgerodon/collections" -) - -type ( - Grid struct { - values []interface{} - cols, rows int - } -) - -func New(cols, rows int) *Grid { - return &Grid{ - values: make([]interface{}, cols*rows), - cols: cols, - rows: rows, - } -} - -func (this *Grid) Do(f func(p Point, value interface{})) { - for x := 0; x < this.cols; x++ { - for y := 0; y < this.rows; y++ { - f(Point{x, y}, this.values[x*this.cols+y]) - } - } -} - -func (this *Grid) Get(p Point) interface{} { - if p.X < 0 || p.Y < 0 || p.X >= this.cols || p.Y >= this.rows { - return nil - } - v, _ := this.values[p.X*this.cols+p.Y] - return v -} - -func (this *Grid) Rows() int { - return this.rows -} - -func (this *Grid) Cols() int { - return this.cols -} - -func (this *Grid) Len() int { - return this.rows * this.cols -} - -func (this *Grid) Set(p Point, v interface{}) { - -} diff --git a/map.go b/map.go new file mode 100644 index 0000000..40ebcd2 --- /dev/null +++ b/map.go @@ -0,0 +1,93 @@ +package collections + +type dictionaryViaMap[TKey comparable, TValue any] struct { + m map[TKey]TValue +} + +var _ interface{ Dictionary[int, int] } = (*dictionaryViaMap[int, int])(nil) + +func newDictionaryViaMap[TKey comparable, TValue any]() *dictionaryViaMap[TKey, TValue] { + return &dictionaryViaMap[TKey, TValue]{ + m: make(map[TKey]TValue), + } +} + +func (d *dictionaryViaMap[TKey, TValue]) Clear() { + d.m = make(map[TKey]TValue) +} + +func (d *dictionaryViaMap[TKey, TValue]) Delete(key TKey) { + delete(d.m, key) +} + +func (d *dictionaryViaMap[TKey, TValue]) ForEach(callback func(entry Pair[TKey, TValue]) bool) { + for k, v := range d.m { + if !callback(NewPair(k, v)) { + return + } + } +} + +func (d *dictionaryViaMap[TKey, TValue]) Get(key TKey) (value TValue, ok bool) { + value, ok = d.m[key] + return value, ok +} + +func (d *dictionaryViaMap[TKey, TValue]) Keys() Collection[TKey] { + return Map[Pair[TKey, TValue]](d, func(pair Pair[TKey, TValue]) TKey { + return pair.First + }) +} + +func (d *dictionaryViaMap[TKey, TValue]) Set(key TKey, value TValue) { + d.m[key] = value +} + +func (d *dictionaryViaMap[TKey, TValue]) Size() int { + return len(d.m) +} + +func (d *dictionaryViaMap[TKey, TValue]) Values() Collection[TValue] { + return Map[Pair[TKey, TValue]](d, func(pair Pair[TKey, TValue]) TValue { + return pair.Second + }) +} + +type setViaMap[T comparable] struct { + m map[T]struct{} +} + +var _ interface{ Set[int] } = (*setViaMap[int])(nil) + +func newSetViaMap[T comparable]() *setViaMap[T] { + return &setViaMap[T]{m: make(map[T]struct{})} +} + +func (s *setViaMap[T]) Add(value T) { + s.m[value] = struct{}{} +} + +func (s *setViaMap[T]) Clear() { + s.m = make(map[T]struct{}) +} + +func (s *setViaMap[T]) Delete(value T) { + delete(s.m, value) +} + +func (s *setViaMap[T]) ForEach(callback func(T) bool) { + for v := range s.m { + if !callback(v) { + break + } + } +} + +func (s *setViaMap[T]) Has(value T) bool { + _, ok := s.m[value] + return ok +} + +func (s *setViaMap[T]) Size() int { + return len(s.m) +} diff --git a/map_test.go b/map_test.go new file mode 100644 index 0000000..e1f88fa --- /dev/null +++ b/map_test.go @@ -0,0 +1,17 @@ +package collections + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMapSet(t *testing.T) { + s := newSetViaMap[int]() + s.Add(1) + assert.True(t, s.Has(1)) + assert.Equal(t, 1, s.Size()) + s.Delete(1) + assert.False(t, s.Has(1)) + assert.Equal(t, 0, s.Size()) +} diff --git a/point.go b/point.go deleted file mode 100644 index 732e2b9..0000000 --- a/point.go +++ /dev/null @@ -1,7 +0,0 @@ -package collections - -type ( - Point struct { - X, Y int - } -) diff --git a/queue/queue.go b/queue/queue.go deleted file mode 100644 index ae17348..0000000 --- a/queue/queue.go +++ /dev/null @@ -1,55 +0,0 @@ -package queue - -type ( - Queue struct { - start, end *node - length int - } - node struct { - value interface{} - next *node - } -) - -// Create a new queue -func New() *Queue { - return &Queue{nil,nil,0} -} -// Take the next item off the front of the queue -func (this *Queue) Dequeue() interface{} { - if this.length == 0 { - return nil - } - n := this.start - if this.length == 1 { - this.start = nil - this.end = nil - } else { - this.start = this.start.next - } - this.length-- - return n.value -} -// Put an item on the end of a queue -func (this *Queue) Enqueue(value interface{}) { - n := &node{value,nil} - if this.length == 0 { - this.start = n - this.end = n - } else { - this.end.next = n - this.end = n - } - this.length++ -} -// Return the number of items in the queue -func (this *Queue) Len() int { - return this.length -} -// Return the first item in the queue without removing it -func (this *Queue) Peek() interface{} { - if this.length == 0 { - return nil - } - return this.start.value -} diff --git a/queue/queue_test.go b/queue/queue_test.go deleted file mode 100644 index fe6fe07..0000000 --- a/queue/queue_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package queue - -import ( - "testing" -) - -func Test(t *testing.T) { - q := New() - - if q.Len() != 0 { - t.Errorf("Length should be 0") - } - - q.Enqueue(1) - - if q.Len() != 1 { - t.Errorf("Length should be 1") - } - - if q.Peek().(int) != 1 { - t.Errorf("Enqueued value should be 1") - } - - v := q.Dequeue() - - if v.(int) != 1 { - t.Errorf("Dequeued value should be 1") - } - - if q.Peek() != nil || q.Dequeue() != nil { - t.Errorf("Empty queue should have no values") - } - - q.Enqueue(1) - q.Enqueue(2) - - if q.Len() != 2 { - t.Errorf("Length should be 2") - } - - if q.Peek().(int) != 1 { - t.Errorf("First value should be 1") - } - - q.Dequeue() - - if q.Peek().(int) != 2 { - t.Errorf("Next value should be 2") - } -} \ No newline at end of file diff --git a/set/set.go b/set/set.go deleted file mode 100755 index b528a5c..0000000 --- a/set/set.go +++ /dev/null @@ -1,106 +0,0 @@ -package set - -type ( - Set struct { - hash map[interface{}]nothing - } - - nothing struct{} -) - -// Create a new set -func New(initial ...interface{}) *Set { - s := &Set{make(map[interface{}]nothing)} - - for _, v := range initial { - s.Insert(v) - } - - return s -} - -// Find the difference between two sets -func (this *Set) Difference(set *Set) *Set { - n := make(map[interface{}]nothing) - - for k, _ := range this.hash { - if _, exists := set.hash[k]; !exists { - n[k] = nothing{} - } - } - - return &Set{n} -} - -// Call f for each item in the set -func (this *Set) Do(f func(interface{})) { - for k, _ := range this.hash { - f(k) - } -} - -// Test to see whether or not the element is in the set -func (this *Set) Has(element interface{}) bool { - _, exists := this.hash[element] - return exists -} - -// Add an element to the set -func (this *Set) Insert(element interface{}) { - this.hash[element] = nothing{} -} - -// Find the intersection of two sets -func (this *Set) Intersection(set *Set) *Set { - n := make(map[interface{}]nothing) - - for k, _ := range this.hash { - if _, exists := set.hash[k]; exists { - n[k] = nothing{} - } - } - - return &Set{n} -} - -// Return the number of items in the set -func (this *Set) Len() int { - return len(this.hash) -} - -// Test whether or not this set is a proper subset of "set" -func (this *Set) ProperSubsetOf(set *Set) bool { - return this.SubsetOf(set) && this.Len() < set.Len() -} - -// Remove an element from the set -func (this *Set) Remove(element interface{}) { - delete(this.hash, element) -} - -// Test whether or not this set is a subset of "set" -func (this *Set) SubsetOf(set *Set) bool { - if this.Len() > set.Len() { - return false - } - for k, _ := range this.hash { - if _, exists := set.hash[k]; !exists { - return false - } - } - return true -} - -// Find the union of two sets -func (this *Set) Union(set *Set) *Set { - n := make(map[interface{}]nothing) - - for k, _ := range this.hash { - n[k] = nothing{} - } - for k, _ := range set.hash { - n[k] = nothing{} - } - - return &Set{n} -} diff --git a/set/set_test.go b/set/set_test.go deleted file mode 100644 index 9f7e19e..0000000 --- a/set/set_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package set - -import ( - "testing" -) - -func Test(t *testing.T) { - s := New() - - s.Insert(5) - - if s.Len() != 1 { - t.Errorf("Length should be 1") - } - - if !s.Has(5) { - t.Errorf("Membership test failed") - } - - s.Remove(5) - - if s.Len() != 0 { - t.Errorf("Length should be 0") - } - - if s.Has(5) { - t.Errorf("The set should be empty") - } - - // Difference - s1 := New(1,2,3,4,5,6) - s2 := New(4,5,6) - s3 := s1.Difference(s2) - - if s3.Len() != 3 { - t.Errorf("Length should be 3") - } - - if !(s3.Has(1) && s3.Has(2) && s3.Has(3)) { - t.Errorf("Set should only contain 1, 2, 3") - } - - // Intersection - s3 = s1.Intersection(s2) - if s3.Len() != 3 { - t.Errorf("Length should be 3 after intersection") - } - - if !(s3.Has(4) && s3.Has(5) && s3.Has(6)) { - t.Errorf("Set should contain 4, 5, 6") - } - - // Union - s4 := New(7,8,9) - s3 = s2.Union(s4) - - if s3.Len() != 6 { - t.Errorf("Length should be 6 after union") - } - - if !(s3.Has(7)) { - t.Errorf("Set should contain 4, 5, 6, 7, 8, 9") - } - - // Subset - if !s1.SubsetOf(s1) { - t.Errorf("set should be a subset of itself") - } - // Proper Subset - if s1.ProperSubsetOf(s1) { - t.Errorf("set should not be a subset of itself") - } - -} diff --git a/skip/skip.go b/skip/skip.go deleted file mode 100644 index a070075..0000000 --- a/skip/skip.go +++ /dev/null @@ -1,171 +0,0 @@ -package skip - -import ( - "fmt" - "math/rand" - "time" -) - -type ( - node struct { - next []*node - key interface{} - value interface{} - } - SkipList struct { - root *node - size int - less func(interface{},interface{})bool - gen *rand.Rand - probability float64 - } -) -// Create a new skip list -func New(less func(interface{},interface{})bool) *SkipList { - gen := rand.New(rand.NewSource(time.Now().UnixNano())) - n := &node{make([]*node, 0),nil,nil} - return &SkipList{n, 0, less, gen, 0.75} -} -func (this *SkipList) Do(f func(interface{}, interface{})bool) { - if this.size == 0 { - return - } - cur := this.root.next[0] - for cur != nil { - if !f(cur.key, cur.value) { - break - } - cur = cur.next[0] - } -} -// Get an item from the skip list -func (this *SkipList) Get(key interface{}) interface{} { - if this.size == 0 { - return nil - } - - cur := this.root - // Start at the top - for i := len(cur.next)-1; i >= 0; i-- { - for this.less(cur.next[i].key, key) { - cur = cur.next[i] - } - } - cur = cur.next[0] - - if this.equals(cur.key, key) { - return cur.value - } - - return nil -} -// Insert a new item into the skip list -func (this *SkipList) Insert(key interface{}, value interface{}) { - prev := this.getPrevious(key) - - // Already in the list so just update the value - if len(prev) > 0 && prev[0].next[0] != nil && this.equals(prev[0].next[0].key, key) { - prev[0].next[0].value = value - return - } - - h := len(this.root.next) - nh := this.pickHeight() - n := &node{make([]*node, nh),key,value} - - // Higher than anything seen before, so tack it on top - if nh > h { - this.root.next = append(this.root.next, n) - } - - // Update the previous nodes - for i := 0; i < h && i < nh; i++ { - n.next[i] = prev[i].next[i] - prev[i].next[i] = n - } - - this.size++ -} -// Get the length of the skip list -func (this *SkipList) Len() int { - return this.size -} -// Remove an item from the skip list -func (this *SkipList) Remove(key interface{}) interface{} { - prev := this.getPrevious(key) - if len(prev) == 0 { - return nil - } - cur := prev[0].next[0] - - // If we found it - if cur != nil && this.equals(key, cur.key) { - // Change all the linked lists - for i := 0; i < len(prev); i++ { - if prev[i] != nil && prev[i].next[i] != nil { - prev[i].next[i] = cur.next[i] - } - } - - // Kill off the upper links if they're nil - for i := len(this.root.next)-1; i>=0; i-- { - if this.root.next[i] == nil { - this.root.next = this.root.next[:i] - } else { - break - } - } - - this.size-- - - return cur.value - } - - return nil -} -// String representation of the list -func (this *SkipList) String() string { - str := "{" - if len(this.root.next) > 0 { - cur := this.root.next[0] - for cur != nil { - str += fmt.Sprint(cur.key) - str += ":" - str += fmt.Sprint(cur.value) - str += " " - cur = cur.next[0] - } - } - str += "}" - - return str -} -// Get a vertical list of nodes of all the things that occur -// immediately before "key" -func (this *SkipList) getPrevious(key interface{}) []*node { - cur := this.root - h := len(cur.next) - nodes := make([]*node, h) - for i := h-1; i >= 0; i-- { - for cur.next[i] != nil && this.less(cur.next[i].key, key) { - cur = cur.next[i] - } - nodes[i] = cur - } - return nodes -} -// Defines an equals method in terms of "less" -func (this *SkipList) equals(a, b interface{}) bool { - return !this.less(a,b) && !this.less(b,a) -} -// Pick a random height -func (this *SkipList) pickHeight() int { - h := 1 - for this.gen.Float64() > this.probability { - h++ - } - if h > len(this.root.next) { - return h + 1 - } - return h -} diff --git a/skip/skip_test.go b/skip/skip_test.go deleted file mode 100644 index 45a600d..0000000 --- a/skip/skip_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package skip - -import ( - //"fmt" - "testing" -) - -func Test(t *testing.T) { - sl := New(func(a,b interface{})bool { - return a.(int) < b.(int) - }) - sl.Insert(1, 100) - if sl.Len() != 1 { - t.Errorf("expecting len 1") - } - sl.Insert(1, 1000) - if sl.Len() != 1 { - t.Errorf("expecting len 1") - } - if sl.Get(1).(int) != 1000 { - t.Errorf("expecting sl[1] == 1000") - } - sl.Remove(1) - if sl.Len() != 0 { - t.Errorf("expecting len 0") - } - - sl.Insert(2, 200) - sl.Insert(1, 100) - vs := make([]int, 0) - sl.Do(func(k, v interface{}) bool { - vs = append(vs, k.(int)) - return true - }) - if len(vs) != 2 || vs[0] != 1 || vs[1] != 2 { - t.Errorf("expecting sorted iteration of all keys") - } -} diff --git a/slice.go b/slice.go new file mode 100644 index 0000000..40371db --- /dev/null +++ b/slice.go @@ -0,0 +1,187 @@ +package collections + +type dequeViaRingSlice[T any] struct { + v []T + + front, back, length int +} + +var _ interface{ Deque[int] } = (*dequeViaRingSlice[int])(nil) + +func newDequeViaRingSlice[T any]() *dequeViaRingSlice[T] { + return &dequeViaRingSlice[T]{} +} + +func (d *dequeViaRingSlice[T]) Clear() { + d.v = nil + d.front, d.back, d.length = 0, 0, 0 +} + +func (d *dequeViaRingSlice[T]) PeekBack() (value T, ok bool) { + if d.length == 0 { + return value, false + } + + return d.v[mod(d.back-1, len(d.v))], true +} + +func (d *dequeViaRingSlice[T]) PeekFront() (value T, ok bool) { + if d.length == 0 { + return value, false + } + + return d.v[d.front], true +} + +func (d *dequeViaRingSlice[T]) PopBack() (value T, ok bool) { + value, ok = d.PeekBack() + if !ok { + return value, ok + } + + d.back = mod(d.back-1, len(d.v)) + d.length-- + return value, true +} + +func (d *dequeViaRingSlice[T]) PopFront() (value T, ok bool) { + value, ok = d.PeekFront() + if !ok { + return value, ok + } + + d.front = mod(d.front+1, len(d.v)) + d.length-- + return value, true +} + +func (d *dequeViaRingSlice[T]) PushBack(value T) { + d.maybeGrow() + d.v[d.back] = value + d.back = mod(d.back+1, len(d.v)) + d.length++ +} + +func (d *dequeViaRingSlice[T]) PushFront(value T) { + d.maybeGrow() + d.front = mod(d.front-1, len(d.v)) + d.v[d.front] = value + d.length++ +} + +func (d *dequeViaRingSlice[T]) Size() int { + return d.length +} + +func (d *dequeViaRingSlice[T]) isEmpty() bool { + return d.length == 0 +} + +func (d *dequeViaRingSlice[T]) isFull() bool { + return d.length == len(d.v) +} + +func (d *dequeViaRingSlice[T]) isSparse() bool { + return 1 < d.length && d.length < len(d.v)/4 +} + +func (d *dequeViaRingSlice[T]) maybeGrow() { + if d.length == 0 { + d.resize(1) + return + } + + if d.isFull() { + d.resize(len(d.v) * 2) + } +} + +func (d *dequeViaRingSlice[T]) maybeShrink() { + if d.isSparse() { + d.resize(len(d.v) / 2) + } +} + +func (d *dequeViaRingSlice[T]) resize(size int) { + v := make([]T, size) + for i := 0; i < d.length && i < size; i++ { + v[i] = d.v[(d.front+i)%len(d.v)] + } + d.v = v + d.front = 0 + d.back = d.length +} + +type queueViaSlice[T any] struct { + v []T +} + +var _ interface{ Queue[int] } = (*queueViaSlice[int])(nil) + +func newQueueViaSlice[T any]() *queueViaSlice[T] { + return &queueViaSlice[T]{} +} + +func (q *queueViaSlice[T]) Clear() { + q.v = nil +} + +func (q *queueViaSlice[T]) Peek() (value T, ok bool) { + if len(q.v) > 0 { + return q.v[0], true + } + return value, false +} + +func (q *queueViaSlice[T]) Pop() (value T, ok bool) { + if len(q.v) > 0 { + value, q.v = q.v[0], q.v[1:] + return value, true + } + return value, false +} + +func (q *queueViaSlice[T]) Push(value T) { + q.v = append(q.v, value) +} + +func (q *queueViaSlice[T]) Size() int { + return len(q.v) +} + +type stackViaSlice[T any] struct { + v []T +} + +var _ interface{ Stack[int] } = (*stackViaSlice[int])(nil) + +func newStackViaSlice[T any]() *stackViaSlice[T] { + return &stackViaSlice[T]{} +} + +func (q *stackViaSlice[T]) Clear() { + q.v = nil +} + +func (q *stackViaSlice[T]) Peek() (value T, ok bool) { + if len(q.v) > 0 { + return q.v[len(q.v)-1], true + } + return value, false +} + +func (q *stackViaSlice[T]) Pop() (value T, ok bool) { + if len(q.v) > 0 { + value, q.v = q.v[len(q.v)-1], q.v[:len(q.v)-1] + return value, true + } + return value, false +} + +func (q *stackViaSlice[T]) Push(value T) { + q.v = append(q.v, value) +} + +func (q *stackViaSlice[T]) Size() int { + return len(q.v) +} diff --git a/slice_test.go b/slice_test.go new file mode 100644 index 0000000..e2e7e76 --- /dev/null +++ b/slice_test.go @@ -0,0 +1,36 @@ +package collections + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDequeViaRingSlice(t *testing.T) { + d := newDequeViaRingSlice[int]() + d.PushFront(1) + d.PushBack(2) + assert.Equal(t, 2, d.Size()) + v, ok := d.PopBack() + assert.True(t, ok) + assert.Equal(t, 2, v) + v, ok = d.PopBack() + assert.True(t, ok) + assert.Equal(t, 1, v) + v, ok = d.PopBack() + assert.False(t, ok) + assert.Equal(t, 0, v) + d.PushBack(3) + v, ok = d.PopFront() + assert.True(t, ok) + assert.Equal(t, 3, v) + assert.Equal(t, 0, d.Size()) + + for i := 0; i < 1000; i++ { + d.PushBack(i) + } + for i := 0; i < 1000; i++ { + _, ok := d.PopFront() + assert.True(t, ok) + } +} diff --git a/splay/splay.go b/splay/splay.go deleted file mode 100644 index 0f7a636..0000000 --- a/splay/splay.go +++ /dev/null @@ -1,487 +0,0 @@ -package splay - -import ( - "fmt" -) - -type ( - Any interface{} - LessFunc func(interface{}, interface{}) bool - VisitFunc func(interface{}) bool - - node struct { - value Any - parent, left, right *node - } - nodei struct { - step int - node *node - prev *nodei - } - - SplayTree struct { - length int - root *node - less LessFunc - } -) - -// Create a new splay tree, using the less function to determine the order. -func New(less LessFunc) *SplayTree { - return &SplayTree{0, nil, less} -} - -// Get the first value from the collection. Returns nil if empty. -func (this *SplayTree) First() Any { - if this.length == 0 { - return nil - } - - n := this.root - for n.left != nil { - n = n.left - } - return n.value -} - -// Get the last value from the collection. Returns nil if empty. -func (this *SplayTree) Last() Any { - if this.length == 0 { - return nil - } - n := this.root - for n.right != nil { - n = n.right - } - return n.value -} - -// Get an item from the splay tree -func (this *SplayTree) Get(item Any) Any { - if this.length == 0 { - return nil - } - - n := this.root - for n != nil { - if this.less(item, n.value) { - n = n.left - continue - } - - if this.less(n.value, item) { - n = n.right - continue - } - - this.splay(n) - return n.value - } - return nil -} -func (this *SplayTree) Has(value Any) bool { - return this.Get(value) != nil -} -func (this *SplayTree) Init() { - this.length = 0 - this.root = nil -} -func (this *SplayTree) Add(value Any) { - if this.length == 0 { - this.root = &node{value, nil, nil, nil} - this.length = 1 - return - } - - n := this.root - for { - if this.less(value, n.value) { - if n.left == nil { - n.left = &node{value, n, nil, nil} - this.length++ - n = n.left - break - } - n = n.left - continue - } - - if this.less(n.value, value) { - if n.right == nil { - n.right = &node{value, n, nil, nil} - this.length++ - n = n.right - break - } - n = n.right - continue - } - - n.value = value - break - } - this.splay(n) -} -func (this *SplayTree) PreOrder(visit VisitFunc) { - if this.length == 1 { - return - } - i := &nodei{0, this.root, nil} - for i != nil { - switch i.step { - // Value - case 0: - i.step++ - if !visit(i.node.value) { - break - } - // Left - case 1: - i.step++ - if i.node.left != nil { - i = &nodei{0, i.node.left, i} - } - // Right - case 2: - i.step++ - if i.node.right != nil { - i = &nodei{0, i.node.right, i} - } - default: - i = i.prev - } - } -} -func (this *SplayTree) InOrder(visit VisitFunc) { - if this.length == 1 { - return - } - i := &nodei{0, this.root, nil} - for i != nil { - switch i.step { - // Left - case 0: - i.step++ - if i.node.left != nil { - i = &nodei{0, i.node.left, i} - } - // Value - case 1: - i.step++ - if !visit(i.node.value) { - break - } - // Right - case 2: - i.step++ - if i.node.right != nil { - i = &nodei{0, i.node.right, i} - } - default: - i = i.prev - } - } -} -func (this *SplayTree) PostOrder(visit VisitFunc) { - if this.length == 1 { - return - } - i := &nodei{0, this.root, nil} - for i != nil { - switch i.step { - // Left - case 0: - i.step++ - if i.node.left != nil { - i = &nodei{0, i.node.left, i} - } - // Right - case 1: - i.step++ - if i.node.right != nil { - i = &nodei{0, i.node.right, i} - } - // Value - case 2: - i.step++ - if !visit(i.node.value) { - break - } - default: - i = i.prev - } - } -} -func (this *SplayTree) Do(visit VisitFunc) { - this.InOrder(visit) -} -func (this *SplayTree) Len() int { - return this.length -} -func (this *SplayTree) Remove(value Any) { - if this.length == 0 { - return - } - - n := this.root - for n != nil { - if this.less(value, n.value) { - n = n.left - continue - } - if this.less(n.value, value) { - n = n.right - continue - } - - // First splay the parent node - if n.parent != nil { - this.splay(n.parent) - } - - // No children - if n.left == nil && n.right == nil { - // guess we're the root node - if n.parent == nil { - this.root = nil - break - } - if n.parent.left == n { - n.parent.left = nil - } else { - n.parent.right = nil - } - } else if n.left == nil { - // root node - if n.parent == nil { - this.root = n.right - break - } - if n.parent.left == n { - n.parent.left = n.right - } else { - n.parent.right = n.right - } - } else if n.right == nil { - // root node - if n.parent == nil { - this.root = n.left - break - } - if n.parent.left == n { - n.parent.left = n.left - } else { - n.parent.right = n.left - } - } else { - // find the successor - s := n.right - for s.left != nil { - s = s.left - } - - np := n.parent - nl := n.left - nr := n.right - - sp := s.parent - sr := s.right - - // Update parent - s.parent = np - if np == nil { - this.root = s - } else { - if np.left == n { - np.left = s - } else { - np.right = s - } - } - - // Update left - s.left = nl - s.left.parent = s - - // Update right - if nr != s { - s.right = nr - s.right.parent = s - } - - // Update successor parent - if sp.left == s { - sp.left = sr - } else { - sp.right = sr - } - } - - break - } - - if n != nil { - this.length-- - } -} -func (this *SplayTree) String() string { - if this.length == 0 { - return "{}" - } - return this.root.String() -} - -// Splay a node in the tree (send it to the top) -func (this *SplayTree) splay(n *node) { - // Already root, nothing to do - if n.parent == nil { - this.root = n - return - } - - p := n.parent - g := p.parent - - // Zig - if p == this.root { - if n == p.left { - p.rotateRight() - } else { - p.rotateLeft() - } - } else { - // Zig-zig - if n == p.left && p == g.left { - g.rotateRight() - p.rotateRight() - } else if n == p.right && p == g.right { - g.rotateLeft() - p.rotateLeft() - // Zig-zag - } else if n == p.right && p == g.left { - p.rotateLeft() - g.rotateRight() - } else if n == p.left && p == g.right { - p.rotateRight() - g.rotateLeft() - } - } - this.splay(n) -} - -// Swap two nodes in the tree -func (this *SplayTree) swap(n1, n2 *node) { - p1 := n1.parent - l1 := n1.left - r1 := n1.right - - p2 := n2.parent - l2 := n2.left - r2 := n2.right - - // Update node links - n1.parent = p2 - n1.left = l2 - n1.right = r2 - - n2.parent = p1 - n2.left = l1 - n2.right = r1 - - // Update parent links - if p1 != nil { - if p1.left == n1 { - p1.left = n2 - } else { - p1.right = n2 - } - } - if p2 != nil { - if p2.left == n2 { - p2.left = n1 - } else { - p2.right = n1 - } - } - - if n1 == this.root { - this.root = n2 - } else if n2 == this.root { - this.root = n1 - } -} - -// Node methods -func (this *node) String() string { - str := "{" + fmt.Sprint(this.value) + "|" - if this.left != nil { - str += this.left.String() - } - str += "|" - if this.right != nil { - str += this.right.String() - } - str += "}" - return str -} -func (this *node) rotateLeft() { - parent := this.parent - pivot := this.right - child := pivot.left - - if pivot == nil { - return - } - - // Update the parent - if parent != nil { - if parent.left == this { - parent.left = pivot - } else { - parent.right = pivot - } - } - - // Update the pivot - pivot.parent = parent - pivot.left = this - - // Update the child - if child != nil { - child.parent = this - } - - // Update this - this.parent = pivot - this.right = child -} -func (this *node) rotateRight() { - parent := this.parent - pivot := this.left - child := pivot.right - - if pivot == nil { - return - } - - // Update the parent - if parent != nil { - if parent.left == this { - parent.left = pivot - } else { - parent.right = pivot - } - } - - // Update the pivot - pivot.parent = parent - pivot.right = this - - if child != nil { - child.parent = this - } - - // Update this - this.parent = pivot - this.left = child -} diff --git a/splay/splay_test.go b/splay/splay_test.go deleted file mode 100644 index c832a1c..0000000 --- a/splay/splay_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package splay - -import ( - //"fmt" - "testing" -) - -func Test(t *testing.T) { - tree := New(func(a,b interface{})bool { - return a.(string) < b.(string) - }) - - tree.Insert("d", 4) - tree.Insert("b", 2) - tree.Insert("a", 1) - tree.Insert("c", 3) - - if tree.Len() != 4 { - t.Errorf("expecting len 4") - } - - tree.Remove("b") - - if tree.Len() != 3 { - t.Errorf("expecting len 3") - } -} \ No newline at end of file diff --git a/stack/stack.go b/stack/stack.go deleted file mode 100644 index 11f472a..0000000 --- a/stack/stack.go +++ /dev/null @@ -1,44 +0,0 @@ -package stack - -type ( - Stack struct { - top *node - length int - } - node struct { - value interface{} - prev *node - } -) -// Create a new stack -func New() *Stack { - return &Stack{nil,0} -} -// Return the number of items in the stack -func (this *Stack) Len() int { - return this.length -} -// View the top item on the stack -func (this *Stack) Peek() interface{} { - if this.length == 0 { - return nil - } - return this.top.value -} -// Pop the top item of the stack and return it -func (this *Stack) Pop() interface{} { - if this.length == 0 { - return nil - } - - n := this.top - this.top = n.prev - this.length-- - return n.value -} -// Push a value onto the top of the stack -func (this *Stack) Push(value interface{}) { - n := &node{value,this.top} - this.top = n - this.length++ -} \ No newline at end of file diff --git a/stack/stack_test.go b/stack/stack_test.go deleted file mode 100644 index e3c8d71..0000000 --- a/stack/stack_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package stack - -import ( - "testing" -) - -func Test(t *testing.T) { - s := New() - - if s.Len() != 0 { - t.Errorf("Length of an empty stack should be 0") - } - - s.Push(1) - - if s.Len() != 1 { - t.Errorf("Length should be 0") - } - - if s.Peek().(int) != 1 { - t.Errorf("Top item on the stack should be 1") - } - - if s.Pop().(int) != 1 { - t.Errorf("Top item should have been 1") - } - - if s.Len() != 0 { - t.Errorf("Stack should be empty") - } - - s.Push(1) - s.Push(2) - - if s.Len() != 2 { - t.Errorf("Length should be 2") - } - - if s.Peek().(int) != 2 { - t.Errorf("Top of the stack should be 2") - } -} \ No newline at end of file diff --git a/trie/trie.go b/trie/trie.go deleted file mode 100644 index 8b4583f..0000000 --- a/trie/trie.go +++ /dev/null @@ -1,148 +0,0 @@ -package trie - -import ( - "fmt" -) - -type ( - Trie struct { - root *node - size int - } - node struct { - key interface{} - value interface{} - next [256]*node - } - iterator struct { - step int - node *node - prev *iterator - } -) - -func toBytes(obj interface{}) []byte { - switch o := obj.(type) { - case []byte: - return o - case string: - return []byte(o) - } - return []byte(fmt.Sprint(obj)) -} - -func New() *Trie { - return &Trie{nil,0} -} -func (this *Trie) Do(handler func(interface{},interface{})bool) { - if this.size > 0 { - this.root.do(handler) - } -} -func (this *Trie) Get(key interface{}) interface{} { - if this.size == 0 { - return nil - } - - bs := toBytes(key) - cur := this.root - for i := 0; i < len(bs); i++ { - if cur.next[bs[i]] != nil { - cur = cur.next[bs[i]] - } else { - return nil - } - } - return cur.value -} -func (this *Trie) Has(key interface{}) bool { - return this.Get(key) != nil -} -func (this *Trie) Init() { - this.root = nil - this.size = 0 -} -func (this *Trie) Insert(key interface{}, value interface{}) { - if this.size == 0 { - this.root = newNode() - } - - bs := toBytes(key) - cur := this.root - for i := 0; i < len(bs); i++ { - if cur.next[bs[i]] != nil { - cur = cur.next[bs[i]] - } else { - cur.next[bs[i]] = newNode() - cur = cur.next[bs[i]] - } - } - if cur.key == nil { - this.size++ - } - cur.key = key - cur.value = value -} -func (this *Trie) Len() int { - return this.size -} -func (this *Trie) Remove(key interface{}) interface{} { - if this.size == 0 { - return nil - } - bs := toBytes(key) - cur := this.root - - for i := 0; i < len(bs); i++ { - if cur.next[bs[i]] != nil { - cur = cur.next[bs[i]] - } else { - return nil - } - } - - // TODO: cleanup dead nodes - - val := cur.value - - if cur.value != nil { - this.size-- - cur.value = nil - cur.key = nil - } - return val -} -func (this *Trie) String() string { - str := "{" - i := 0 - this.Do(func(k, v interface{}) bool { - if i > 0 { - str += ", " - } - str += fmt.Sprint(k, ":", v) - i++ - return true - }) - str += "}" - return str -} - -func newNode() *node { - var next [256]*node - return &node{nil,nil,next} -} -func (this *node) do(handler func(interface{}, interface{}) bool) bool { - for i := 0; i < 256; i++ { - if this.next[i] != nil { - if this.next[i].key != nil { - if !handler(this.next[i].key, this.next[i].value) { - return false - } - } - if !this.next[i].do(handler) { - return false - } - } - } - return true -} \ No newline at end of file diff --git a/trie/trie_test.go b/trie/trie_test.go deleted file mode 100644 index 9cd44ff..0000000 --- a/trie/trie_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package trie - -import ( - //"fmt" - "testing" -) - -func Test(t *testing.T) { - x := New() - x.Insert(1, 100) - if x.Len() != 1 { - t.Errorf("expected len 1") - } - if x.Get(1).(int) != 100 { - t.Errorf("expected to get 100 for 1") - } - x.Remove(1) - if x.Len() != 0 { - t.Errorf("expected len 0") - } - x.Insert(2, 200) - x.Insert(1, 100) - vs := make([]int, 0) - x.Do(func(k, v interface{}) bool { - vs = append(vs, k.(int)) - return true - }) - if len(vs) != 2 || vs[0] != 1 || vs[1] != 2 { - t.Errorf("expected in order traversal") - } -} \ No newline at end of file diff --git a/tst/tst.go b/tst/tst.go deleted file mode 100644 index 64ea8f3..0000000 --- a/tst/tst.go +++ /dev/null @@ -1,306 +0,0 @@ -package tst - -import ( - "fmt" -) - -type ( - Node struct { - key byte - value interface{} - left, middle, right *Node - } - NodeIterator struct { - step int - node *Node - prev *NodeIterator - } - TernarySearchTree struct { - length int - root *Node - } -) - -// Create a new ternary search tree -func New() *TernarySearchTree { - tree := &TernarySearchTree{} - tree.Init() - return tree -} -// Iterate over the collection -func (this *TernarySearchTree) Do(callback func(string, interface{})bool) { - if this.Len() == 0 { - return - } - bs := []byte{} - i := &NodeIterator{0,this.root,nil} - for i != nil { - switch i.step { - // Left - case 0: - i.step++ - if i.node.left != nil { - i = &NodeIterator{0,i.node.left,i} - continue - } - // Value - case 1: - i.step++ - if i.node.key > 0 { - bs = append(bs, i.node.key) - } - if i.node.value != nil { - if !callback(string(bs), i.node.value) { - return - } - continue - } - // Middle - case 2: - i.step++ - if i.node.middle != nil { - i = &NodeIterator{0,i.node.middle,i} - continue - } - // Right - case 3: - if len(bs) > 0 { - bs = bs[:len(bs)-1] - } - i.step++ - if i.node.right != nil { - i = &NodeIterator{0,i.node.right,i} - continue - } - // Backtrack - case 4: - i = i.prev - } - } -} -// Get the value at the specified key. Returns nil if not found. -func (this *TernarySearchTree) Get(key string) interface{} { - if this.length == 0 { - return nil - } - - node := this.root - bs := []byte(key) - for i := 0; i < len(bs); { - b := bs[i] - if b > node.key { - if node.right == nil { - return nil - } - node = node.right - } else if (b < node.key) { - if node.left == nil { - return nil - } - node = node.left - } else { - i++ - if i < len(bs) { - if node.middle == nil { - return nil - } - node = node.middle - } else { - break - } - } - } - return node.value -} -func (this *TernarySearchTree) GetLongestPrefix(key string) interface{} { - if this.length == 0 { - return nil - } - - n := this.root - v := n.value - bs := []byte(key) - for i := 0; i < len(bs); { - b := bs[i] - if n.value != nil { - v = n.value - } - if b > n.key { - if n.right == nil { - break - } - n = n.right - } else if b < n.key { - if n.left == nil { - break - } - n = n.left - } else { - i++ - if i < len(bs) { - if n.middle == nil { - break - } - n = n.middle - } else { - break - } - } - } - if n.value != nil { - v = n.value - } - return v -} -// Test to see whether or not the given key is contained in the tree. -func (this *TernarySearchTree) Has(key string) bool { - return this.Get(key) != nil -} -// Initialize the tree (reset it so that it's empty). New will do this for you. -func (this *TernarySearchTree) Init() { - this.length = 0 - this.root = nil -} -// Insert a new key value pair into the collection -func (this *TernarySearchTree) Insert(key string, value interface{}) { - // If the value is nil then remove this key from the collection - if value == nil { - this.Remove(key) - return - } - - if this.length == 0 { - this.root = &Node{0,nil,nil,nil,nil} - } - - t := this.root - bs := []byte(key) - for i := 0; i < len(bs); { - b := bs[i] - if b > t.key { - if t.right == nil { - t.right = &Node{b,nil,nil,nil,nil} - } - t = t.right - } else if b < t.key { - if t.left == nil { - t.left = &Node{b,nil,nil,nil,nil} - } - t = t.left - } else { - i++ - if i < len(bs) { - if t.middle == nil { - t.middle = &Node{bs[i],nil,nil,nil,nil} - } - t = t.middle - } - } - } - - if t.value == nil { - this.length++ - } - t.value = value -} -// Get the number of items stored in the tree -func (this *TernarySearchTree) Len() int { - return this.length -} -// Remove a key from the collection -func (this *TernarySearchTree) Remove(key string) interface{} { - if this.length == 0 { - return nil - } - - var remove *Node - var direction int - - t := this.root - bs := []byte(key) - for i := 0; i < len(bs); { - b := bs[i] - if b > t.key { - // Not in the collection - if t.right == nil { - return nil - } - // This is a branch so we have to keep it - remove = t - direction = 1 - // Move to the next node - t = t.right - } else if b < t.key { - // Not in the collection - if t.left == nil { - return nil - } - // This is a branch so we have to keep it - remove = t - direction = -1 - // Move to the next node - t = t.left - } else { - i++ - if i < len(bs) { - // Not in the collection - if t.middle == nil { - return nil - } - // Has a value so we need to keep at least this much - if t.value != nil { - remove = t - direction = 0 - } - // Move to the next node - t = t.middle - } - } - } - - // If this was the only item in the tree, set the root pointer to nil - if this.length == 1 { - this.root = nil - } else { - if direction == -1 { - remove.left = nil - } else if direction == 0 { - remove.middle = nil - } else { - remove.right = nil - } - } - this.length-- - return t.value -} -func (this *TernarySearchTree) String() string { - if this.length == 0 { - return "{}" - } - - return this.root.String() -} -// Dump the tree to a string for easier debugging -func (this *Node) String() string { - str := "{" + string(this.key) - if this.value != nil { - str += ":" + fmt.Sprint(this.value) - } - if this.left != nil { - str += this.left.String() - } else { - str += " " - } - if this.middle != nil { - str += this.middle.String() - } else { - str += " " - } - if this.right != nil { - str += this.right.String() - } else { - str += " " - } - str += "}" - return str -} diff --git a/tst/tst_test.go b/tst/tst_test.go deleted file mode 100644 index 2e77157..0000000 --- a/tst/tst_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package tst - -import ( - //"fmt" - "math/rand" - "testing" -) - -func randomString() string { - n := 3 + rand.Intn(10) - bs := make([]byte, n) - for i := 0; i Date: Mon, 21 Mar 2022 10:20:10 -0600 Subject: [PATCH 2/8] github workflows --- .github/workflows/lint.yaml | 20 ++++++++++++++ .github/workflows/test.yaml | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..0903b34 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,20 @@ +name: Lint +on: + push: + tags: + - v* + branches: + - main + pull_request: +jobs: + golangci: + name: Go + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Lint + uses: golangci/golangci-lint-action@v2 + with: + version: latest diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..4ad15d0 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,53 @@ +name: Test +on: + push: + tags: + - v* + branches: + - main + pull_request: +jobs: + test: + name: Go + runs-on: ubuntu-latest + steps: + - name: Install go + uses: actions/setup-go@v3 + with: + go-version: "1.18" + + - id: go-cache-paths + run: | + echo "::set-output name=go-build::$(go env GOCACHE)" + echo "::set-output name=go-mod::$(go env GOMODCACHE)" + + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache go build + uses: actions/cache@v3 + with: + path: ${{ steps.go-cache-paths.outputs.go-build }} + key: ${{ runner.os }}-go-build + + - name: Cache go modules + uses: actions/cache@v3 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod }} + key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go-mod- + + - name: Download go modules + run: | + go mod download + + - name: Run tests + run: | + go test -race -covermode atomic -coverprofile=covprofile ./... + + - name: Send coverage + env: + COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + go run github.com/mattn/goveralls -coverprofile=covprofile -service=github From b6c437f5c496a3cd88ad9e7af357593d09f2f4de Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 10:23:16 -0600 Subject: [PATCH 3/8] add goveralls --- go.mod | 4 ++++ go.sum | 20 ++++++++++++++++++++ tools.go | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100644 tools.go diff --git a/go.mod b/go.mod index 4aa810b..57f2819 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/badgerodon/collections go 1.18 require ( + github.com/mattn/goveralls v0.0.11 github.com/stretchr/testify v1.7.1 github.com/tidwall/btree v1.2.0 ) @@ -10,5 +11,8 @@ require ( require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/tools v0.1.1 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index bea72a5..a6a7e1d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/mattn/goveralls v0.0.11 h1:eJXea6R6IFlL1QMKNMzDvvHv/hwGrnvyig4N+0+XiMM= +github.com/mattn/goveralls v0.0.11/go.mod h1:gU8SyhNswsJKchEV93xRQxX6X3Ei4PJdQk/6ZHvrvRk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -7,6 +9,24 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/btree v1.2.0 h1:cXmC8TB0xxua4tmOf+cLhi4KE59e2075IXB/KRjEhYQ= github.com/tidwall/btree v1.2.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..57177f0 --- /dev/null +++ b/tools.go @@ -0,0 +1,7 @@ +//go:build tools + +package collections + +import ( + _ "github.com/mattn/goveralls" // used for code coverage +) From 295bd45f2beb2908002c512f8a3feab0752e26b4 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 10:25:47 -0600 Subject: [PATCH 4/8] try reviewdog? --- .github/workflows/lint.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0903b34..20a752e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -11,10 +11,8 @@ jobs: name: Go runs-on: ubuntu-latest steps: - - name: Checkout + - name: checkout uses: actions/checkout@v3 - - name: Lint - uses: golangci/golangci-lint-action@v2 - with: - version: latest + - name: golangci-lint + uses: reviewdog/action-golangci-lint@v2 From 4b09546ad2806d8679869d84173a6b3c16568bdb Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 11:02:39 -0600 Subject: [PATCH 5/8] workflow --- .github/workflows/lint.yaml | 18 ------------------ .github/workflows/test.yaml | 17 ++++++++++++----- tools.go | 7 ------- 3 files changed, 12 insertions(+), 30 deletions(-) delete mode 100644 .github/workflows/lint.yaml delete mode 100644 tools.go diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index 20a752e..0000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Lint -on: - push: - tags: - - v* - branches: - - main - pull_request: -jobs: - golangci: - name: Go - runs-on: ubuntu-latest - steps: - - name: checkout - uses: actions/checkout@v3 - - - name: golangci-lint - uses: reviewdog/action-golangci-lint@v2 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4ad15d0..dd9466d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -38,16 +38,23 @@ jobs: restore-keys: | ${{ runner.os }}-go-mod- - - name: Download go modules + - name: install dependencies run: | - go mod download + go install honnef.co/go/tools/cmd/staticcheck@latest + go install github.com/mattn/goveralls@latest - - name: Run tests + - name: lint + run: | + go mod verify ./... + go vet ./... + staticcheck ./... + + - name: test run: | go test -race -covermode atomic -coverprofile=covprofile ./... - - name: Send coverage + - name: code-coverage env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - go run github.com/mattn/goveralls -coverprofile=covprofile -service=github + goveralls -coverprofile=covprofile -service=github diff --git a/tools.go b/tools.go deleted file mode 100644 index 57177f0..0000000 --- a/tools.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build tools - -package collections - -import ( - _ "github.com/mattn/goveralls" // used for code coverage -) From d05c5f4903443d2518359fc7f306dda07854497e Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 11:03:33 -0600 Subject: [PATCH 6/8] wip --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index dd9466d..921c98d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -45,7 +45,7 @@ jobs: - name: lint run: | - go mod verify ./... + go mod verify go vet ./... staticcheck ./... From dcf73996986c328b75241b8850910e076de8c419 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 11:05:42 -0600 Subject: [PATCH 7/8] wip --- .github/workflows/test.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 921c98d..5e17885 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -40,14 +40,12 @@ jobs: - name: install dependencies run: | - go install honnef.co/go/tools/cmd/staticcheck@latest go install github.com/mattn/goveralls@latest - name: lint run: | go mod verify go vet ./... - staticcheck ./... - name: test run: | From 1a974f77b9c192a65bb2fc9b97b862b1866eae3a Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 21 Mar 2022 11:15:04 -0600 Subject: [PATCH 8/8] wip --- .github/workflows/test.yaml | 45 ++++++++++--------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5e17885..70b96ee 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,48 +11,27 @@ jobs: name: Go runs-on: ubuntu-latest steps: - - name: Install go + - name: install-go uses: actions/setup-go@v3 with: go-version: "1.18" - - id: go-cache-paths - run: | - echo "::set-output name=go-build::$(go env GOCACHE)" - echo "::set-output name=go-mod::$(go env GOMODCACHE)" - - - name: Checkout + - name: checkout uses: actions/checkout@v3 - - name: Cache go build - uses: actions/cache@v3 - with: - path: ${{ steps.go-cache-paths.outputs.go-build }} - key: ${{ runner.os }}-go-build - - - name: Cache go modules - uses: actions/cache@v3 - with: - path: ${{ steps.go-cache-paths.outputs.go-mod }} - key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go-mod- - - - name: install dependencies - run: | - go install github.com/mattn/goveralls@latest + - name: mod-verify + run: go mod verify - - name: lint - run: | - go mod verify - go vet ./... + - name: vet + run: go vet ./... - name: test - run: | - go test -race -covermode atomic -coverprofile=covprofile ./... + run: go test -race -covermode atomic -coverprofile=covprofile ./... + + - name: install-goveralls + run: go install github.com/mattn/goveralls@latest - - name: code-coverage + - name: run-goveralls env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - goveralls -coverprofile=covprofile -service=github + run: goveralls -coverprofile=covprofile -service=github