Skip to content

Commit

Permalink
Dev compare and equal constraint types (#36)
Browse files Browse the repository at this point in the history
* add constrained type constructor for Graph

* add constrained type constructor for Queue

* add constrained type constructor for Deque

* add constrained type constructor for List

* add constrained type constructor for Map

* add constrained type constructor for tree based Map and Set

* add constrained type constructor for Stack

* add doc to constraint interfaces

* inline newRbTreeOfComp

* make Map constructors dry
  • Loading branch information
Cause Chung authored Dec 10, 2022
1 parent e2f9afd commit 2eccd05
Show file tree
Hide file tree
Showing 23 changed files with 348 additions and 62 deletions.
14 changes: 11 additions & 3 deletions collections/array_stack.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package collections

import (
"github.com/cuzfrog/tgods/funcs"
"github.com/cuzfrog/tgods/types"
"github.com/cuzfrog/tgods/utils"
)

// arrayStack limited size array based stack
type arrayStack[T comparable] struct {
type arrayStack[T any] struct {
arr []T
cur int
eq types.Equal[T]
}

func newArrayStack[T comparable](size int) *arrayStack[T] {
arr := make([]T, size)
return &arrayStack[T]{arr, -1}
return &arrayStack[T]{arr, -1, funcs.ValueEqual[T]}
}

func newArrayStackOfEq[T any](size int, eq types.Equal[T]) *arrayStack[T] {
arr := make([]T, size)
return &arrayStack[T]{arr, -1, eq}
}

func (s *arrayStack[T]) Size() int {
Expand Down Expand Up @@ -58,7 +66,7 @@ func (s *arrayStack[T]) Peek() (elem T, found bool) {

func (s *arrayStack[T]) Contains(elem T) bool {
for i := 0; i <= s.cur; i++ {
if s.arr[i] == elem {
if s.eq(s.arr[i], elem) {
return true
}
}
Expand Down
8 changes: 6 additions & 2 deletions collections/circular_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type circularArrayForSort[T any] struct {
// newCircularArrayOf creates an auto expandable circular array based list, auto shrinkable, but will not shrink if the length is <= defaultArrInitSize,
// the underlying array will be lazily created unless init values are provided, the init arr size is the same as init values'
func newCircularArrayOf[T comparable](values ...T) *circularArray[T] {
return newCircularArrayOfP[T](funcs.ValueEqual[T], AutoExpand+AutoShrink, values...)
}

func newCircularArrayOfP[T any](eq types.Equal[T], flag AutoSizingFlag, values ...T) *circularArray[T] {
var arr []T
var size, start int
length := len(values)
Expand All @@ -39,15 +43,15 @@ func newCircularArrayOf[T comparable](values ...T) *circularArray[T] {
size = length
start = 0
}
return &circularArray[T]{start, size, arr, size, funcs.ValueEqual[T], list, AutoExpand + AutoShrink}
return &circularArray[T]{start, size, arr, size, eq, list, flag}
}

// newCircularArray creates underlying array eagerly with the init cap
func newCircularArray[T comparable](initCap int, flag AutoSizingFlag) *circularArray[T] {
return &circularArray[T]{-1, 0, make([]T, initCap), 0, funcs.ValueEqual[T], list, flag}
}

// newCircularArrayOfEq creates underlying array eagerly with the init cap
// newCircularArrayOfEq creates underlying array eagerly with the init cap, AutoExpand + AutoShrink
func newCircularArrayOfEq[T any](initCap int, eq types.Equal[T]) *circularArray[T] {
return &circularArray[T]{-1, 0, make([]T, initCap), 0, eq, list, AutoExpand + AutoShrink}
}
Expand Down
8 changes: 8 additions & 0 deletions collections/defs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ func (s *intStruct) Hash() uint {
func (s *intStruct) Equal(other *intStruct) bool {
return s.v == other.v
}

func (s *intStruct) Compare(other *intStruct) int8 {
return funcs.ValueCompare(s.v, other.v)
}

func intStructCompare(s1, s2 *intStruct) int8 {
return s1.Compare(s2)
}
20 changes: 20 additions & 0 deletions collections/deques.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ func NewLinkedListDeque[T comparable]() types.Deque[T] {
return newLinkedListOf[T]().withRole(deque)
}

func NewLinkedListDequeC[T types.WithEqual[T]]() types.Deque[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newLinkedListOfEq[T](eq).withRole(deque)
}

func NewLinkedListDequeOfEq[T any](eq types.Equal[T]) types.Deque[T] {
return newLinkedListOfEq[T](eq).withRole(deque)
}
Expand All @@ -14,14 +19,29 @@ func NewArrayListDeque[T comparable]() types.Deque[T] {
return newCircularArrayOf[T]().withRole(deque)
}

func NewArrayListDequeC[T types.WithEqual[T]]() types.Deque[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfEq[T](0, eq).withRole(deque)
}

func NewArrayListDequeOfSize[T comparable](initCap int) types.Deque[T] {
return newCircularArray[T](initCap, AutoExpand+AutoShrink).withRole(deque)
}

func NewArrayListDequeOfSizeC[T types.WithEqual[T]](initCap int) types.Deque[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfEq[T](initCap, eq).withRole(deque)
}

func NewArrayListDequeOfSizeP[T comparable](initCap int, autoSizingFlag AutoSizingFlag) types.Deque[T] {
return newCircularArray[T](initCap, autoSizingFlag).withRole(deque)
}

func NewArrayListDequeOfSizePC[T types.WithEqual[T]](initCap int, autoSizingFlag AutoSizingFlag) types.Deque[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfEqP[T](initCap, eq, autoSizingFlag).withRole(deque)
}

func NewArrayListDequeOfEq[T any](initCap int, eq types.Equal[T]) types.Deque[T] {
return newCircularArrayOfEq[T](initCap, eq).withRole(deque)
}
Expand Down
20 changes: 20 additions & 0 deletions collections/deques_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,23 @@ func TestDequeProperties(t *testing.T) {
})
}
}

func TestDequeConstraintTypeConstructors(t *testing.T) {
tests := []struct {
name string
q types.Deque[*intStruct]
}{
{"linkedList1", NewLinkedListDequeC[*intStruct]()},
{"arrayList1", NewArrayListDequeC[*intStruct]()},
{"arrayList2", NewArrayListDequeOfSizeC[*intStruct](10)},
{"arrayList4", NewArrayListDequeOfSizePC[*intStruct](10, NoAutoSizing)},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
q := test.q
v1 := &intStruct{1}
q.Enqueue(v1)
assert.True(t, q.Contains(v1))
})
}
}
6 changes: 6 additions & 0 deletions collections/graphs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ import "github.com/cuzfrog/tgods/types"
func NewTreeAdjacencyGraph[V any, E any](comp types.Compare[V]) types.Graph[V, E] {
return newTreeAdjacencyList[V, E](comp)
}

// NewTreeAdjacencyGraphC creates a treeMap based adjacency list graph with a constrained vertex type that implements custom Hash and Equal
func NewTreeAdjacencyGraphC[V types.WithCompare[V], E any]() types.Graph[V, E] {
comp := func(v, o V) int8 { return v.Compare(o) }
return newTreeAdjacencyList[V, E](comp)
}
28 changes: 28 additions & 0 deletions collections/graphs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package collections

import (
"github.com/cuzfrog/tgods/types"
"github.com/stretchr/testify/assert"
"testing"
)

func TestGraphConstructors(t *testing.T) {
tests := []struct {
name string
g types.Graph[*intStruct, int]
}{
{"treeMapGraph1", NewTreeAdjacencyGraph[*intStruct, int](intStructCompare)},
{"treeMapGraph2", NewTreeAdjacencyGraphC[*intStruct, int]()},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
g := test.g
v3 := &intStruct{3}
v5 := &intStruct{5}
g.Add(v3)
g.Add(v5)
g.Connect(v3, v5, 35)
assert.Equal(t, 1, g.OutwardCount(v3))
})
}
}
8 changes: 6 additions & 2 deletions collections/hash_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ type hashMap[K any, V any] struct {
hashTable[types.Entry[K, V]]
}

func newHashMap[K any, V any](hs types.Hash[K], eq types.Equal[K]) *hashMap[K, V] {
func newHashMap[K any, V any](hs types.Hash[K], eq types.Equal[K], entries ...types.Entry[K, V]) *hashMap[K, V] {
hhs := func(a types.Entry[K, V]) uint { return hs(a.Key()) }
heq := func(a, b types.Entry[K, V]) bool { return eq(a.Key(), b.Key()) }
h := newHashTable[types.Entry[K, V]](hhs, heq)
return &hashMap[K, V]{*h}
m := &hashMap[K, V]{*h}
for _, e := range entries {
m.Put(e.Key(), e.Value())
}
return m
}

func (h *hashMap[K, V]) Get(k K) (V, bool) {
Expand Down
2 changes: 1 addition & 1 deletion collections/iterators.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (s *arrayStack[T]) Iterator() types.Iterator[T] {
return &arrayStackIterator[T]{s, s.cur + 1}
}

type arrayStackIterator[T comparable] struct {
type arrayStackIterator[T any] struct {
s *arrayStack[T]
cur int
}
Expand Down
8 changes: 6 additions & 2 deletions collections/linked_hash_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ type linkedHashMap[K any, V any] struct {
limit int // the maximum size limit, 0 means unlimited
}

func newLinkedHashMap[K any, V any](hs types.Hash[K], eq types.Equal[K], sizeLimit int, accessOrder AccessOrder) *linkedHashMap[K, V] {
func newLinkedHashMap[K any, V any](hs types.Hash[K], eq types.Equal[K], sizeLimit int, accessOrder AccessOrder, entries ...types.Entry[K, V]) *linkedHashMap[K, V] {
hhs := func(a types.Entry[K, V]) uint { return hs(a.Key()) }
heq := func(a, b types.Entry[K, V]) bool { return eq(a.Key(), b.Key()) }
h := newLinkedHashTable[types.Entry[K, V]](hhs, heq, accessOrder)
return &linkedHashMap[K, V]{*h, sizeLimit}
m := &linkedHashMap[K, V]{*h, sizeLimit}
for _, e := range entries {
m.Put(e.Key(), e.Value())
}
return m
}

func (h *linkedHashMap[K, V]) Get(k K) (V, bool) {
Expand Down
39 changes: 33 additions & 6 deletions collections/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,59 @@ func NewArrayListOf[T comparable](values ...T) types.ArrayList[T] {
return newCircularArrayOf[T](values...).withRole(list)
}

// NewArrayListOfC creates an auto expandable/shrinkable circular array based list with init values of a constrained type,
// the underlying array will be lazily created if init values are not provided.
// 'C' stands for Client Customized Constrained type.
func NewArrayListOfC[T types.WithEqual[T]](values ...T) types.ArrayList[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfP[T](eq, AutoExpand+AutoShrink, values...).withRole(list)
}

// NewArrayListOfSize creates an auto expandable/shrinkable circular array based list with init cap
func NewArrayListOfSize[T comparable](initCap int) types.ArrayList[T] {
return newCircularArray[T](initCap, AutoExpand+AutoShrink).withRole(list)
}

// NewArrayListOfSizeC creates an auto expandable/shrinkable circular array based list with init cap
func NewArrayListOfSizeC[T types.WithEqual[T]](initCap int) types.ArrayList[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfEq[T](initCap, eq).withRole(list)
}

// NewArrayListOfSizeP creates a circular array based list with init cap
func NewArrayListOfSizeP[T comparable](initCap int, flag AutoSizingFlag) types.ArrayList[T] {
return newCircularArray[T](initCap, flag).withRole(list)
}

// NewArrayListOfSizePC creates a circular array based list with init cap
func NewArrayListOfSizePC[T types.WithEqual[T]](initCap int, flag AutoSizingFlag) types.ArrayList[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newCircularArrayOfEqP[T](initCap, eq, flag).withRole(list)
}

// NewArrayListOfEq creates an auto expandable/shrinkable circular array based list with init cap and an Equal func
func NewArrayListOfEq[T any](initCap int, comp types.Equal[T]) types.ArrayList[T] {
return newCircularArrayOfEq(initCap, comp).withRole(list)
func NewArrayListOfEq[T any](initCap int, eq types.Equal[T]) types.ArrayList[T] {
return newCircularArrayOfEq(initCap, eq).withRole(list)
}

// NewArrayListOfEqP creates a circular array based list with init cap and an Equal func
func NewArrayListOfEqP[T any](initCap int, comp types.Equal[T], flag AutoSizingFlag) types.ArrayList[T] {
return newCircularArrayOfEqP(initCap, comp, flag).withRole(list)
func NewArrayListOfEqP[T any](initCap int, eq types.Equal[T], flag AutoSizingFlag) types.ArrayList[T] {
return newCircularArrayOfEqP(initCap, eq, flag).withRole(list)
}

// NewLinkedListOf creates a linked list with init values
func NewLinkedListOf[T comparable](values ...T) types.LinkedList[T] {
return newLinkedListOf[T](values...).withRole(list)
}

// NewLinkedListOfC creates a linked list with init values
// 'C' stands for Client Customized Constrained type.
func NewLinkedListOfC[T types.WithEqual[T]](values ...T) types.LinkedList[T] {
eq := func(a, b T) bool { return a.Equal(b) }
return newLinkedListOfEq[T](eq, values...).withRole(list)
}

// NewLinkedListOfEq creates a linked list with an Equal func and init values
func NewLinkedListOfEq[T any](equal types.Equal[T], values ...T) types.LinkedList[T] {
return newLinkedListOfEq[T](equal, values...).withRole(list)
func NewLinkedListOfEq[T any](eq types.Equal[T], values ...T) types.LinkedList[T] {
return newLinkedListOfEq[T](eq, values...).withRole(list)
}
20 changes: 20 additions & 0 deletions collections/lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,23 @@ func TestListProperties(t *testing.T) {
})
}
}

func TestListConstraintTypeConstructors(t *testing.T) {
tests := []struct {
name string
q types.List[*intStruct]
}{
{"arrayList1", NewArrayListOfC[*intStruct]()},
{"arrayList2", NewArrayListOfSizeC[*intStruct](10)},
{"arrayList3", NewArrayListOfSizePC[*intStruct](10, NoAutoSizing)},
{"linkedList1", NewLinkedListOfC[*intStruct]()},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
q := test.q
v1 := &intStruct{1}
q.Add(v1)
assert.True(t, q.Contains(v1))
})
}
}
Loading

0 comments on commit 2eccd05

Please sign in to comment.