diff --git a/internal/treeenc/treeenc.go b/internal/treeenc/treeenc.go new file mode 100644 index 00000000..e3280c27 --- /dev/null +++ b/internal/treeenc/treeenc.go @@ -0,0 +1,89 @@ +package treeenc + +import ( + "encoding" + "encoding/json" + "reflect" + "strconv" +) + +// KeyMarshaler is a helper type for marshaling keys of a tree. +// When marshaling a tree, we need first to convert tree key/value +// pairs to a standard Go map, and then marshal the map. However, +// Go maps can only have keys of comparable types, and so we wrap +// the key in a KeyMarshaler and implement the encoding.TextMarshaler +// interface to make it comparable and marshalable. +// +// The map should be declared as map[*KeyMarshaler[K]]V. +type KeyMarshaler[K any] struct { + Key K +} + +var _ encoding.TextMarshaler = &KeyMarshaler[string]{} + +func (m *KeyMarshaler[T]) MarshalText() ([]byte, error) { + kv := reflect.ValueOf(m.Key) + if tm, ok := kv.Interface().(encoding.TextMarshaler); ok { + if kv.Kind() == reflect.Pointer && kv.IsNil() { + return nil, nil + } + return tm.MarshalText() + } + + var text string + switch kv.Kind() { + case reflect.String: + text = kv.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + text = strconv.FormatInt(kv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + text = strconv.FormatUint(kv.Uint(), 10) + default: + return nil, &json.UnsupportedTypeError{Type: kv.Type()} + } + return []byte(text), nil +} + +// KeyUnmarshaler is a helper type for unmarshaling keys of a tree. +// When unmarshaling a tree, we first unmarshal the JSON into a Go +// map, and then convert the map to tree key/value pairs. Similar to +// KeyMarshaler, we wrap the key in a KeyUnmarshaler to make it +// unmarshalable by implementing the encoding.TextUnmarshaler interface. +// +// The map should be declared as map[KeyUnmarshaler[K]]V. +type KeyUnmarshaler[K any] struct { + Key *K +} + +var _ encoding.TextUnmarshaler = &KeyUnmarshaler[string]{} + +func (m *KeyUnmarshaler[K]) UnmarshalText(text []byte) error { + var key K + m.Key = &key + + kv := reflect.ValueOf(m.Key) + if tu, ok := kv.Interface().(encoding.TextUnmarshaler); ok { + if kv.Kind() == reflect.Ptr && kv.IsNil() { + return nil + } + return tu.UnmarshalText(text) + } + + var err error + kv = kv.Elem() + switch kv.Kind() { + case reflect.String: + kv.SetString(string(text)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + var i int64 + i, err = strconv.ParseInt(string(text), 10, 64) + kv.SetInt(i) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + var u uint64 + u, err = strconv.ParseUint(string(text), 10, 64) + kv.SetUint(u) + default: + err = &json.UnsupportedTypeError{Type: kv.Type()} + } + return err +} diff --git a/internal/treeenc/treeenc_test.go b/internal/treeenc/treeenc_test.go new file mode 100644 index 00000000..bd36409e --- /dev/null +++ b/internal/treeenc/treeenc_test.go @@ -0,0 +1,140 @@ +package treeenc + +import ( + "encoding/json" + "fmt" + "math" + "strings" + "testing" +) + +type customType struct { + value string +} + +func (c customType) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("customType(%s)", c.value)), nil +} + +func (c *customType) UnmarshalText(text []byte) error { + value := strings.TrimPrefix(string(text), "customType(") + value = strings.TrimSuffix(value, ")") + c.value = value + return nil +} + +func TestKeyMarshaler(t *testing.T) { + strMap := make(map[*KeyMarshaler[string]]string) + strMap[&KeyMarshaler[string]{Key: "key"}] = "value" + data, err := json.Marshal(strMap) + if err != nil { + t.Fatalf("Got error: %v", err) + } + expected := "{\"key\":\"value\"}" + if string(data) != expected { + t.Fatalf("Expected %q, got %q", expected, string(data)) + } + + intMap := make(map[*KeyMarshaler[int]]int) + intMap[&KeyMarshaler[int]{Key: math.MinInt}] = math.MinInt + data, err = json.Marshal(&intMap) + if err != nil { + t.Fatalf("Got error: %v", err) + } + expected = "{\"-9223372036854775808\":-9223372036854775808}" + if string(data) != expected { + t.Fatalf("Expected %q, got %q", expected, string(data)) + } + + uintMap := make(map[*KeyMarshaler[uint]]uint) + uintMap[&KeyMarshaler[uint]{Key: math.MaxUint}] = math.MaxUint + data, err = json.Marshal(&uintMap) + if err != nil { + t.Fatalf("Got error: %v", err) + } + expected = "{\"18446744073709551615\":18446744073709551615}" + if string(data) != expected { + t.Fatalf("Expected %q, got %q", expected, string(data)) + } + + customMap := make(map[*KeyMarshaler[customType]]string) + customMap[&KeyMarshaler[customType]{Key: customType{value: "key"}}] = "value" + data, err = json.Marshal(&customMap) + if err != nil { + t.Fatalf("Got error: %v", err) + } + expected = "{\"customType(key)\":\"value\"}" + if string(data) != expected { + t.Fatalf("Expected %q, got %q", expected, string(data)) + } +} + +func TestKeyUnmarshaler(t *testing.T) { + strMap := make(map[KeyUnmarshaler[string]]string) + data := []byte("{\"key\":\"value\"}") + if err := json.Unmarshal(data, &strMap); err != nil { + t.Fatalf("Got error: %v", err) + } + if len(strMap) != 1 { + t.Fatalf("Expected 1 key, got %d", len(strMap)) + } + for k, v := range strMap { + if *k.Key != "key" { + t.Fatalf("Expected key %q, got %q", "key", *k.Key) + } + if v != "value" { + t.Fatalf("Expected value %q, got %q", "value", v) + } + } + + intMap := make(map[KeyUnmarshaler[int]]int) + data = []byte("{\"-9223372036854775808\":-9223372036854775808}") + if err := json.Unmarshal(data, &intMap); err != nil { + t.Fatalf("Got error: %v", err) + } + if len(intMap) != 1 { + t.Fatalf("Expected 1 key, got %d", len(intMap)) + } + for k, v := range intMap { + if *k.Key != math.MinInt { + t.Fatalf("Expected key %d, got %d", math.MinInt, *k.Key) + } + if v != math.MinInt { + t.Fatalf("Expected value %d, got %d", math.MinInt, v) + } + } + + uintMap := make(map[KeyUnmarshaler[uint]]uint) + data = []byte("{\"18446744073709551615\":18446744073709551615}") + if err := json.Unmarshal(data, &uintMap); err != nil { + t.Fatalf("Got error: %v", err) + } + if len(uintMap) != 1 { + t.Fatalf("Expected 1 key, got %d", len(uintMap)) + } + for k, v := range uintMap { + if *k.Key != math.MaxUint { + t.Fatalf("Expected key %d, got %d", uint(math.MaxUint), *k.Key) + } + if v != math.MaxUint { + t.Fatalf("Expected value %d, got %d", uint(math.MaxUint), v) + } + } + + customMap := make(map[KeyUnmarshaler[customType]]string) + data = []byte("{\"customType(key)\":\"value\"}") + if err := json.Unmarshal(data, &customMap); err != nil { + t.Fatalf("Got error: %v", err) + } + if len(customMap) != 1 { + t.Fatalf("Expected 1 key, got %d", len(customMap)) + } + for k, v := range customMap { + if (*k.Key).value != "key" { + t.Fatalf("Expected key %q, got %q", "key", (*k.Key).value) + } + if v != "value" { + t.Fatalf("Expected value %q, got %q", "value", v) + } + } +} diff --git a/lists/arraylist/arraylist.go b/lists/arraylist/arraylist.go index e67ff292..2b338cf0 100644 --- a/lists/arraylist/arraylist.go +++ b/lists/arraylist/arraylist.go @@ -22,9 +22,10 @@ import ( var _ lists.List[int] = (*List[int])(nil) // List holds the elements in a slice -type List[T comparable] struct { +type List[T any] struct { elements []T size int + equal func(a, b T) bool } const ( @@ -34,7 +35,14 @@ const ( // New instantiates a new list and adds the passed values, if any, to the list func New[T comparable](values ...T) *List[T] { - list := &List[T]{} + equal := func(a, b T) bool { return a == b } + return NewWith(equal, values...) +} + +// NewWith instantiates a new list with the custom equal +// function and adds the passed values, if any, to the list. +func NewWith[T any](equal func(a, b T) bool, values ...T) *List[T] { + list := &List[T]{equal: equal} if len(values) > 0 { list.Add(values...) } @@ -85,7 +93,7 @@ func (list *List[T]) Contains(values ...T) bool { for _, searchValue := range values { found := false for index := 0; index < list.size; index++ { - if list.elements[index] == searchValue { + if list.equal(list.elements[index], searchValue) { found = true break } @@ -99,7 +107,7 @@ func (list *List[T]) Contains(values ...T) bool { // Values returns all elements in the list. func (list *List[T]) Values() []T { - newElements := make([]T, list.size, list.size) + newElements := make([]T, list.size) copy(newElements, list.elements[:list.size]) return newElements } @@ -110,7 +118,7 @@ func (list *List[T]) IndexOf(value T) int { return -1 } for index, element := range list.elements { - if element == value { + if list.equal(element, value) { return index } } diff --git a/lists/arraylist/iterator.go b/lists/arraylist/iterator.go index 5a6705ce..ba8d27c6 100644 --- a/lists/arraylist/iterator.go +++ b/lists/arraylist/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator holding the iterator's state -type Iterator[T comparable] struct { +type Iterator[T any] struct { list *List[T] index int } diff --git a/lists/doublylinkedlist/doublylinkedlist.go b/lists/doublylinkedlist/doublylinkedlist.go index 6a34e9de..d1fa66cc 100644 --- a/lists/doublylinkedlist/doublylinkedlist.go +++ b/lists/doublylinkedlist/doublylinkedlist.go @@ -22,13 +22,14 @@ import ( var _ lists.List[any] = (*List[any])(nil) // List holds the elements, where each element points to the next and previous element -type List[T comparable] struct { +type List[T any] struct { first *element[T] last *element[T] size int + equal func(a, b T) bool } -type element[T comparable] struct { +type element[T any] struct { value T prev *element[T] next *element[T] @@ -36,7 +37,14 @@ type element[T comparable] struct { // New instantiates a new list and adds the passed values, if any, to the list func New[T comparable](values ...T) *List[T] { - list := &List[T]{} + equal := func(a, b T) bool { return a == b } + return NewWith(equal, values...) +} + +// NewWith instantiates a new list with the custom equal +// function and adds the passed values, if any, to the list. +func NewWith[T any](equal func(a, b T) bool, values ...T) *List[T] { + list := &List[T]{equal: equal} if len(values) > 0 { list.Add(values...) } @@ -158,7 +166,7 @@ func (list *List[T]) Contains(values ...T) bool { for _, value := range values { found := false for element := list.first; element != nil; element = element.next { - if element.value == value { + if list.equal(element.value, value) { found = true break } @@ -185,7 +193,7 @@ func (list *List[T]) IndexOf(value T) int { return -1 } for index, element := range list.Values() { - if element == value { + if list.equal(element, value) { return index } } diff --git a/lists/doublylinkedlist/iterator.go b/lists/doublylinkedlist/iterator.go index 1b637073..e809d172 100644 --- a/lists/doublylinkedlist/iterator.go +++ b/lists/doublylinkedlist/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator holding the iterator's state -type Iterator[T comparable] struct { +type Iterator[T any] struct { list *List[T] index int element *element[T] diff --git a/lists/lists.go b/lists/lists.go index cc310366..6a14a4ab 100644 --- a/lists/lists.go +++ b/lists/lists.go @@ -15,7 +15,7 @@ import ( ) // List interface that all lists implement -type List[T comparable] interface { +type List[T any] interface { Get(index int) (T, bool) Remove(index int) Add(values ...T) diff --git a/lists/singlylinkedlist/iterator.go b/lists/singlylinkedlist/iterator.go index dc3304a8..be0b1747 100644 --- a/lists/singlylinkedlist/iterator.go +++ b/lists/singlylinkedlist/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.IteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator holding the iterator's state -type Iterator[T comparable] struct { +type Iterator[T any] struct { list *List[T] index int element *element[T] diff --git a/lists/singlylinkedlist/singlylinkedlist.go b/lists/singlylinkedlist/singlylinkedlist.go index a4126021..62c873a3 100644 --- a/lists/singlylinkedlist/singlylinkedlist.go +++ b/lists/singlylinkedlist/singlylinkedlist.go @@ -22,20 +22,28 @@ import ( var _ lists.List[int] = (*List[int])(nil) // List holds the elements, where each element points to the next element -type List[T comparable] struct { +type List[T any] struct { first *element[T] last *element[T] size int + equal func(a, b T) bool } -type element[T comparable] struct { +type element[T any] struct { value T next *element[T] } // New instantiates a new list and adds the passed values, if any, to the list func New[T comparable](values ...T) *List[T] { - list := &List[T]{} + equal := func(a, b T) bool { return a == b } + return NewWith(equal, values...) +} + +// NewWith instantiates a new list with the custom equal +// function and adds the passed values, if any, to the list. +func NewWith[T any](equal func(a, b T) bool, values ...T) *List[T] { + list := &List[T]{equal: equal} if len(values) > 0 { list.Add(values...) } @@ -139,7 +147,7 @@ func (list *List[T]) Contains(values ...T) bool { for _, value := range values { found := false for element := list.first; element != nil; element = element.next { - if element.value == value { + if list.equal(element.value, value) { found = true break } @@ -166,7 +174,7 @@ func (list *List[T]) IndexOf(value T) int { return -1 } for index, element := range list.Values() { - if element == value { + if list.equal(element, value) { return index } } diff --git a/maps/maps.go b/maps/maps.go index b54f7dc5..e13f9cdc 100644 --- a/maps/maps.go +++ b/maps/maps.go @@ -18,7 +18,7 @@ package maps import "github.com/emirpasic/gods/v2/containers" // Map interface that all maps implement -type Map[K comparable, V any] interface { +type Map[K any, V any] interface { Put(key K, value V) Get(key K) (value V, found bool) Remove(key K) @@ -33,7 +33,7 @@ type Map[K comparable, V any] interface { } // BidiMap interface that all bidirectional maps implement (extends the Map interface) -type BidiMap[K comparable, V comparable] interface { +type BidiMap[K any, V any] interface { GetKey(value V) (key K, found bool) Map[K, V] diff --git a/maps/treebidimap/iterator.go b/maps/treebidimap/iterator.go index bf86ea71..24c45df9 100644 --- a/maps/treebidimap/iterator.go +++ b/maps/treebidimap/iterator.go @@ -13,7 +13,7 @@ import ( var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil) // Iterator holding the iterator's state -type Iterator[K comparable, V any] struct { +type Iterator[K any, V any] struct { iterator *rbt.Iterator[K, V] } diff --git a/maps/treebidimap/serialization.go b/maps/treebidimap/serialization.go index b72907b9..6d015fbd 100644 --- a/maps/treebidimap/serialization.go +++ b/maps/treebidimap/serialization.go @@ -8,6 +8,7 @@ import ( "encoding/json" "github.com/emirpasic/gods/v2/containers" + "github.com/emirpasic/gods/v2/internal/treeenc" ) // Assert Serialization implementation @@ -21,15 +22,15 @@ func (m *Map[K, V]) ToJSON() ([]byte, error) { // FromJSON populates the map from the input JSON representation. func (m *Map[K, V]) FromJSON(data []byte) error { - var elements map[K]V + var elements map[treeenc.KeyUnmarshaler[K]]V err := json.Unmarshal(data, &elements) if err != nil { return err } m.Clear() - for key, value := range elements { - m.Put(key, value) + for ku, value := range elements { + m.Put(*ku.Key, value) } return nil diff --git a/maps/treebidimap/treebidimap.go b/maps/treebidimap/treebidimap.go index 27d6c9ef..4fd2b059 100644 --- a/maps/treebidimap/treebidimap.go +++ b/maps/treebidimap/treebidimap.go @@ -32,7 +32,7 @@ import ( var _ maps.BidiMap[string, int] = (*Map[string, int])(nil) // Map holds the elements in two red-black trees. -type Map[K, V comparable] struct { +type Map[K, V any] struct { forwardMap redblacktree.Tree[K, V] inverseMap redblacktree.Tree[V, K] } @@ -46,7 +46,7 @@ func New[K, V cmp.Ordered]() *Map[K, V] { } // NewWith instantiates a bidirectional map. -func NewWith[K, V comparable](keyComparator utils.Comparator[K], valueComparator utils.Comparator[V]) *Map[K, V] { +func NewWith[K, V any](keyComparator utils.Comparator[K], valueComparator utils.Comparator[V]) *Map[K, V] { return &Map[K, V]{ forwardMap: *redblacktree.NewWith[K, V](keyComparator), inverseMap: *redblacktree.NewWith[V, K](valueComparator), diff --git a/maps/treemap/iterator.go b/maps/treemap/iterator.go index 9bdf91b5..edef9a48 100644 --- a/maps/treemap/iterator.go +++ b/maps/treemap/iterator.go @@ -13,7 +13,7 @@ import ( var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil) // Iterator holding the iterator's state -type Iterator[K comparable, V any] struct { +type Iterator[K any, V any] struct { iterator *rbt.Iterator[K, V] } diff --git a/maps/treemap/treemap.go b/maps/treemap/treemap.go index f3766622..e97ab469 100644 --- a/maps/treemap/treemap.go +++ b/maps/treemap/treemap.go @@ -25,7 +25,7 @@ import ( var _ maps.Map[string, int] = (*Map[string, int])(nil) // Map holds the elements in a red-black tree -type Map[K comparable, V any] struct { +type Map[K any, V any] struct { tree *rbt.Tree[K, V] } @@ -35,7 +35,7 @@ func New[K cmp.Ordered, V any]() *Map[K, V] { } // NewWith instantiates a tree map with the custom comparator. -func NewWith[K comparable, V any](comparator utils.Comparator[K]) *Map[K, V] { +func NewWith[K any, V any](comparator utils.Comparator[K]) *Map[K, V] { return &Map[K, V]{tree: rbt.NewWith[K, V](comparator)} } diff --git a/queues/arrayqueue/arrayqueue.go b/queues/arrayqueue/arrayqueue.go index 6c57c722..0de3b457 100644 --- a/queues/arrayqueue/arrayqueue.go +++ b/queues/arrayqueue/arrayqueue.go @@ -21,7 +21,7 @@ import ( var _ queues.Queue[int] = (*Queue[int])(nil) // Queue holds elements in an array-list -type Queue[T comparable] struct { +type Queue[T any] struct { list *arraylist.List[T] } @@ -30,6 +30,11 @@ func New[T comparable]() *Queue[T] { return &Queue[T]{list: arraylist.New[T]()} } +// NewWith instantiates a new empty queue with the custom equal function. +func NewWith[T comparable](equal func(a, b T) bool) *Queue[T] { + return &Queue[T]{list: arraylist.NewWith(equal)} +} + // Enqueue adds a value to the end of the queue func (queue *Queue[T]) Enqueue(value T) { queue.list.Add(value) diff --git a/queues/arrayqueue/iterator.go b/queues/arrayqueue/iterator.go index bc684685..b69a1853 100644 --- a/queues/arrayqueue/iterator.go +++ b/queues/arrayqueue/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { queue *Queue[T] index int } diff --git a/queues/circularbuffer/circularbuffer.go b/queues/circularbuffer/circularbuffer.go index ef71fdd4..81a1cc7d 100644 --- a/queues/circularbuffer/circularbuffer.go +++ b/queues/circularbuffer/circularbuffer.go @@ -22,7 +22,7 @@ import ( var _ queues.Queue[int] = (*Queue[int])(nil) // Queue holds values in a slice. -type Queue[T comparable] struct { +type Queue[T any] struct { values []T start int end int @@ -33,7 +33,7 @@ type Queue[T comparable] struct { // New instantiates a new empty queue with the specified size of maximum number of elements that it can hold. // This max size of the buffer cannot be changed. -func New[T comparable](maxSize int) *Queue[T] { +func New[T any](maxSize int) *Queue[T] { if maxSize < 1 { panic("Invalid maxSize, should be at least 1") } diff --git a/queues/circularbuffer/iterator.go b/queues/circularbuffer/iterator.go index be6af87d..1275fb83 100644 --- a/queues/circularbuffer/iterator.go +++ b/queues/circularbuffer/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { queue *Queue[T] index int } diff --git a/queues/linkedlistqueue/iterator.go b/queues/linkedlistqueue/iterator.go index 29bfffda..596fc695 100644 --- a/queues/linkedlistqueue/iterator.go +++ b/queues/linkedlistqueue/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.IteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { queue *Queue[T] index int } diff --git a/queues/linkedlistqueue/linkedlistqueue.go b/queues/linkedlistqueue/linkedlistqueue.go index 32ad9f32..c0db1818 100644 --- a/queues/linkedlistqueue/linkedlistqueue.go +++ b/queues/linkedlistqueue/linkedlistqueue.go @@ -21,7 +21,7 @@ import ( var _ queues.Queue[int] = (*Queue[int])(nil) // Queue holds elements in a singly-linked-list -type Queue[T comparable] struct { +type Queue[T any] struct { list *singlylinkedlist.List[T] } @@ -30,6 +30,11 @@ func New[T comparable]() *Queue[T] { return &Queue[T]{list: singlylinkedlist.New[T]()} } +// NewWith instantiates a new empty queue with the custom equal function. +func NewWith[T any](equal func(a, b T) bool) *Queue[T] { + return &Queue[T]{list: singlylinkedlist.NewWith(equal)} +} + // Enqueue adds a value to the end of the queue func (queue *Queue[T]) Enqueue(value T) { queue.list.Add(value) diff --git a/queues/priorityqueue/iterator.go b/queues/priorityqueue/iterator.go index 1a55e07f..c6f8329c 100644 --- a/queues/priorityqueue/iterator.go +++ b/queues/priorityqueue/iterator.go @@ -13,7 +13,7 @@ import ( var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { iterator *binaryheap.Iterator[T] } diff --git a/queues/priorityqueue/priorityqueue.go b/queues/priorityqueue/priorityqueue.go index 4427f75e..a384cc3c 100644 --- a/queues/priorityqueue/priorityqueue.go +++ b/queues/priorityqueue/priorityqueue.go @@ -29,7 +29,7 @@ import ( var _ queues.Queue[int] = (*Queue[int])(nil) // Queue holds elements in an array-list -type Queue[T comparable] struct { +type Queue[T any] struct { heap *binaryheap.Heap[T] Comparator utils.Comparator[T] } @@ -39,7 +39,7 @@ func New[T cmp.Ordered]() *Queue[T] { } // NewWith instantiates a new empty queue with the custom comparator. -func NewWith[T comparable](comparator utils.Comparator[T]) *Queue[T] { +func NewWith[T any](comparator utils.Comparator[T]) *Queue[T] { return &Queue[T]{heap: binaryheap.NewWith(comparator), Comparator: comparator} } diff --git a/queues/queues.go b/queues/queues.go index 6f2deb43..492692d8 100644 --- a/queues/queues.go +++ b/queues/queues.go @@ -13,7 +13,7 @@ package queues import "github.com/emirpasic/gods/v2/containers" // Queue interface that all queues implement -type Queue[T comparable] interface { +type Queue[T any] interface { Enqueue(value T) Dequeue() (value T, ok bool) Peek() (value T, ok bool) diff --git a/sets/sets.go b/sets/sets.go index e63bb5fe..4c77c4d3 100644 --- a/sets/sets.go +++ b/sets/sets.go @@ -14,7 +14,7 @@ import ( ) // Set interface that all sets implement -type Set[T comparable] interface { +type Set[T any] interface { Add(elements ...T) Remove(elements ...T) Contains(elements ...T) bool diff --git a/sets/treeset/iterator.go b/sets/treeset/iterator.go index 435b46b0..60282dde 100644 --- a/sets/treeset/iterator.go +++ b/sets/treeset/iterator.go @@ -13,7 +13,7 @@ import ( var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { index int iterator *rbt.Iterator[T, struct{}] tree *rbt.Tree[T, struct{}] diff --git a/sets/treeset/treeset.go b/sets/treeset/treeset.go index 6b63a7e1..a7ec60e0 100644 --- a/sets/treeset/treeset.go +++ b/sets/treeset/treeset.go @@ -24,7 +24,7 @@ import ( var _ sets.Set[int] = (*Set[int])(nil) // Set holds elements in a red-black tree -type Set[T comparable] struct { +type Set[T any] struct { tree *rbt.Tree[T, struct{}] } @@ -35,7 +35,7 @@ func New[T cmp.Ordered](values ...T) *Set[T] { } // NewWith instantiates a new empty set with the custom comparator. -func NewWith[T comparable](comparator utils.Comparator[T], values ...T) *Set[T] { +func NewWith[T any](comparator utils.Comparator[T], values ...T) *Set[T] { set := &Set[T]{tree: rbt.NewWith[T, struct{}](comparator)} if len(values) > 0 { set.Add(values...) diff --git a/stacks/arraystack/arraystack.go b/stacks/arraystack/arraystack.go index ec80cb20..7066d11d 100644 --- a/stacks/arraystack/arraystack.go +++ b/stacks/arraystack/arraystack.go @@ -21,7 +21,7 @@ import ( var _ stacks.Stack[int] = (*Stack[int])(nil) // Stack holds elements in an array-list -type Stack[T comparable] struct { +type Stack[T any] struct { list *arraylist.List[T] } @@ -30,6 +30,11 @@ func New[T comparable]() *Stack[T] { return &Stack[T]{list: arraylist.New[T]()} } +// NewWith instantiates a new empty stack with the custom equals function. +func NewWith[T any](equal func(a, b T) bool) *Stack[T] { + return &Stack[T]{list: arraylist.NewWith[T](equal)} +} + // Push adds a value onto the top of the stack func (stack *Stack[T]) Push(value T) { stack.list.Add(value) diff --git a/stacks/arraystack/iterator.go b/stacks/arraystack/iterator.go index 651e41a0..00c77608 100644 --- a/stacks/arraystack/iterator.go +++ b/stacks/arraystack/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { stack *Stack[T] index int } diff --git a/stacks/linkedliststack/iterator.go b/stacks/linkedliststack/iterator.go index 5350e330..d16b8cb9 100644 --- a/stacks/linkedliststack/iterator.go +++ b/stacks/linkedliststack/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.IteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { stack *Stack[T] index int } diff --git a/stacks/linkedliststack/linkedliststack.go b/stacks/linkedliststack/linkedliststack.go index ec373dd5..b98a1555 100644 --- a/stacks/linkedliststack/linkedliststack.go +++ b/stacks/linkedliststack/linkedliststack.go @@ -21,15 +21,20 @@ import ( var _ stacks.Stack[int] = (*Stack[int])(nil) // Stack holds elements in a singly-linked-list -type Stack[T comparable] struct { +type Stack[T any] struct { list *singlylinkedlist.List[T] } -// New nnstantiates a new empty stack +// New instantiates a new empty stack. func New[T comparable]() *Stack[T] { return &Stack[T]{list: singlylinkedlist.New[T]()} } +// NewWith instantiates a new empty stack with the custom equal function. +func NewWith[T any](equal func(a, b T) bool) *Stack[T] { + return &Stack[T]{list: singlylinkedlist.NewWith(equal)} +} + // Push adds a value onto the top of the stack func (stack *Stack[T]) Push(value T) { stack.list.Prepend(value) diff --git a/trees/avltree/avltree.go b/trees/avltree/avltree.go index 81b9f3f8..0713b912 100644 --- a/trees/avltree/avltree.go +++ b/trees/avltree/avltree.go @@ -21,14 +21,14 @@ import ( var _ trees.Tree[int] = (*Tree[string, int])(nil) // Tree holds elements of the AVL tree. -type Tree[K comparable, V any] struct { +type Tree[K any, V any] struct { Root *Node[K, V] // Root node Comparator utils.Comparator[K] // Key comparator size int // Total number of keys in the tree } // Node is a single element within the tree -type Node[K comparable, V any] struct { +type Node[K any, V any] struct { Key K Value V Parent *Node[K, V] // Parent node @@ -42,7 +42,7 @@ func New[K cmp.Ordered, V any]() *Tree[K, V] { } // NewWith instantiates an AVL tree with the custom comparator. -func NewWith[K comparable, V any](comparator utils.Comparator[K]) *Tree[K, V] { +func NewWith[K any, V any](comparator utils.Comparator[K]) *Tree[K, V] { return &Tree[K, V]{Comparator: comparator} } @@ -287,7 +287,7 @@ func (tree *Tree[K, V]) remove(key K, qp **Node[K, V]) bool { return false } -func removeMin[K comparable, V any](qp **Node[K, V], minKey *K, minVal *V) bool { +func removeMin[K any, V any](qp **Node[K, V], minKey *K, minVal *V) bool { q := *qp if q.Children[0] == nil { *minKey = q.Key @@ -305,7 +305,7 @@ func removeMin[K comparable, V any](qp **Node[K, V], minKey *K, minVal *V) bool return false } -func putFix[K comparable, V any](c int8, t **Node[K, V]) bool { +func putFix[K any, V any](c int8, t **Node[K, V]) bool { s := *t if s.b == 0 { s.b = c @@ -326,7 +326,7 @@ func putFix[K comparable, V any](c int8, t **Node[K, V]) bool { return false } -func removeFix[K comparable, V any](c int8, t **Node[K, V]) bool { +func removeFix[K any, V any](c int8, t **Node[K, V]) bool { s := *t if s.b == 0 { s.b = c @@ -355,14 +355,14 @@ func removeFix[K comparable, V any](c int8, t **Node[K, V]) bool { return true } -func singlerot[K comparable, V any](c int8, s *Node[K, V]) *Node[K, V] { +func singlerot[K any, V any](c int8, s *Node[K, V]) *Node[K, V] { s.b = 0 s = rotate(c, s) s.b = 0 return s } -func doublerot[K comparable, V any](c int8, s *Node[K, V]) *Node[K, V] { +func doublerot[K any, V any](c int8, s *Node[K, V]) *Node[K, V] { a := (c + 1) / 2 r := s.Children[a] s.Children[a] = rotate(-c, s.Children[a]) @@ -384,7 +384,7 @@ func doublerot[K comparable, V any](c int8, s *Node[K, V]) *Node[K, V] { return p } -func rotate[K comparable, V any](c int8, s *Node[K, V]) *Node[K, V] { +func rotate[K any, V any](c int8, s *Node[K, V]) *Node[K, V] { a := (c + 1) / 2 r := s.Children[a] s.Children[a] = r.Children[a^1] @@ -442,7 +442,7 @@ func (n *Node[K, V]) walk1(a int) *Node[K, V] { return p } -func output[K comparable, V any](node *Node[K, V], prefix string, isTail bool, str *string) { +func output[K any, V any](node *Node[K, V], prefix string, isTail bool, str *string) { if node.Children[1] != nil { newPrefix := prefix if isTail { diff --git a/trees/avltree/iterator.go b/trees/avltree/iterator.go index 8541ce0a..72fa24e1 100644 --- a/trees/avltree/iterator.go +++ b/trees/avltree/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil) // Iterator holding the iterator's state -type Iterator[K comparable, V any] struct { +type Iterator[K any, V any] struct { tree *Tree[K, V] node *Node[K, V] position position diff --git a/trees/avltree/serialization.go b/trees/avltree/serialization.go index 4b3da5d3..fad195f3 100644 --- a/trees/avltree/serialization.go +++ b/trees/avltree/serialization.go @@ -8,6 +8,7 @@ import ( "encoding/json" "github.com/emirpasic/gods/v2/containers" + "github.com/emirpasic/gods/v2/internal/treeenc" ) // Assert Serialization implementation @@ -16,25 +17,25 @@ var _ containers.JSONDeserializer = (*Tree[string, int])(nil) // ToJSON outputs the JSON representation of the tree. func (tree *Tree[K, V]) ToJSON() ([]byte, error) { - elements := make(map[K]V) + elements := make(map[*treeenc.KeyMarshaler[K]]V) it := tree.Iterator() for it.Next() { - elements[it.Key()] = it.Value() + elements[&treeenc.KeyMarshaler[K]{Key: it.Key()}] = it.Value() } return json.Marshal(&elements) } // FromJSON populates the tree from the input JSON representation. func (tree *Tree[K, V]) FromJSON(data []byte) error { - elements := make(map[K]V) + elements := make(map[treeenc.KeyUnmarshaler[K]]V) err := json.Unmarshal(data, &elements) if err != nil { return err } tree.Clear() - for key, value := range elements { - tree.Put(key, value) + for ku, value := range elements { + tree.Put(*ku.Key, value) } return nil diff --git a/trees/binaryheap/binaryheap.go b/trees/binaryheap/binaryheap.go index 9f1605cd..67dac1c7 100644 --- a/trees/binaryheap/binaryheap.go +++ b/trees/binaryheap/binaryheap.go @@ -25,7 +25,7 @@ import ( var _ trees.Tree[int] = (*Heap[int])(nil) // Heap holds elements in an array-list -type Heap[T comparable] struct { +type Heap[T any] struct { list *arraylist.List[T] Comparator utils.Comparator[T] } @@ -36,8 +36,9 @@ func New[T cmp.Ordered]() *Heap[T] { } // NewWith instantiates a new empty heap tree with the custom comparator. -func NewWith[T comparable](comparator utils.Comparator[T]) *Heap[T] { - return &Heap[T]{list: arraylist.New[T](), Comparator: comparator} +func NewWith[T any](comparator utils.Comparator[T]) *Heap[T] { + equal := func(a, b T) bool { return comparator(a, b) == 0 } + return &Heap[T]{list: arraylist.NewWith[T](equal), Comparator: comparator} } // Push adds a value onto the heap and bubbles it up accordingly. diff --git a/trees/binaryheap/iterator.go b/trees/binaryheap/iterator.go index 73ff1815..5f5583f9 100644 --- a/trees/binaryheap/iterator.go +++ b/trees/binaryheap/iterator.go @@ -12,7 +12,7 @@ import ( var _ containers.ReverseIteratorWithIndex[int] = (*Iterator[int])(nil) // Iterator returns a stateful iterator whose values can be fetched by an index. -type Iterator[T comparable] struct { +type Iterator[T any] struct { heap *Heap[T] index int } diff --git a/trees/btree/btree.go b/trees/btree/btree.go index 75f60ad6..41b071e3 100644 --- a/trees/btree/btree.go +++ b/trees/btree/btree.go @@ -30,7 +30,7 @@ import ( var _ trees.Tree[int] = (*Tree[string, int])(nil) // Tree holds elements of the B-tree -type Tree[K comparable, V any] struct { +type Tree[K any, V any] struct { Root *Node[K, V] // Root node Comparator utils.Comparator[K] // Key comparator size int // Total number of keys in the tree @@ -38,14 +38,14 @@ type Tree[K comparable, V any] struct { } // Node is a single element within the tree -type Node[K comparable, V any] struct { +type Node[K any, V any] struct { Parent *Node[K, V] Entries []*Entry[K, V] // Contained keys in node Children []*Node[K, V] // Children nodes } // Entry represents the key-value pair contained within nodes -type Entry[K comparable, V any] struct { +type Entry[K any, V any] struct { Key K Value V } @@ -56,7 +56,7 @@ func New[K cmp.Ordered, V any](order int) *Tree[K, V] { } // NewWith instantiates a B-tree with the order (maximum number of children) and a custom key comparator. -func NewWith[K comparable, V any](order int, comparator utils.Comparator[K]) *Tree[K, V] { +func NewWith[K any, V any](order int, comparator utils.Comparator[K]) *Tree[K, V] { if order < 3 { panic("Invalid order, should be at least 3") } @@ -411,7 +411,7 @@ func (tree *Tree[K, V]) splitRoot() { tree.Root = newRoot } -func setParent[K comparable, V any](nodes []*Node[K, V], parent *Node[K, V]) { +func setParent[K any, V any](nodes []*Node[K, V], parent *Node[K, V]) { for _, node := range nodes { node.Parent = parent } diff --git a/trees/btree/iterator.go b/trees/btree/iterator.go index 23c9387d..ac08ead1 100644 --- a/trees/btree/iterator.go +++ b/trees/btree/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil) // Iterator holding the iterator's state -type Iterator[K comparable, V any] struct { +type Iterator[K any, V any] struct { tree *Tree[K, V] node *Node[K, V] entry *Entry[K, V] diff --git a/trees/btree/serialization.go b/trees/btree/serialization.go index 4b9a3139..d4895298 100644 --- a/trees/btree/serialization.go +++ b/trees/btree/serialization.go @@ -8,6 +8,7 @@ import ( "encoding/json" "github.com/emirpasic/gods/v2/containers" + "github.com/emirpasic/gods/v2/internal/treeenc" ) // Assert Serialization implementation @@ -16,25 +17,25 @@ var _ containers.JSONDeserializer = (*Tree[string, int])(nil) // ToJSON outputs the JSON representation of the tree. func (tree *Tree[K, V]) ToJSON() ([]byte, error) { - elements := make(map[K]V) + elements := make(map[*treeenc.KeyMarshaler[K]]V) it := tree.Iterator() for it.Next() { - elements[it.Key()] = it.Value() + elements[&treeenc.KeyMarshaler[K]{Key: it.Key()}] = it.Value() } return json.Marshal(&elements) } // FromJSON populates the tree from the input JSON representation. func (tree *Tree[K, V]) FromJSON(data []byte) error { - elements := make(map[K]V) + elements := make(map[treeenc.KeyUnmarshaler[K]]V) err := json.Unmarshal(data, &elements) if err != nil { return err } tree.Clear() - for key, value := range elements { - tree.Put(key, value) + for ku, value := range elements { + tree.Put(*ku.Key, value) } return err diff --git a/trees/redblacktree/iterator.go b/trees/redblacktree/iterator.go index 9aa5605e..14d95c4c 100644 --- a/trees/redblacktree/iterator.go +++ b/trees/redblacktree/iterator.go @@ -10,7 +10,7 @@ import "github.com/emirpasic/gods/v2/containers" var _ containers.ReverseIteratorWithKey[string, int] = (*Iterator[string, int])(nil) // Iterator holding the iterator's state -type Iterator[K comparable, V any] struct { +type Iterator[K any, V any] struct { tree *Tree[K, V] node *Node[K, V] position position diff --git a/trees/redblacktree/redblacktree.go b/trees/redblacktree/redblacktree.go index ec53532d..f6880697 100644 --- a/trees/redblacktree/redblacktree.go +++ b/trees/redblacktree/redblacktree.go @@ -29,14 +29,14 @@ const ( ) // Tree holds elements of the red-black tree -type Tree[K comparable, V any] struct { +type Tree[K any, V any] struct { Root *Node[K, V] size int Comparator utils.Comparator[K] } // Node is a single element within the tree -type Node[K comparable, V any] struct { +type Node[K any, V any] struct { Key K Value V color color @@ -51,7 +51,7 @@ func New[K cmp.Ordered, V any]() *Tree[K, V] { } // NewWith instantiates a red-black tree with the custom comparator. -func NewWith[K comparable, V any](comparator utils.Comparator[K]) *Tree[K, V] { +func NewWith[K any, V any](comparator utils.Comparator[K]) *Tree[K, V] { return &Tree[K, V]{Comparator: comparator} } @@ -292,7 +292,7 @@ func (node *Node[K, V]) String() string { return fmt.Sprintf("%v", node.Key) } -func output[K comparable, V any](node *Node[K, V], prefix string, isTail bool, str *string) { +func output[K any, V any](node *Node[K, V], prefix string, isTail bool, str *string) { if node.Right != nil { newPrefix := prefix if isTail { @@ -537,7 +537,7 @@ func (tree *Tree[K, V]) deleteCase6(node *Node[K, V]) { } } -func nodeColor[K comparable, V any](node *Node[K, V]) color { +func nodeColor[K any, V any](node *Node[K, V]) color { if node == nil { return black } diff --git a/trees/redblacktree/serialization.go b/trees/redblacktree/serialization.go index 9311c897..266024fe 100644 --- a/trees/redblacktree/serialization.go +++ b/trees/redblacktree/serialization.go @@ -8,6 +8,7 @@ import ( "encoding/json" "github.com/emirpasic/gods/v2/containers" + "github.com/emirpasic/gods/v2/internal/treeenc" ) // Assert Serialization implementation @@ -16,22 +17,22 @@ var _ containers.JSONDeserializer = (*Tree[string, int])(nil) // ToJSON outputs the JSON representation of the tree. func (tree *Tree[K, V]) ToJSON() ([]byte, error) { - elements := make(map[K]V) + elements := make(map[*treeenc.KeyMarshaler[K]]V) it := tree.Iterator() for it.Next() { - elements[it.Key()] = it.Value() + elements[&treeenc.KeyMarshaler[K]{Key: it.Key()}] = it.Value() } return json.Marshal(&elements) } // FromJSON populates the tree from the input JSON representation. func (tree *Tree[K, V]) FromJSON(data []byte) error { - elements := make(map[K]V) + elements := make(map[treeenc.KeyUnmarshaler[K]]V) err := json.Unmarshal(data, &elements) if err == nil { tree.Clear() - for key, value := range elements { - tree.Put(key, value) + for ku, value := range elements { + tree.Put(*ku.Key, value) } } return err