diff options
| author | EuAndreh <eu@euandre.org> | 2024-12-14 10:30:47 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2024-12-14 10:30:47 -0300 |
| commit | 06eaf1738494a9e783ed100565911d08efaae826 (patch) | |
| tree | a372bcc99fed70e1cf298eeddef1ec134b178102 | |
| parent | Remove extraneous files (diff) | |
| download | pds-06eaf1738494a9e783ed100565911d08efaae826.tar.gz pds-06eaf1738494a9e783ed100565911d08efaae826.tar.xz | |
Add Makefile and move files to structured folders
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | Makefile | 159 | ||||
| -rw-r--r-- | deps.mk | 25 | ||||
| -rwxr-xr-x | mkdeps.sh | 29 | ||||
| -rw-r--r-- | sets.go | 243 | ||||
| -rw-r--r-- | sets_test.go | 126 | ||||
| -rw-r--r-- | src/pds.go (renamed from immutable.go) | 249 | ||||
| -rw-r--r-- | tests/main.go | 7 | ||||
| -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: @@ -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() { +} |
