aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2024-12-14 10:30:47 -0300
committerEuAndreh <eu@euandre.org>2024-12-14 10:30:47 -0300
commit06eaf1738494a9e783ed100565911d08efaae826 (patch)
treea372bcc99fed70e1cf298eeddef1ec134b178102
parentRemove extraneous files (diff)
downloadpds-06eaf1738494a9e783ed100565911d08efaae826.tar.gz
pds-06eaf1738494a9e783ed100565911d08efaae826.tar.xz
Add Makefile and move files to structured folders
-rw-r--r--.gitignore15
-rw-r--r--Makefile159
-rw-r--r--deps.mk25
-rwxr-xr-xmkdeps.sh29
-rw-r--r--sets.go243
-rw-r--r--sets_test.go126
-rw-r--r--src/pds.go (renamed from immutable.go)249
-rw-r--r--tests/main.go7
-rw-r--r--tests/pds.go (renamed from immutable_test.go)139
9 files changed, 612 insertions, 380 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..094db69
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+/src/version.go
+/*.bin
+/src/*.a
+/src/*.bin
+/src/*cgo*
+/tests/*.a
+/tests/*.bin
+/tests/functional/*/*.a
+/tests/functional/*/*.bin
+/tests/fuzz/*/*.a
+/tests/fuzz/*/*.bin
+/tests/benchmarks/*/*.a
+/tests/benchmarks/*/*.bin
+/tests/benchmarks/*/*.txt
+/tests/fuzz/corpus/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..001b63c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,159 @@
+.POSIX:
+DATE = 1970-01-01
+VERSION = 0.1.0
+NAME = pds
+NAME_UC = $(NAME)
+LANGUAGES = en
+## Installation prefix. Defaults to "/usr".
+PREFIX = /usr
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+GOLIBDIR = $(LIBDIR)/go
+INCLUDEDIR = $(PREFIX)/include
+SRCDIR = $(PREFIX)/src/$(NAME)
+SHAREDIR = $(PREFIX)/share
+LOCALEDIR = $(SHAREDIR)/locale
+MANDIR = $(SHAREDIR)/man
+EXEC = ./
+## Where to store the installation. Empty by default.
+DESTDIR =
+LDLIBS = --static
+GOCFLAGS = -I $(GOLIBDIR)
+GOLDFLAGS = -L $(GOLIBDIR)
+
+
+
+.SUFFIXES:
+.SUFFIXES: .go .a .bin .bin-check
+
+.go.a:
+ go tool compile -I $(@D) $(GOCFLAGS) -o $@ -p $(*F) \
+ `find $< $$(if [ $(*F) != main ]; then \
+ echo src/$(NAME).go src/version.go; fi) | uniq`
+
+.a.bin:
+ go tool link -L $(@D) $(GOLDFLAGS) -o $@ --extldflags '$(LDLIBS)' $<
+
+
+
+all:
+include deps.mk
+
+
+libs.a = $(libs.go:.go=.a)
+mains.a = $(mains.go:.go=.a)
+mains.bin = $(mains.go:.go=.bin)
+functional-tests/lib.a = $(functional-tests/lib.go:.go=.a)
+fuzz-targets/lib.a = $(fuzz-targets/lib.go:.go=.a)
+benchmarks/lib.a = $(benchmarks/lib.go:.go=.a)
+
+sources = \
+ src/$(NAME).go \
+ src/version.go \
+
+
+derived-assets = \
+ src/version.go \
+ $(libs.a) \
+ $(mains.a) \
+ $(mains.bin) \
+
+side-assets = \
+ tests/fuzz/corpus/ \
+ tests/benchmarks/*/main.txt \
+
+
+
+## Default target. Builds all artifacts required for testing
+## and installation.
+all: $(derived-assets)
+
+
+$(libs.a): Makefile deps.mk
+$(libs.a): src/$(NAME).go src/version.go
+
+
+$(fuzz-targets/lib.a):
+ go tool compile $(GOCFLAGS) -o $@ -p $(NAME) -d=libfuzzer \
+ $*.go src/$(NAME).go src/version.go
+
+src/version.go: Makefile
+ echo 'package $(NAME); const Version = "$(VERSION)"' > $@
+
+
+
+tests.bin-check = \
+ tests/main.bin-check \
+ $(functional-tests/main.go:.go=.bin-check) \
+
+$(tests.bin-check):
+ $(EXEC)$*.bin
+
+check-unit: $(tests.bin-check)
+
+
+integration-tests = \
+
+.PRECIOUS: $(integration-tests)
+$(integration-tests): $(NAME).bin
+$(integration-tests): ALWAYS
+ sh $@
+
+check-integration: $(integration-tests)
+check-integration: fuzz
+
+
+## Run all tests. Each test suite is isolated, so that a parallel
+## build can run tests at the same time. The required artifacts
+## are created if missing.
+check: check-unit check-integration
+
+
+
+FUZZSEC=1
+fuzz-targets/main.bin-check = $(fuzz-targets/main.go:.go=.bin-check)
+$(fuzz-targets/main.bin-check):
+ $(EXEC)$*.bin --test.fuzztime=$(FUZZSEC)s \
+ --test.fuzz='.*' --test.fuzzcachedir=tests/fuzz/corpus
+
+fuzz: $(fuzz-targets/main.bin-check)
+
+
+
+benchmarks/main.bin-check = $(benchmarks/main.go:.go=.bin-check)
+$(benchmarks/main.bin-check):
+ printf '%s\n' '$(EXEC)$*.bin' > $*.txt
+ LANG=POSIX.UTF-8 time -p $(EXEC)$*.bin 2>> $*.txt
+ printf '%s\n' '$*.txt'
+
+bench: $(benchmarks/main.bin-check)
+
+
+
+## Remove *all* derived artifacts produced during the build.
+## A dedicated test asserts that this is always true.
+clean:
+ rm -rf $(derived-assets) $(side-assets)
+
+
+## Installs into $(DESTDIR)$(PREFIX). Its dependency target
+## ensures that all installable artifacts are crafted beforehand.
+install: all
+ mkdir -p \
+ '$(DESTDIR)$(GOLIBDIR)' \
+ '$(DESTDIR)$(SRCDIR)' \
+
+ cp src/$(NAME).a '$(DESTDIR)$(GOLIBDIR)'
+ cp $(sources) '$(DESTDIR)$(SRCDIR)'
+
+## Uninstalls from $(DESTDIR)$(PREFIX). This is a perfect mirror
+## of the "install" target, and removes *all* that was installed.
+## A dedicated test asserts that this is always true.
+uninstall:
+ rm -rf \
+ '$(DESTDIR)$(GOLIBDIR)'/$(NAME).a \
+ '$(DESTDIR)$(SRCDIR)' \
+
+
+
+ALWAYS:
diff --git a/deps.mk b/deps.mk
new file mode 100644
index 0000000..b7701af
--- /dev/null
+++ b/deps.mk
@@ -0,0 +1,25 @@
+libs.go = \
+ src/pds.go \
+ tests/pds.go \
+
+mains.go = \
+ tests/main.go \
+
+functional-tests/lib.go = \
+
+functional-tests/main.go = \
+
+fuzz-targets/lib.go = \
+
+fuzz-targets/main.go = \
+
+benchmarks/lib.go = \
+
+benchmarks/main.go = \
+
+src/pds.a: src/pds.go
+tests/main.a: tests/main.go
+tests/pds.a: tests/pds.go
+tests/main.bin: tests/main.a
+tests/main.bin-check: tests/main.bin
+tests/main.a: tests/$(NAME).a
diff --git a/mkdeps.sh b/mkdeps.sh
new file mode 100755
index 0000000..e8da8a4
--- /dev/null
+++ b/mkdeps.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+set -eu
+
+export LANG=POSIX.UTF-8
+
+
+libs() {
+ find src tests -name '*.go' | grep -v '/main\.go$' |
+ grep -v '/version\.go$'
+}
+
+mains() {
+ find src tests -name '*.go' | grep '/main\.go$'
+}
+
+libs | varlist 'libs.go'
+mains | varlist 'mains.go'
+
+find tests/functional/*/*.go -not -name main.go | varlist 'functional-tests/lib.go'
+find tests/functional/*/main.go | varlist 'functional-tests/main.go'
+find tests/fuzz/*/*.go -not -name main.go | varlist 'fuzz-targets/lib.go'
+find tests/fuzz/*/main.go | varlist 'fuzz-targets/main.go'
+find tests/benchmarks/*/*.go -not -name main.go | varlist 'benchmarks/lib.go'
+find tests/benchmarks/*/main.go | varlist 'benchmarks/main.go'
+
+{ libs; mains; } | sort | sed 's/^\(.*\)\.go$/\1.a:\t\1.go/'
+mains | sort | sed 's/^\(.*\)\.go$/\1.bin:\t\1.a/'
+mains | sort | sed 's/^\(.*\)\.go$/\1.bin-check:\t\1.bin/'
+mains | sort | sed 's|^\(.*\)/main\.go$|\1/main.a:\t\1/$(NAME).a|'
diff --git a/sets.go b/sets.go
deleted file mode 100644
index b41bd37..0000000
--- a/sets.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package immutable
-
-// Set represents a collection of unique values. The set uses a Hasher
-// to generate hashes and check for equality of key values.
-//
-// Internally, the Set stores values as keys of a Map[T,struct{}]
-type Set[T any] struct {
- m *Map[T, struct{}]
-}
-
-// NewSet returns a new instance of Set.
-//
-// If hasher is nil, a default hasher implementation will automatically be chosen based on the first key added.
-// Default hasher implementations only exist for int, string, and byte slice types.
-// NewSet can also take some initial values as varargs.
-func NewSet[T any](hasher Hasher[T], values ...T) Set[T] {
- m := NewMap[T, struct{}](hasher)
- for _, value := range values {
- m = m.set(value, struct{}{}, true)
- }
- return Set[T]{m}
-}
-
-// Add returns a set containing the new value.
-//
-// This function will return a new set even if the set already contains the value.
-func (s Set[T]) Add(value T) Set[T] {
- return Set[T]{s.m.Set(value, struct{}{})}
-}
-
-// Delete returns a set with the given key removed.
-func (s Set[T]) Delete(value T) Set[T] {
- return Set[T]{s.m.Delete(value)}
-}
-
-// Has returns true when the set contains the given value
-func (s Set[T]) Has(val T) bool {
- _, ok := s.m.Get(val)
- return ok
-}
-
-// Len returns the number of elements in the underlying map.
-func (s Set[K]) Len() int {
- return s.m.Len()
-}
-
-// Items returns a slice of the items inside the set
-func (s Set[T]) Items() []T {
- r := make([]T, 0, s.Len())
- itr := s.Iterator()
- for !itr.Done() {
- v, _ := itr.Next()
- r = append(r, v)
- }
- return r
-}
-
-// Iterator returns a new iterator for this set positioned at the first value.
-func (s Set[T]) Iterator() *SetIterator[T] {
- itr := &SetIterator[T]{mi: s.m.Iterator()}
- itr.mi.First()
- return itr
-}
-
-// SetIterator represents an iterator over a set.
-// Iteration can occur in natural or reverse order based on use of Next() or Prev().
-type SetIterator[T any] struct {
- mi *MapIterator[T, struct{}]
-}
-
-// Done returns true if no more values remain in the iterator.
-func (itr *SetIterator[T]) Done() bool {
- return itr.mi.Done()
-}
-
-// First moves the iterator to the first value.
-func (itr *SetIterator[T]) First() {
- itr.mi.First()
-}
-
-// Next moves the iterator to the next value.
-func (itr *SetIterator[T]) Next() (val T, ok bool) {
- val, _, ok = itr.mi.Next()
- return
-}
-
-type SetBuilder[T any] struct {
- s Set[T]
-}
-
-func NewSetBuilder[T any](hasher Hasher[T]) *SetBuilder[T] {
- return &SetBuilder[T]{s: NewSet(hasher)}
-}
-
-func (s SetBuilder[T]) Set(val T) {
- s.s.m = s.s.m.set(val, struct{}{}, true)
-}
-
-func (s SetBuilder[T]) Delete(val T) {
- s.s.m = s.s.m.delete(val, true)
-}
-
-func (s SetBuilder[T]) Has(val T) bool {
- return s.s.Has(val)
-}
-
-func (s SetBuilder[T]) Len() int {
- return s.s.Len()
-}
-
-type SortedSet[T any] struct {
- m *SortedMap[T, struct{}]
-}
-
-// NewSortedSet returns a new instance of SortedSet.
-//
-// If comparer is nil then
-// a default comparer is set after the first key is inserted. Default comparers
-// exist for int, string, and byte slice keys.
-// NewSortedSet can also take some initial values as varargs.
-func NewSortedSet[T any](comparer Comparer[T], values ...T) SortedSet[T] {
- m := NewSortedMap[T, struct{}](comparer)
- for _, value := range values {
- m = m.set(value, struct{}{}, true)
- }
- return SortedSet[T]{m}
-}
-
-// Add returns a set containing the new value.
-//
-// This function will return a new set even if the set already contains the value.
-func (s SortedSet[T]) Add(value T) SortedSet[T] {
- return SortedSet[T]{s.m.Set(value, struct{}{})}
-}
-
-// Delete returns a set with the given key removed.
-func (s SortedSet[T]) Delete(value T) SortedSet[T] {
- return SortedSet[T]{s.m.Delete(value)}
-}
-
-// Has returns true when the set contains the given value
-func (s SortedSet[T]) Has(val T) bool {
- _, ok := s.m.Get(val)
- return ok
-}
-
-// Len returns the number of elements in the underlying map.
-func (s SortedSet[K]) Len() int {
- return s.m.Len()
-}
-
-// Items returns a slice of the items inside the set
-func (s SortedSet[T]) Items() []T {
- r := make([]T, 0, s.Len())
- itr := s.Iterator()
- for !itr.Done() {
- v, _ := itr.Next()
- r = append(r, v)
- }
- return r
-}
-
-// Iterator returns a new iterator for this set positioned at the first value.
-func (s SortedSet[T]) Iterator() *SortedSetIterator[T] {
- itr := &SortedSetIterator[T]{mi: s.m.Iterator()}
- itr.mi.First()
- return itr
-}
-
-// SortedSetIterator represents an iterator over a sorted set.
-// Iteration can occur in natural or reverse order based on use of Next() or Prev().
-type SortedSetIterator[T any] struct {
- mi *SortedMapIterator[T, struct{}]
-}
-
-// Done returns true if no more values remain in the iterator.
-func (itr *SortedSetIterator[T]) Done() bool {
- return itr.mi.Done()
-}
-
-// First moves the iterator to the first value.
-func (itr *SortedSetIterator[T]) First() {
- itr.mi.First()
-}
-
-// Last moves the iterator to the last value.
-func (itr *SortedSetIterator[T]) Last() {
- itr.mi.Last()
-}
-
-// Next moves the iterator to the next value.
-func (itr *SortedSetIterator[T]) Next() (val T, ok bool) {
- val, _, ok = itr.mi.Next()
- return
-}
-
-// Prev moves the iterator to the previous value.
-func (itr *SortedSetIterator[T]) Prev() (val T, ok bool) {
- val, _, ok = itr.mi.Prev()
- return
-}
-
-// Seek moves the iterator to the given value.
-//
-// If the value does not exist then the next value is used. If no more keys exist
-// then the iterator is marked as done.
-func (itr *SortedSetIterator[T]) Seek(val T) {
- itr.mi.Seek(val)
-}
-
-type SortedSetBuilder[T any] struct {
- s *SortedSet[T]
-}
-
-func NewSortedSetBuilder[T any](comparer Comparer[T]) *SortedSetBuilder[T] {
- s := NewSortedSet(comparer)
- return &SortedSetBuilder[T]{s: &s}
-}
-
-func (s SortedSetBuilder[T]) Set(val T) {
- s.s.m = s.s.m.set(val, struct{}{}, true)
-}
-
-func (s SortedSetBuilder[T]) Delete(val T) {
- s.s.m = s.s.m.delete(val, true)
-}
-
-func (s SortedSetBuilder[T]) Has(val T) bool {
- return s.s.Has(val)
-}
-
-func (s SortedSetBuilder[T]) Len() int {
- return s.s.Len()
-}
-
-// SortedSet returns the current copy of the set.
-// The builder should not be used again after the list after this call.
-func (s SortedSetBuilder[T]) SortedSet() SortedSet[T] {
- assert(s.s != nil, "immutable.SortedSetBuilder.SortedSet(): duplicate call to fetch sorted set")
- set := s.s
- s.s = nil
- return *set
-}
diff --git a/sets_test.go b/sets_test.go
deleted file mode 100644
index 6612cba..0000000
--- a/sets_test.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package immutable
-
-import (
- "testing"
-)
-
-func TestSetsPut(t *testing.T) {
- s := NewSet[string](nil)
- s2 := s.Add("1").Add("1")
- s2.Add("2")
- if s.Len() != 0 {
- t.Fatalf("Unexpected mutation of set")
- }
- if s.Has("1") {
- t.Fatalf("Unexpected set element")
- }
- if s2.Len() != 1 {
- t.Fatalf("Unexpected non-mutation of set")
- }
- if !s2.Has("1") {
- t.Fatalf("Set element missing")
- }
- itr := s2.Iterator()
- counter := 0
- for !itr.Done() {
- i, v := itr.Next()
- t.Log(i, v)
- counter++
- }
- if counter != 1 {
- t.Fatalf("iterator wrong length")
- }
-}
-
-func TestSetsDelete(t *testing.T) {
- s := NewSet[string](nil)
- s2 := s.Add("1")
- s3 := s.Delete("1")
- if s2.Len() != 1 {
- t.Fatalf("Unexpected non-mutation of set")
- }
- if !s2.Has("1") {
- t.Fatalf("Set element missing")
- }
- if s3.Len() != 0 {
- t.Fatalf("Unexpected set length after delete")
- }
- if s3.Has("1") {
- t.Fatalf("Unexpected set element after delete")
- }
-}
-
-func TestSortedSetsPut(t *testing.T) {
- s := NewSortedSet[string](nil)
- s2 := s.Add("1").Add("1").Add("0")
- if s.Len() != 0 {
- t.Fatalf("Unexpected mutation of set")
- }
- if s.Has("1") {
- t.Fatalf("Unexpected set element")
- }
- if s2.Len() != 2 {
- t.Fatalf("Unexpected non-mutation of set")
- }
- if !s2.Has("1") {
- t.Fatalf("Set element missing")
- }
-
- itr := s2.Iterator()
- counter := 0
- for !itr.Done() {
- i, v := itr.Next()
- t.Log(i, v)
- if counter == 0 && i != "0" {
- t.Fatalf("sort did not work for first el")
- }
- if counter == 1 && i != "1" {
- t.Fatalf("sort did not work for second el")
- }
- counter++
- }
- if counter != 2 {
- t.Fatalf("iterator wrong length")
- }
-}
-
-func TestSortedSetsDelete(t *testing.T) {
- s := NewSortedSet[string](nil)
- s2 := s.Add("1")
- s3 := s.Delete("1")
- if s2.Len() != 1 {
- t.Fatalf("Unexpected non-mutation of set")
- }
- if !s2.Has("1") {
- t.Fatalf("Set element missing")
- }
- if s3.Len() != 0 {
- t.Fatalf("Unexpected set length after delete")
- }
- if s3.Has("1") {
- t.Fatalf("Unexpected set element after delete")
- }
-}
-
-func TestSortedSetBuilder(t *testing.T) {
- b := NewSortedSetBuilder[string](nil)
- b.Set("test3")
- b.Set("test1")
- b.Set("test2")
-
- s := b.SortedSet()
- items := s.Items()
-
- if len(items) != 3 {
- t.Fatalf("Set has wrong number of items")
- }
- if items[0] != "test1" {
- t.Fatalf("First item incorrectly sorted")
- }
- if items[1] != "test2" {
- t.Fatalf("Second item incorrectly sorted")
- }
- if items[2] != "test3" {
- t.Fatalf("Third item incorrectly sorted")
- }
-}
diff --git a/immutable.go b/src/pds.go
index 1642de3..0ce9ade 100644
--- a/immutable.go
+++ b/src/pds.go
@@ -39,16 +39,15 @@
// then simply pass a nil into the constructor. Otherwise you will need to
// implement a custom Hasher or Comparer type. Please see the provided
// implementations for reference.
-package immutable
+package pds
import (
+ "cmp"
"fmt"
"math/bits"
"reflect"
"sort"
"strings"
-
- "golang.org/x/exp/constraints"
)
// List is a dense, ordered, indexed collections. They are analogous to slices
@@ -2416,7 +2415,7 @@ func (c *defaultComparer[K]) Compare(i K, j K) int {
// defaultCompare only operates on constraints.Ordered.
// For other types, users should bring their own comparers
-func defaultCompare[K constraints.Ordered](i, j K) int {
+func defaultCompare[K cmp.Ordered](i, j K) int {
if i < j {
return -1
} else if i > j {
@@ -2457,3 +2456,245 @@ func assert(condition bool, message string) {
panic(message)
}
}
+
+// Set represents a collection of unique values. The set uses a Hasher
+// to generate hashes and check for equality of key values.
+//
+// Internally, the Set stores values as keys of a Map[T,struct{}]
+type Set[T any] struct {
+ m *Map[T, struct{}]
+}
+
+// NewSet returns a new instance of Set.
+//
+// If hasher is nil, a default hasher implementation will automatically be chosen based on the first key added.
+// Default hasher implementations only exist for int, string, and byte slice types.
+// NewSet can also take some initial values as varargs.
+func NewSet[T any](hasher Hasher[T], values ...T) Set[T] {
+ m := NewMap[T, struct{}](hasher)
+ for _, value := range values {
+ m = m.set(value, struct{}{}, true)
+ }
+ return Set[T]{m}
+}
+
+// Add returns a set containing the new value.
+//
+// This function will return a new set even if the set already contains the value.
+func (s Set[T]) Add(value T) Set[T] {
+ return Set[T]{s.m.Set(value, struct{}{})}
+}
+
+// Delete returns a set with the given key removed.
+func (s Set[T]) Delete(value T) Set[T] {
+ return Set[T]{s.m.Delete(value)}
+}
+
+// Has returns true when the set contains the given value
+func (s Set[T]) Has(val T) bool {
+ _, ok := s.m.Get(val)
+ return ok
+}
+
+// Len returns the number of elements in the underlying map.
+func (s Set[K]) Len() int {
+ return s.m.Len()
+}
+
+// Items returns a slice of the items inside the set
+func (s Set[T]) Items() []T {
+ r := make([]T, 0, s.Len())
+ itr := s.Iterator()
+ for !itr.Done() {
+ v, _ := itr.Next()
+ r = append(r, v)
+ }
+ return r
+}
+
+// Iterator returns a new iterator for this set positioned at the first value.
+func (s Set[T]) Iterator() *SetIterator[T] {
+ itr := &SetIterator[T]{mi: s.m.Iterator()}
+ itr.mi.First()
+ return itr
+}
+
+// SetIterator represents an iterator over a set.
+// Iteration can occur in natural or reverse order based on use of Next() or Prev().
+type SetIterator[T any] struct {
+ mi *MapIterator[T, struct{}]
+}
+
+// Done returns true if no more values remain in the iterator.
+func (itr *SetIterator[T]) Done() bool {
+ return itr.mi.Done()
+}
+
+// First moves the iterator to the first value.
+func (itr *SetIterator[T]) First() {
+ itr.mi.First()
+}
+
+// Next moves the iterator to the next value.
+func (itr *SetIterator[T]) Next() (val T, ok bool) {
+ val, _, ok = itr.mi.Next()
+ return
+}
+
+type SetBuilder[T any] struct {
+ s Set[T]
+}
+
+func NewSetBuilder[T any](hasher Hasher[T]) *SetBuilder[T] {
+ return &SetBuilder[T]{s: NewSet(hasher)}
+}
+
+func (s SetBuilder[T]) Set(val T) {
+ s.s.m = s.s.m.set(val, struct{}{}, true)
+}
+
+func (s SetBuilder[T]) Delete(val T) {
+ s.s.m = s.s.m.delete(val, true)
+}
+
+func (s SetBuilder[T]) Has(val T) bool {
+ return s.s.Has(val)
+}
+
+func (s SetBuilder[T]) Len() int {
+ return s.s.Len()
+}
+
+type SortedSet[T any] struct {
+ m *SortedMap[T, struct{}]
+}
+
+// NewSortedSet returns a new instance of SortedSet.
+//
+// If comparer is nil then
+// a default comparer is set after the first key is inserted. Default comparers
+// exist for int, string, and byte slice keys.
+// NewSortedSet can also take some initial values as varargs.
+func NewSortedSet[T any](comparer Comparer[T], values ...T) SortedSet[T] {
+ m := NewSortedMap[T, struct{}](comparer)
+ for _, value := range values {
+ m = m.set(value, struct{}{}, true)
+ }
+ return SortedSet[T]{m}
+}
+
+// Add returns a set containing the new value.
+//
+// This function will return a new set even if the set already contains the value.
+func (s SortedSet[T]) Add(value T) SortedSet[T] {
+ return SortedSet[T]{s.m.Set(value, struct{}{})}
+}
+
+// Delete returns a set with the given key removed.
+func (s SortedSet[T]) Delete(value T) SortedSet[T] {
+ return SortedSet[T]{s.m.Delete(value)}
+}
+
+// Has returns true when the set contains the given value
+func (s SortedSet[T]) Has(val T) bool {
+ _, ok := s.m.Get(val)
+ return ok
+}
+
+// Len returns the number of elements in the underlying map.
+func (s SortedSet[K]) Len() int {
+ return s.m.Len()
+}
+
+// Items returns a slice of the items inside the set
+func (s SortedSet[T]) Items() []T {
+ r := make([]T, 0, s.Len())
+ itr := s.Iterator()
+ for !itr.Done() {
+ v, _ := itr.Next()
+ r = append(r, v)
+ }
+ return r
+}
+
+// Iterator returns a new iterator for this set positioned at the first value.
+func (s SortedSet[T]) Iterator() *SortedSetIterator[T] {
+ itr := &SortedSetIterator[T]{mi: s.m.Iterator()}
+ itr.mi.First()
+ return itr
+}
+
+// SortedSetIterator represents an iterator over a sorted set.
+// Iteration can occur in natural or reverse order based on use of Next() or Prev().
+type SortedSetIterator[T any] struct {
+ mi *SortedMapIterator[T, struct{}]
+}
+
+// Done returns true if no more values remain in the iterator.
+func (itr *SortedSetIterator[T]) Done() bool {
+ return itr.mi.Done()
+}
+
+// First moves the iterator to the first value.
+func (itr *SortedSetIterator[T]) First() {
+ itr.mi.First()
+}
+
+// Last moves the iterator to the last value.
+func (itr *SortedSetIterator[T]) Last() {
+ itr.mi.Last()
+}
+
+// Next moves the iterator to the next value.
+func (itr *SortedSetIterator[T]) Next() (val T, ok bool) {
+ val, _, ok = itr.mi.Next()
+ return
+}
+
+// Prev moves the iterator to the previous value.
+func (itr *SortedSetIterator[T]) Prev() (val T, ok bool) {
+ val, _, ok = itr.mi.Prev()
+ return
+}
+
+// Seek moves the iterator to the given value.
+//
+// If the value does not exist then the next value is used. If no more keys exist
+// then the iterator is marked as done.
+func (itr *SortedSetIterator[T]) Seek(val T) {
+ itr.mi.Seek(val)
+}
+
+type SortedSetBuilder[T any] struct {
+ s *SortedSet[T]
+}
+
+func NewSortedSetBuilder[T any](comparer Comparer[T]) *SortedSetBuilder[T] {
+ s := NewSortedSet(comparer)
+ return &SortedSetBuilder[T]{s: &s}
+}
+
+func (s SortedSetBuilder[T]) Set(val T) {
+ s.s.m = s.s.m.set(val, struct{}{}, true)
+}
+
+func (s SortedSetBuilder[T]) Delete(val T) {
+ s.s.m = s.s.m.delete(val, true)
+}
+
+func (s SortedSetBuilder[T]) Has(val T) bool {
+ return s.s.Has(val)
+}
+
+func (s SortedSetBuilder[T]) Len() int {
+ return s.s.Len()
+}
+
+// SortedSet returns the current copy of the set.
+// The builder should not be used again after the list after this call.
+func (s SortedSetBuilder[T]) SortedSet() SortedSet[T] {
+ assert(s.s != nil, "immutable.SortedSetBuilder.SortedSet(): duplicate call to fetch sorted set")
+ set := s.s
+ s.s = nil
+ return *set
+}
diff --git a/tests/main.go b/tests/main.go
new file mode 100644
index 0000000..db923fd
--- /dev/null
+++ b/tests/main.go
@@ -0,0 +1,7 @@
+package main
+
+import "pds"
+
+func main() {
+ pds.MainTest()
+}
diff --git a/immutable_test.go b/tests/pds.go
index 581d4d8..ad74531 100644
--- a/immutable_test.go
+++ b/tests/pds.go
@@ -1,13 +1,12 @@
-package immutable
+package pds
import (
+ "cmp"
"flag"
"fmt"
"math/rand"
"sort"
"testing"
-
- "golang.org/x/exp/constraints"
)
var (
@@ -2131,7 +2130,7 @@ func TestNewHasher(t *testing.T) {
})
}
-func testNewHasher[V constraints.Ordered](t *testing.T, v V) {
+func testNewHasher[V cmp.Ordered](t *testing.T, v V) {
t.Helper()
h := NewHasher(v)
h.Hash(v)
@@ -2170,7 +2169,7 @@ func TestNewComparer(t *testing.T) {
})
}
-func testNewComparer[T constraints.Ordered](t *testing.T, x, y T) {
+func testNewComparer[T cmp.Ordered](t *testing.T, x, y T) {
t.Helper()
c := NewComparer(x)
if c.Compare(x, y) != -1 {
@@ -2518,7 +2517,7 @@ func uniqueIntSlice(a []int) []int {
}
// mockHasher represents a mock implementation of immutable.Hasher.
-type mockHasher[K constraints.Ordered] struct {
+type mockHasher[K cmp.Ordered] struct {
hash func(value K) uint32
equal func(a, b K) bool
}
@@ -2534,7 +2533,7 @@ func (h *mockHasher[K]) Equal(a, b K) bool {
}
// mockComparer represents a mock implementation of immutable.Comparer.
-type mockComparer[K constraints.Ordered] struct {
+type mockComparer[K cmp.Ordered] struct {
compare func(a, b K) int
}
@@ -2542,3 +2541,129 @@ type mockComparer[K constraints.Ordered] struct {
func (h *mockComparer[K]) Compare(a, b K) int {
return h.compare(a, b)
}
+
+func TestSetsPut(t *testing.T) {
+ s := NewSet[string](nil)
+ s2 := s.Add("1").Add("1")
+ s2.Add("2")
+ if s.Len() != 0 {
+ t.Fatalf("Unexpected mutation of set")
+ }
+ if s.Has("1") {
+ t.Fatalf("Unexpected set element")
+ }
+ if s2.Len() != 1 {
+ t.Fatalf("Unexpected non-mutation of set")
+ }
+ if !s2.Has("1") {
+ t.Fatalf("Set element missing")
+ }
+ itr := s2.Iterator()
+ counter := 0
+ for !itr.Done() {
+ i, v := itr.Next()
+ t.Log(i, v)
+ counter++
+ }
+ if counter != 1 {
+ t.Fatalf("iterator wrong length")
+ }
+}
+
+func TestSetsDelete(t *testing.T) {
+ s := NewSet[string](nil)
+ s2 := s.Add("1")
+ s3 := s.Delete("1")
+ if s2.Len() != 1 {
+ t.Fatalf("Unexpected non-mutation of set")
+ }
+ if !s2.Has("1") {
+ t.Fatalf("Set element missing")
+ }
+ if s3.Len() != 0 {
+ t.Fatalf("Unexpected set length after delete")
+ }
+ if s3.Has("1") {
+ t.Fatalf("Unexpected set element after delete")
+ }
+}
+
+func TestSortedSetsPut(t *testing.T) {
+ s := NewSortedSet[string](nil)
+ s2 := s.Add("1").Add("1").Add("0")
+ if s.Len() != 0 {
+ t.Fatalf("Unexpected mutation of set")
+ }
+ if s.Has("1") {
+ t.Fatalf("Unexpected set element")
+ }
+ if s2.Len() != 2 {
+ t.Fatalf("Unexpected non-mutation of set")
+ }
+ if !s2.Has("1") {
+ t.Fatalf("Set element missing")
+ }
+
+ itr := s2.Iterator()
+ counter := 0
+ for !itr.Done() {
+ i, v := itr.Next()
+ t.Log(i, v)
+ if counter == 0 && i != "0" {
+ t.Fatalf("sort did not work for first el")
+ }
+ if counter == 1 && i != "1" {
+ t.Fatalf("sort did not work for second el")
+ }
+ counter++
+ }
+ if counter != 2 {
+ t.Fatalf("iterator wrong length")
+ }
+}
+
+func TestSortedSetsDelete(t *testing.T) {
+ s := NewSortedSet[string](nil)
+ s2 := s.Add("1")
+ s3 := s.Delete("1")
+ if s2.Len() != 1 {
+ t.Fatalf("Unexpected non-mutation of set")
+ }
+ if !s2.Has("1") {
+ t.Fatalf("Set element missing")
+ }
+ if s3.Len() != 0 {
+ t.Fatalf("Unexpected set length after delete")
+ }
+ if s3.Has("1") {
+ t.Fatalf("Unexpected set element after delete")
+ }
+}
+
+func TestSortedSetBuilder(t *testing.T) {
+ b := NewSortedSetBuilder[string](nil)
+ b.Set("test3")
+ b.Set("test1")
+ b.Set("test2")
+
+ s := b.SortedSet()
+ items := s.Items()
+
+ if len(items) != 3 {
+ t.Fatalf("Set has wrong number of items")
+ }
+ if items[0] != "test1" {
+ t.Fatalf("First item incorrectly sorted")
+ }
+ if items[1] != "test2" {
+ t.Fatalf("Second item incorrectly sorted")
+ }
+ if items[2] != "test3" {
+ t.Fatalf("Third item incorrectly sorted")
+ }
+}
+
+
+
+func MainTest() {
+}