diff options
author | Sasha Smundak <asmundak@google.com> | 2019-12-16 13:54:08 -0800 |
---|---|---|
committer | Sasha Smundak <asmundak@google.com> | 2019-12-16 15:10:25 -0800 |
commit | 0bb095d895baddf8eb21c0347c091518b8aa0a18 (patch) | |
tree | 6989a259f6ea7965bbb55418c8ce004e2f3a92db | |
parent | 5e285379e96f481eeb0659e0ed3cbc3f92269a18 (diff) | |
parent | 7c6678a70f6812a0abfb9d71d21d74ee24af01f9 (diff) | |
download | go-creachadair-stringset-0bb095d895baddf8eb21c0347c091518b8aa0a18.tar.gz |
Populate from upstream-master, add mandatory files
Test: build on build-tools branch
Bug: 137798757
Change-Id: I19cbc3579f861519b5f53350df91c730c45fdc87
-rw-r--r-- | LICENSE | 27 | ||||
-rw-r--r-- | METADATA | 18 | ||||
-rw-r--r-- | MODULE_LICENSE_BSD | 0 | ||||
l--------- | NOTICE | 1 | ||||
-rw-r--r-- | README.md | 25 | ||||
-rw-r--r-- | bitbucket-pipelines.yml | 26 | ||||
-rw-r--r-- | examples_test.go | 186 | ||||
-rw-r--r-- | go.mod | 8 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | makeset/core.go.in | 454 | ||||
-rw-r--r-- | makeset/core_test.go.in | 680 | ||||
-rw-r--r-- | makeset/intset.toml | 8 | ||||
-rw-r--r-- | makeset/makeset.go | 182 | ||||
-rw-r--r-- | makeset/nodeset.toml | 28 | ||||
-rw-r--r-- | makeset/static.go | 200 | ||||
-rw-r--r-- | makeset/stringset.toml | 11 | ||||
-rw-r--r-- | stringset.go | 444 | ||||
-rw-r--r-- | stringset_test.go | 681 |
18 files changed, 2983 insertions, 0 deletions
@@ -0,0 +1,27 @@ +Copyright (c) 2014, Michael J. Fromberger +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..e154ca5 --- /dev/null +++ b/METADATA @@ -0,0 +1,18 @@ +name: "go-creachadair-stringset" +description: + "The stringset package implements a lightweight set-of-strings type " + "based around Go's built-in map type." + +third_party { + url { + type: HOMEPAGE + value: "https://bitbucket.org/creachadair/stringset/" + } + url { + type: GIT + value: "https://bitbucket.org/creachadair/stringset/" + } + version: "v0.0.3" + last_upgrade_date { year: 2019 month: 11 day: 22 } + license_type: NOTICE +} diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_BSD @@ -0,0 +1 @@ +LICENSE
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..45a1bb5 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# stringset + +http://godoc.org/bitbucket.org/creachadair/stringset + +[![Go Report Card](https://goreportcard.com/badge/bitbucket.org/creachadair/stringset)](https://goreportcard.com/report/bitbucket.org/creachadair/stringset) + +The `stringset` package implements a lightweight set-of-strings type based +around Go's built-in map type. + +## Generating the Code + +The `stringset` package is generated by the `makeset` program from source +templates `core.go.in` (the main package source) and `core_test.go.in` (for the +unit tests). If you need to modify the templates, edit those files and run: + +```shell +go generate ./makeset +``` + +to update the `static.go` file. You can then re-generate the `stringset` +package by running: + +```shell +go run ./makeset -config makeset/stringset.toml -output . +``` diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 0000000..b6631b5 --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,26 @@ +definitions: + steps: + - step: &Verify + script: + - PACKAGE_PATH="${GOPATH}/src/bitbucket.org/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}" + - mkdir -pv "${PACKAGE_PATH}" + - tar -cO --exclude-vcs --exclude=bitbucket-pipelines.yml . | tar -xv -C "${PACKAGE_PATH}" + - cd "${PACKAGE_PATH}" + - go version # log the version of Go we are using in this step + - export GO111MODULE=on # enable modules inside $GOPATH + - go get -v ./... + - go build -v ./... + - go test -v -race -cpu=1,4 ./... + - go vet -v ./... + +pipelines: + default: # run on each push + - step: + image: golang:1.10 + <<: *Verify + - step: + image: golang:1.11 + <<: *Verify + - step: + image: golang:1.12 + <<: *Verify diff --git a/examples_test.go b/examples_test.go new file mode 100644 index 0000000..47b9850 --- /dev/null +++ b/examples_test.go @@ -0,0 +1,186 @@ +package stringset_test + +import ( + "fmt" + "path/filepath" + "regexp" + "strings" + + "bitbucket.org/creachadair/stringset" +) + +func ExampleSet_Intersect() { + a := stringset.New("one", "two", "three") + b := stringset.New("two", "four", "six") + fmt.Println(a.Intersect(b)) + // Output: {"two"} +} + +func ExampleSet_Union() { + s := stringset.New("0", "1", "2").Union(stringset.New("x")) + fmt.Println(s) + // Output: {"0", "1", "2", "x"} +} + +func ExampleSet_Discard() { + nat := stringset.New("0", "1", "2", "3", "4") + ok := nat.Discard("2", "4", "6") + fmt.Println(ok, nat) + // Output: true {"0", "1", "3"} +} + +func ExampleSet_Add() { + s := stringset.New("A", "B") + s.Add("B", "C", "D") + fmt.Println(s) + // Output: {"A", "B", "C", "D"} +} + +func ExampleSet_Select() { + re := regexp.MustCompile(`[a-z]\d+`) + s := stringset.New("a", "b15", "c9", "q").Select(re.MatchString) + fmt.Println(s) + // Output: {"b15", "c9"} +} + +func ExampleSet_Choose() { + s := stringset.New("a", "ab", "abc", "abcd") + long, ok := s.Choose(func(c string) bool { + return len(c) > 3 + }) + fmt.Println(long, ok) + // Output: abcd true +} + +func ExampleSet_Contains() { + s := stringset.New("a", "b", "c", "d", "e") + ae := s.Contains("a", "e") // all present + bdx := s.Contains("b", "d", "x") // x missing + fmt.Println(ae, bdx) + // Output: true false +} + +func ExampleSet_ContainsAny() { + s := stringset.New("a", "b", "c") + fm := s.ContainsAny("f", "m") // all missing + bdx := s.ContainsAny("b", "d", "x") // b present + fmt.Println(fm, bdx) + // Output: false true +} + +func ExampleSet_Diff() { + a := stringset.New("a", "b", "c") + v := stringset.New("a", "e", "i") + fmt.Println(a.Diff(v)) + // Output: {"b", "c"} +} + +func ExampleSet_Each() { + sum := 0 + stringset.New("one", "two", "three").Each(func(s string) { + sum += len(s) + }) + fmt.Println(sum) + // Output: 11 +} + +func ExampleSet_Pop() { + s := stringset.New("a", "bc", "def", "ghij") + p, ok := s.Pop(func(s string) bool { + return len(s) == 2 + }) + fmt.Println(p, ok, s) + // Output: bc true {"a", "def", "ghij"} +} + +func ExampleSet_Partition() { + s := stringset.New("aba", "d", "qpc", "ff") + a, b := s.Partition(func(s string) bool { + return s[0] == s[len(s)-1] + }) + fmt.Println(a, b) + // Output: {"aba", "d", "ff"} {"qpc"} +} + +func ExampleSet_SymDiff() { + s := stringset.New("a", "b", "c") + t := stringset.New("a", "c", "t") + fmt.Println(s.SymDiff(t)) + // Output: {"b", "t"} +} + +func ExampleContains_slice() { + s := strings.Fields("four fine fat fishes fly far") + fmt.Println(stringset.Contains(s, "fishes")) + // Output: + // true +} + +func ExampleContains_map() { + s := map[string]int{"apples": 12, "pears": 2, "plums": 0, "cherries": 18} + fmt.Println(stringset.Contains(s, "pears")) + // Output: + // true +} + +func ExampleContains_set() { + s := stringset.New("lead", "iron", "copper", "chromium") + fmt.Println(stringset.Contains(s, "chromium")) + // Output: + // true +} + +func ExampleFromKeys() { + s := stringset.FromKeys(map[string]int{ + "one": 1, + "two": 2, + "three": 3, + }) + fmt.Println(s) + // Output: {"one", "three", "two"} +} + +func ExampleFromIndexed() { + type T struct { + Event string + Probability float64 + } + events := []T{ + {"heads", 0.625}, + {"tails", 0.370}, + {"edge", 0.005}, + } + s := stringset.FromIndexed(len(events), func(i int) string { + return events[i].Event + }) + fmt.Println(s) + // Output: {"edge", "heads", "tails"} +} + +func ExampleFromValues() { + s := stringset.FromValues(map[int]string{ + 1: "red", + 2: "green", + 3: "red", + 4: "blue", + 5: "green", + }) + fmt.Println(s) + // Output: {"blue", "green", "red"} +} + +func ExampleIndex() { + s := strings.Fields("full plate and packing steel") + fmt.Println(stringset.Index("plate", s)) + fmt.Println(stringset.Index("spoon", s)) + // Output: + // 1 + // -1 +} + +func ExampleSet_Map() { + names := stringset.New("stdio.h", "main.cc", "lib.go", "BUILD", "fixup.py") + fmt.Println(names.Map(filepath.Ext)) + // Output: + // {"", ".cc", ".go", ".h", ".py"} +} @@ -0,0 +1,8 @@ +module bitbucket.org/creachadair/stringset + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/creachadair/staticfile v0.1.2 +) @@ -0,0 +1,4 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/creachadair/staticfile v0.1.2 h1:QG0u27/Ietu0UVOk1aMbF6jrWrEzPIdZP4ju3c1PPfY= +github.com/creachadair/staticfile v0.1.2/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= diff --git a/makeset/core.go.in b/makeset/core.go.in new file mode 100644 index 0000000..65bb6f1 --- /dev/null +++ b/makeset/core.go.in @@ -0,0 +1,454 @@ +{{/* The main source for the package, including doc comments. */ -}} +// Package {{.Package}} implements a lightweight (finite) set of {{.Type}} values +// based on Go's built-in map. A Set provides some convenience methods for +// common set operations. +// +// A nil Set is ready for use as an empty set. The basic set methods (Diff, +// Intersect, Union, IsSubset, Map, Choose, Partition) do not mutate their +// arguments. There are also mutating operations (Add, Discard, Pop, Remove, +// Update) that modify their receiver in-place. +// +// A Set can also be traversed and modified using the normal map operations. +// Being a map, a Set is not safe for concurrent access by multiple goroutines +// unless all the concurrent accesses are reads. +package {{.Package}} + +import ( +{{range .Imports}}{{printf "%q" .}} +{{end}} +) + +{{if .Decl}} +// {{.Type}} is the type of the elements of the set. +type {{.Type}} {{.Decl}}{{end}} + +{{if .Less}} +// isLess reports whether x is less than y in standard order. +func isLess(x, y {{.Type}}) bool { + {{.Less}} +}{{end}} + +{{if .ToString}}func toString(x {{.Type}}) string { + {{.ToString}} +}{{end}} + +// A Set represents a set of {{.Type}} values. A nil Set is a valid +// representation of an empty set. +type Set map[{{.Type}}]struct{} + +// byElement satisfies sort.Interface to order values of type {{.Type}}. +type byElement []{{.Type}} +func(e byElement) Len() int { return len(e) } +func (e byElement) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e byElement) Less(i, j int) bool { + {{if .Less}}return isLess(e[i], e[j]){{else}}return e[i] < e[j]{{end}} +} + +// String implements the fmt.Stringer interface. It renders s in standard set +// notation, e.g., ø for an empty set, {a, b, c} for a nonempty one. +func (s Set) String() string { + if s.Empty() { + return "ø" + } + elts := make([]string, len(s)) + for i, elt := range s.Elements() { + elts[i] = {{if .ToString}}toString{{else}}fmt.Sprint{{end}}(elt) + } + return "{" + strings.Join(elts, ", ") + "}" +} + +// New returns a new set containing exactly the specified elements. +// Returns a non-nil empty Set if no elements are specified. +func New(elts ...{{.Type}}) Set { + set := make(Set, len(elts)) + for _, elt := range elts { + set[elt] = struct{}{} + } + return set +} + +// NewSize returns a new empty set pre-sized to hold at least n elements. +// This is equivalent to make(Set, n) and will panic if n < 0. +func NewSize(n int) Set { return make(Set, n) } + +// Len returns the number of elements in s. +func (s Set) Len() int { return len(s) } + +// Elements returns an ordered slice of the elements in s. +func (s Set) Elements() []{{.Type}} { + elts := s.Unordered() + sort.Sort(byElement(elts)) + return elts +} + +// Unordered returns an unordered slice of the elements in s. +func (s Set) Unordered() []{{.Type}} { + if len(s) == 0 { + return nil + } + elts := make([]{{.Type}}, 0, len(s)) + for elt := range s { + elts = append(elts, elt) + } + return elts +} + +// Clone returns a new Set distinct from s, containing the same elements. +func (s Set) Clone() Set { + var c Set + c.Update(s) + return c +} + +// ContainsAny reports whether s contains one or more of the given elements. +// It is equivalent in meaning to +// s.Intersects({{.Package}}.New(elts...)) +// but does not construct an intermediate set. +func (s Set) ContainsAny(elts ...{{.Type}}) bool { + for _, key := range elts { + if _, ok := s[key]; ok { + return true + } + } + return false +} + +// Contains reports whether s contains (all) the given elements. +// It is equivalent in meaning to +// New(elts...).IsSubset(s) +// but does not construct an intermediate set. +func (s Set) Contains(elts ...{{.Type}}) bool { + for _, elt := range elts { + if _, ok := s[elt]; !ok { + return false + } + } + return true +} + +// IsSubset reports whether s is a subset of s2, s ⊆ s2. +func (s Set) IsSubset(s2 Set) bool { + if s.Empty() { + return true + } else if len(s) > len(s2) { + return false + } + for k := range s { + if _, ok := s2[k]; !ok { + return false + } + } + return true +} + +// Equals reports whether s is equal to s2, having exactly the same elements. +func (s Set) Equals(s2 Set) bool { return len(s) == len(s2) && s.IsSubset(s2) } + +// Empty reports whether s is empty. +func (s Set) Empty() bool { return len(s) == 0 } + +// Intersects reports whether the intersection s ∩ s2 is non-empty, without +// explicitly constructing the intersection. +func (s Set) Intersects(s2 Set) bool { + a, b := s, s2 + if len(b) < len(a) { + a, b = b, a // Iterate over the smaller set + } + for k := range a { + if _, ok := b[k]; ok { + return true + } + } + return false +} + +// Union constructs the union s ∪ s2. +func (s Set) Union(s2 Set) Set { + if s.Empty() { + return s2 + } else if s2.Empty() { + return s + } + set := make(Set) + for k := range s { + set[k] = struct{}{} + } + for k := range s2 { + set[k] = struct{}{} + } + return set +} + +// Intersect constructs the intersection s ∩ s2. +func (s Set) Intersect(s2 Set) Set { + if s.Empty() || s2.Empty() { + return nil + } + set := make(Set) + for k := range s { + if _, ok := s2[k]; ok { + set[k] = struct{}{} + } + } + if len(set) == 0 { + return nil + } + return set +} + +// Diff constructs the set difference s \ s2. +func (s Set) Diff(s2 Set) Set { + if s.Empty() || s2.Empty() { + return s + } + set := make(Set) + for k := range s { + if _, ok := s2[k]; !ok { + set[k] = struct{}{} + } + } + if len(set) == 0 { + return nil + } + return set +} + +// SymDiff constructs the symmetric difference s ∆ s2. +// It is equivalent in meaning to (s ∪ s2) \ (s ∩ s2). +func (s Set) SymDiff(s2 Set) Set { + return s.Union(s2).Diff(s.Intersect(s2)) +} + +// Update adds the elements of s2 to *s in-place, and reports whether anything +// was added. +// If *s == nil and s2 ≠ ø, a new set is allocated that is a copy of s2. +func (s *Set) Update(s2 Set) bool { + in := len(*s) + if *s == nil && len(s2) > 0 { + *s = make(Set) + } + for k := range s2 { + (*s)[k] = struct{}{} + } + return len(*s) != in +} + +// Add adds the specified elements to *s in-place and reports whether anything +// was added. If *s == nil, a new set equivalent to New(ss...) is stored in *s. +func (s *Set) Add(ss ...{{.Type}}) bool { + in := len(*s) + if *s == nil { + *s = make(Set) + } + for _, key := range ss { + (*s)[key] = struct{}{} + } + return len(*s) != in +} + +// Remove removes the elements of s2 from s in-place and reports whether +// anything was removed. +// +// Equivalent to s = s.Diff(s2), but does not allocate a new set. +func (s Set) Remove(s2 Set) bool { + in := s.Len() + if !s.Empty() { + for k := range s2 { + delete(s, k) + } + } + return s.Len() != in +} + +// Discard removes the elements of elts from s in-place and reports whether +// anything was removed. +// +// Equivalent to s.Remove(New(elts...)), but does not allocate an intermediate +// set for ss. +func (s Set) Discard(elts ...{{.Type}}) bool { + in := s.Len() + if !s.Empty() { + for _, elt := range elts { + delete(s, elt) + } + } + return s.Len() != in +} + +// Index returns the first offset of needle in elts, if it occurs; otherwise -1. +func Index(needle {{.Type}}, elts []{{.Type}}) int { + for i, elt := range elts { + if elt == needle { + return i + } + } + return -1 +} + +// Contains reports whether v contains s, for v having type Set, []{{.Type}}, +// map[{{.Type}}]T, or Keyer. It returns false if v's type does not have one of +// these forms. +func Contains(v interface{}, s {{.Type}}) bool { + switch t := v.(type) { + case []{{.Type}}: + return Index(s, t) >= 0 + case Set: + return t.Contains(s) + case Keyer: + return Index(s, t.Keys()) >= 0 + } + if m := reflect.ValueOf(v); m.IsValid() && m.Kind() == reflect.Map && m.Type().Key() == refType { + return m.MapIndex(reflect.ValueOf(s)).IsValid() + } + return false +} + +// A Keyer implements a Keys method that returns the keys of a collection such +// as a map or a Set. +type Keyer interface { + // Keys returns the keys of the receiver, which may be nil. + Keys() []{{.Type}} +} + +var refType = reflect.TypeOf((*{{.Type}})(nil)).Elem() + +// FromKeys returns a Set of {{.Type}}s from v, which must either be a {{.Type}}, +// a []{{.Type}}, a map[{{.Type}}]T, or a Keyer. It returns nil if v's type does +// not have one of these forms. +func FromKeys(v interface{}) Set { + var result Set + switch t := v.(type) { + case {{.Type}}: + return New(t) + case []{{.Type}}: + for _, key := range t { + result.Add(key) + } + return result + case map[{{.Type}}]struct{}: // includes Set + for key := range t { + result.Add(key) + } + return result + case Keyer: + for _, key := range t.Keys() { + result.Add(key) + } + return result + case nil: + return nil + } + m := reflect.ValueOf(v) + if m.Kind() != reflect.Map || m.Type().Key() != refType { + return nil + } + for _, key := range m.MapKeys() { + result.Add(key.Interface().({{.Type}})) + } + return result +} + +// FromIndexed returns a Set constructed from the values of f(i) for +// each 0 ≤ i < n. If n ≤ 0 the result is nil. +func FromIndexed(n int, f func(int) {{.Type}}) Set { + var set Set + for i := 0; i < n; i++ { + set.Add(f(i)) + } + return set +} + +// FromValues returns a Set of the values from v, which has type map[T]{{.Type}}. +// Returns the empty set if v does not have a type of this form. +func FromValues(v interface{}) Set { + if t := reflect.TypeOf(v); t == nil || t.Kind() != reflect.Map || t.Elem() != refType { + return nil + } + var set Set + m := reflect.ValueOf(v) + for _, key := range m.MapKeys() { + set.Add(m.MapIndex(key).Interface().({{.Type}})) + } + return set +} + +{{if .Transforms}} +// Map returns the Set that results from applying f to each element of s. +func (s Set) Map(f func({{.Type}}) {{.Type}}) Set { + var out Set + for k := range s { + out.Add(f(k)) + } + return out +} + +// Each applies f to each element of s. +func (s Set) Each(f func({{.Type}})) { + for k := range s { + f(k) + } +} + +// Select returns the subset of s for which f returns true. +func (s Set) Select(f func({{.Type}}) bool) Set { + var out Set + for k := range s { + if f(k) { + out.Add(k) + } + } + return out +} + +// Partition returns two disjoint sets, yes containing the subset of s for +// which f returns true and no containing the subset for which f returns false. +func (s Set) Partition(f func({{.Type}}) bool) (yes, no Set) { + for k := range s { + if f(k) { + yes.Add(k) + } else { + no.Add(k) + } + } + return +} + +// Choose returns an element of s for which f returns true, if one exists. The +// second result reports whether such an element was found. +// If f == nil, chooses an arbitrary element of s. The element chosen is not +// guaranteed to be the same across repeated calls. +func (s Set) Choose(f func({{.Type}}) bool) ({{.Type}}, bool) { + if f == nil { + for k := range s { + return k, true + } + } + for k := range s { + if f(k) { + return k, true + } + } + return {{.Zero}}, false +} + +// Pop removes and returns an element of s for which f returns true, if one +// exists (essentially Choose + Discard). The second result reports whether +// such an element was found. If f == nil, pops an arbitrary element of s. +func (s Set) Pop(f func({{.Type}}) bool) ({{.Type}}, bool) { + if v, ok := s.Choose(f); ok { + delete(s, v) + return v, true + } + return {{.Zero}}, false +} + +// Count returns the number of elements of s for which f returns true. +func (s Set) Count(f func({{.Type}}) bool) (n int) { + for k := range s { + if f(k) { + n++ + } + } + return +} +{{end}}{{/* transforms */}} diff --git a/makeset/core_test.go.in b/makeset/core_test.go.in new file mode 100644 index 0000000..a688f5a --- /dev/null +++ b/makeset/core_test.go.in @@ -0,0 +1,680 @@ +{{/* Generated unit tests from the config examples. */ -}} +package {{.Package}} + +import ( + "reflect" + "testing" + +{{range .TestImports}}{{printf "%q" .}} +{{end}} +) + +// testValues contains an ordered sequence of ten set keys used for testing. +// The order of the keys must reflect the expected order of key listings. +var testValues = [10]{{.Type}}{ +{{range .TestValues}} {{.}}, +{{end}} +} + +func testKeys(ixs ...int) (keys []{{.Type}}) { + for _, i := range ixs { + keys = append(keys, testValues[i]) + } + return +} + +func testSet(ixs ...int) Set { return New(testKeys(ixs...)...) } + +func keyPos(key {{.Type}}) int { + for i, v := range testValues { + if v == key { + return i + } + } + return -1 +} + +func TestEmptiness(t *testing.T) { + var s Set + if !s.Empty() { + t.Errorf("nil Set is not reported empty: %v", s) + } + + s = New() + if !s.Empty() { + t.Errorf("Empty Set is not reported empty: %v", s) + } + if s == nil { + t.Error("New() unexpectedly returned nil") + } + + if s := testSet(0); s.Empty() { + t.Errorf("Nonempty Set is reported empty: %v", s) + } +} + +func TestClone(t *testing.T) { + a := New(testValues[:]...) + b := testSet(1, 8, 5) + c := a.Clone() + c.Remove(b) + if c.Equals(a) { + t.Errorf("Unexpected equality: %v == %v", a, c) + } else { + t.Logf("%v.Clone().Remove(%v) == %v", a, b, c) + } + c.Update(b) + if !c.Equals(a) { + t.Errorf("Unexpected inequality: %v != %v", a, c) + } + + var s Set + if got := s.Clone(); got != nil { + t.Errorf("Clone of nil set: got %v, want nil", got) + } +} + +func TestUniqueness(t *testing.T) { + // Sets should not contain duplicates. Obviously this is impossible with + // the map implementation, but other representations are viable. + s := testSet(0, 5, 1, 2, 1, 3, 8, 4, 9, 4, 4, 6, 7, 2, 0, 0, 1, 4, 8, 4, 9) + if got, want := s.Len(), len(testValues); got != want { + t.Errorf("s.Len(): got %d, want %d [%v]", got, want, s) + } + + // Keys should come out sorted. + if got := s.Elements(); !reflect.DeepEqual(got, testValues[:]) { + t.Errorf("s.Elements():\n got %+v,\nwant %+v", got, testValues) + } +} + +func TestMembership(t *testing.T) { + s := testSet(0, 1, 2, 3, 4) + for i, v := range testValues { + if got, want := s.ContainsAny(v), i < 5; got != want { + t.Errorf("s.ContainsAny(%v): got %v, want %v", v, got, want) + } + } + +{{if .Transforms}} + // Test non-mutating selection. + if got, ok := s.Choose(func(s {{.Type}}) bool { + return s == testValues[0] + }); !ok { + t.Error("Choose(0): missing element") + } else { + t.Logf("Found %v for element 0", got) + } + if got, ok := s.Choose(func({{.Type}}) bool { return false }); ok { + t.Errorf(`Choose(impossible): got %v, want {{.Zero}}`, got) + } + if got, ok := New().Choose(nil); ok { + t.Errorf(`Choose(nil): got %v, want {{.Zero}}`, got) + } + + // Test mutating selection. + if got, ok := s.Pop(func(s {{.Type}}) bool { + return s == testValues[1] + }); !ok { + t.Error("Pop(1): missing element") + } else { + t.Logf("Found %v for element 1", got) + } + // A popped item is removed from the set. + if len(s) != 4 { + t.Errorf("Length after pop: got %d, want %d", len(s), 4) + } + // Pop of a nonexistent key returns not-found. + if got, ok := s.Pop(func({{.Type}}) bool { return false }); ok { + t.Errorf(`Pop(impossible): got %v, want {{.Zero}}`, got) + } + // Pop from an empty set returns not-found. + if got, ok := New().Pop(nil); ok { + t.Errorf(`Pop(nil) on empty: got %v, want {{.Zero}}`, got) + }{{end}} +} + +func TestContainsAny(t *testing.T) { + set := New(testValues[2:]...) + tests := []struct { + keys []{{.Type}} + want bool + }{ + {nil, false}, + {[]{{.Type}}{}, false}, + {testKeys(0), false}, + {testKeys(1), false}, + {testKeys(0, 1), false}, + {testKeys(7), true}, + {testKeys(8, 3, 4, 9), true}, + {testKeys(0, 7, 1, 0), true}, + } + t.Logf("Test set: %v", set) + for _, test := range tests { + got := set.ContainsAny(test.keys...) + if got != test.want { + t.Errorf("ContainsAny(%+v): got %v, want %v", test.keys, got, test.want) + } + } +} + +func TestContainsAll(t *testing.T) { + //set := New("a", "e", "i", "y") + set := New(testValues[2:]...) + tests := []struct { + keys []{{.Type}} + want bool + }{ + {nil, true}, + {[]{{.Type}}{}, true}, + {testKeys(2, 4, 6), true}, + {testKeys(1, 3, 5, 7), false}, + {testKeys(0), false}, + {testKeys(5, 5, 5), true}, + } + t.Logf("Test set: %v", set) + for _, test := range tests { + got := set.Contains(test.keys...) + if got != test.want { + t.Errorf("Contains(%+v): got %v, want %v", test.keys, got, test.want) + } + } +} + +func TestIsSubset(t *testing.T) { + var empty Set + key := testSet(0, 2, 6, 7, 9) + for _, test := range [][]{{.Type}}{ + {}, testKeys(2, 6), testKeys(0, 7, 9), + } { + probe := New(test...) + if !probe.IsSubset(key) { + t.Errorf("IsSubset %+v ⊂ %+v is false", probe, key) + } + if !empty.IsSubset(probe) { // ø is a subset of everything, including itself. + t.Errorf("IsSubset ø ⊂ %+v is false", probe) + } + } +} + +func TestNotSubset(t *testing.T) { + tests := []struct { + probe, key Set + }{ + {testSet(0), New()}, + {testSet(0), testSet(1)}, + {testSet(0, 1), testSet(1)}, + {testSet(0, 2, 1), testSet(0, 2, 3)}, + } + for _, test := range tests { + if test.probe.IsSubset(test.key) { + t.Errorf("IsSubset %+v ⊂ %+v is true", test.probe, test.key) + } + } +} + +func TestEquality(t *testing.T) { + nat := New(testValues[:]...) + odd := testSet(1, 3, 4, 5, 8) + tests := []struct { + left, right Set + eq bool + }{ + {nil, nil, true}, + {nat, nat, true}, // Equality with the same value + {testSet(0), testSet(0), true}, // Equality with Different values + {testSet(0), nil, false}, + {nat, odd, false}, + {nil, testSet(0), false}, + {testSet(0), testSet(1), false}, + + // Various set operations... + {nat.Intersect(odd), odd, true}, + {odd, nat.Intersect(odd), true}, + {odd.Intersect(nat), odd, true}, + {odd, odd.Intersect(nat), true}, + {nat.Intersect(nat), nat, true}, + {nat, nat.Intersect(nat), true}, + {nat.Union(odd), nat, true}, + {nat, nat.Union(odd), true}, + {odd.Diff(nat), odd, false}, + {odd, odd.Diff(nat), false}, + {odd.Diff(nat), nil, true}, + {nil, odd.Diff(nat), true}, + + {testSet(0, 1, 2).Diff(testSet(2, 5, 6)), testSet(1).Union(testSet(0)), true}, + } + for _, test := range tests { + if got := test.left.Equals(test.right); got != test.eq { + t.Errorf("%v.Equals(%v): got %v, want %v", test.left, test.right, got, test.eq) + } + } +} + +func TestUnion(t *testing.T) { + vkeys := testKeys(0, 4) + vowels := testSet(4, 0) + consonants := testSet(1, 2, 3, 5, 6, 7, 8, 9) + + if got := vowels.Union(nil).Elements(); !reflect.DeepEqual(got, vkeys) { + t.Errorf("Vowels ∪ ø: got %+v, want %+v", got, vkeys) + } + if got := New().Union(vowels).Elements(); !reflect.DeepEqual(got, vkeys) { + t.Errorf("ø ∪ Vowels: got %+v, want %+v", got, vkeys) + } + + if got, want := vowels.Union(consonants).Elements(), testValues[:]; !reflect.DeepEqual(got, want) { + t.Errorf("Vowels ∪ Consonants: got %+v, want %+v", got, want) + } +} + +func TestIntersect(t *testing.T) { + empty := New() + nat := New(testValues[:]...) + odd := testSet(1, 3, 5, 7, 9) + prime := testSet(2, 3, 5, 7) + + tests := []struct { + left, right Set + want []{{.Type}} + }{ + {empty, empty, nil}, + {empty, nat, nil}, + {nat, empty, nil}, + {nat, nat, testValues[:]}, + {nat, odd, testKeys(1, 3, 5, 7, 9)}, + {odd, nat, testKeys(1, 3, 5, 7, 9)}, + {odd, prime, testKeys(3, 5, 7)}, + {prime, nat, testKeys(2, 3, 5, 7)}, + } + for _, test := range tests { + got := test.left.Intersect(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v ∩ %v: got %+v, want %+v", test.left, test.right, got, test.want) + } else if want, ok := len(test.want) != 0, test.left.Intersects(test.right); ok != want { + t.Errorf("%+v.Intersects(%+v): got %v, want %v", test.left, test.right, ok, want) + } + } +} + +func TestDiff(t *testing.T) { + empty := New() + nat := New(testValues[:]...) + odd := testSet(1, 3, 5, 7, 9) + prime := testSet(2, 3, 5, 7) + + tests := []struct { + left, right Set + want []{{.Type}} + }{ + {empty, empty, nil}, + {empty, nat, nil}, + {nat, empty, testValues[:]}, + {nat, nat, nil}, + {nat, odd, testKeys(0, 2, 4, 6, 8)}, + {odd, nat, nil}, + {odd, prime, testKeys(1, 9)}, + {prime, nat, nil}, + } + for _, test := range tests { + got := test.left.Diff(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v \\ %v: got %+q, want %+q", test.left, test.right, got, test.want) + } + } +} + +func TestSymDiff(t *testing.T) { + a := testSet(0, 1, 2, 3, 4) + b := testSet(0, 4, 5, 6, 7) + c := testSet(3, 4, 8, 9) + empty := New() + + tests := []struct { + left, right Set + want []{{.Type}} + }{ + {empty, empty, nil}, + {empty, a, a.Elements()}, + {b, empty, b.Elements()}, + {a, a, nil}, + {a, b, testKeys(1, 2, 3, 5, 6, 7)}, + {b, a, testKeys(1, 2, 3, 5, 6, 7)}, + {a, c, testKeys(0, 1, 2, 8, 9)}, + {c, a, testKeys(0, 1, 2, 8, 9)}, + {c, b, testKeys(0, 3, 5, 6, 7, 8, 9)}, + } + for _, test := range tests { + got := test.left.SymDiff(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v ∆ %v: got %+v, want %+v", test.left, test.right, got, test.want) + } + } +} + +func TestUpdate(t *testing.T) { + tests := []struct { + before, update Set + want []{{.Type}} + changed bool + }{ + {nil, nil, nil, false}, + {nil, testSet(0), testKeys(0), true}, + {testSet(1), nil, testKeys(1), false}, + {testSet(2, 3), testSet(4, 4, 3), testKeys(2, 3, 4), true}, + } + for _, test := range tests { + ok := test.before.Update(test.update) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Update %v: got %+v, want %+q", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Update %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestAdd(t *testing.T) { + tests := []struct { + before Set + update, want []{{.Type}} + changed bool + }{ + {nil, nil, nil, false}, + {nil, testKeys(0), testKeys(0), true}, + {testSet(1), nil, testKeys(1), false}, + {testSet(0, 1), testKeys(2, 2, 1), testKeys(0, 1, 2), true}, + } + for _, test := range tests { + ok := test.before.Add(test.update...) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Add %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Add %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestRemove(t *testing.T) { + tests := []struct { + before, update Set + want []{{.Type}} + changed bool + }{ + {nil, nil, nil, false}, + {nil, testSet(0), nil, false}, + {testSet(5), nil, testKeys(5), false}, + {testSet(3, 9), testSet(5, 1, 9), testKeys(3), true}, + {testSet(0, 1, 2), testSet(4, 6), testKeys(0, 1, 2), false}, + } + for _, test := range tests { + ok := test.before.Remove(test.update) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Remove %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Remove %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestDiscard(t *testing.T) { + tests := []struct { + before Set + update, want []{{.Type}} + changed bool + }{ + {nil, nil, nil, false}, + {nil, testKeys(0), nil, false}, + {testSet(1), nil, testKeys(1), false}, + {testSet(0, 1), testKeys(2, 2, 1), testKeys(0), true}, + {testSet(0, 1, 2), testKeys(3, 4), testKeys(0, 1, 2), false}, + } + for _, test := range tests { + ok := test.before.Discard(test.update...) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Discard %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Discard %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +{{if .Transforms}} +func TestMap(t *testing.T) { + in := New(testValues[:]...) + got := make([]{{.Type}}, len(testValues)) + out := in.Map(func(s {{.Type}}) {{.Type}} { + if p := keyPos(s); p < 0 { + t.Errorf("Unknown input key %v", s) + } else { + got[p] = s + } + return s + }) + if !reflect.DeepEqual(got, testValues[:]) { + t.Errorf("Incomplete mapping:\n got %+v\nwant %+v", got, testValues) + } + if !out.Equals(in) { + t.Errorf("Incorrect mapping:\n got %v\nwant %v", out, in) + } +} + +func TestEach(t *testing.T) { + in := New(testValues[:]...) + saw := make(map[{{.Type}}]int) + in.Each(func(name {{.Type}}) { + saw[name]++ + }) + for want := range in { + if saw[want] != 1 { + t.Errorf("Saw [%v] %d times, wanted 1", want, saw[want]) + } + } + for got, n := range saw { + if _, ok := in[got]; !ok { + t.Errorf("Saw [%v] %d times, wanted 0", got, n) + } + } +} + +func TestSelection(t *testing.T) { + in := New(testValues[:]...) + want := testSet(0, 2, 4, 6, 8) + if got := in.Select(func(s {{.Type}}) bool { + pos := keyPos(s) + return pos >= 0 && pos%2 == 0 + }); !got.Equals(want) { + t.Errorf("%v.Select(evens): got %v, want %v", in, got, want) + } + if got := New().Select(func({{.Type}}) bool { return true }); !got.Empty() { + t.Errorf("%v.Select(true): got %v, want empty", New(), got) + } + if got := in.Select(func({{.Type}}) bool { return false }); !got.Empty() { + t.Errorf("%v.Select(false): got %v, want empty", in, got) + } +} + +func TestPartition(t *testing.T) { + in := New(testValues[:]...) + tests := []struct { + in, left, right Set + f func({{.Type}}) bool + desc string + }{ + {testSet(0, 1), testSet(0, 1), nil, + func({{.Type}}) bool { return true }, + "all true", + }, + {testSet(0, 1), nil, testSet(0, 1), + func({{.Type}}) bool { return false }, + "all false", + }, + {in, + testSet(0, 1, 2, 3, 4), + testSet(5, 6, 7, 8, 9), + func(s {{.Type}}) bool { return keyPos(s) < 5 }, + "pos(s) < 5", + }, + {in, + testSet(1, 3, 5, 7, 9), // odd + testSet(0, 2, 4, 6, 8), // even + func(s {{.Type}}) bool { return keyPos(s)%2 == 1 }, + "odd/even", + }, + } + for _, test := range tests { + gotLeft, gotRight := test.in.Partition(test.f) + if !gotLeft.Equals(test.left) { + t.Errorf("Partition %s left: got %v, want %v", test.desc, gotLeft, test.left) + } + if !gotRight.Equals(test.right) { + t.Errorf("Partition %s right: got %v, want %v", test.desc, gotRight, test.right) + } + t.Logf("Partition %v %s\n\t left: %v\n\tright: %v", test.in, test.desc, gotLeft, gotRight) + } +} +{{end}} + +func TestIndex(t *testing.T) { + tests := []struct { + needle {{.Type}} + keys []{{.Type}} + want int + }{ + {testValues[0], nil, -1}, + {testValues[1], []{{.Type}}{}, -1}, + {testValues[2], testKeys(0, 1), -1}, + {testValues[0], testKeys(0, 1), 0}, + {testValues[1], testKeys(0, 1), 1}, + {testValues[2], testKeys(0, 2, 1, 2), 1}, + {testValues[9], testKeys(0, 2, 1, 9, 6), 3}, + {testValues[4], testKeys(0, 2, 4, 9, 4), 2}, + } + for _, test := range tests { + got := Index(test.needle, test.keys) + if got != test.want { + t.Errorf("Index(%+v, %+v): got %d, want %d", test.needle, test.keys, got, test.want) + } + } +} + +type keyer []{{.Type}} + +func (k keyer) Keys() []{{.Type}} { + p := make([]{{.Type}}, len(k)) + copy(p, k) + return p +} + +type uniq int + +func TestFromValues(t *testing.T) { + tests := []struct { + input interface{} + want []{{.Type}} + }{ + {nil, nil}, + {map[float64]{{.Type}}{}, nil}, + {map[int]{{.Type}}{1: testValues[1], 2: testValues[2], 3: testValues[2]}, testKeys(1, 2)}, + {map[string]{{.Type}}{"foo": testValues[4], "baz": testValues[4]}, testKeys(4)}, + {map[int]uniq{1: uniq(2), 3: uniq(4), 5: uniq(6)}, nil}, + {map[*int]{{.Type}}{nil: testValues[0]}, testKeys(0)}, + } + for _, test := range tests { + got := FromValues(test.input) + want := New(test.want...) + if !got.Equals(want) { + t.Errorf("MapValues %v: got %v, want %v", test.input, got, want) + } + } +} + +func TestFromKeys(t *testing.T) { + tests := []struct { + input interface{} + want Set + }{ + {3.5, nil}, // unkeyable type + {map[uniq]uniq{1: 1}, nil}, // unkeyable type + {nil, nil}, // empty + {[]string{}, nil}, // empty + {map[{{.Type}}]float64{}, nil}, // empty + {testValues[0], testSet(0)}, + {testKeys(0, 1, 0, 0), testSet(0, 1)}, + {map[{{.Type}}]int{testValues[0]: 1, testValues[1]: 2}, testSet(0, 1)}, + {keyer(testValues[:3]), testSet(0, 1, 2)}, + {testSet(4, 7, 8), testSet(4, 7, 8)}, + {map[{{.Type}}]struct{}{testValues[2]: {}, testValues[7]: {}}, testSet(2, 7)}, + } + for _, test := range tests { + got := FromKeys(test.input) + if !got.Equals(test.want) { + t.Errorf("FromKeys %v: got %v, want %v", test.input, got, test.want) + } + } +} + +func TestContainsFunc(t *testing.T) { + tests := []struct { + input interface{} + needle {{.Type}} + want bool + }{ + {[]{{.Type}}(nil), testValues[0], false}, + {[]{{.Type}}{}, testValues[0], false}, + {testKeys(0), testValues[0], true}, + {testKeys(1), testValues[0], false}, + {testKeys(0, 1, 9, 2), testValues[0], true}, + + {map[{{.Type}}]int(nil), testValues[2], false}, + {map[{{.Type}}]int{}, testValues[2], false}, + {map[{{.Type}}]int{testValues[2]: 1}, testValues[2], true}, + {map[{{.Type}}]int{testValues[3]: 3}, testValues[2], false}, + {map[{{.Type}}]float32{testValues[2]: 1, testValues[4]: 2}, testValues[2], true}, + {map[{{.Type}}]float32{testValues[5]: 0, testValues[6]: 1, testValues[7]: 2, testValues[8]: 3}, testValues[2], false}, + + {Set(nil), testValues[3], false}, + {New(), testValues[3], false}, + {New(testValues[3]), testValues[3], true}, + {New(testValues[5]), testValues[3], false}, + {testSet(0, 1), testValues[3], false}, + {testSet(0, 3, 1), testValues[3], true}, + + {keyer(nil), testValues[9], false}, + {keyer{}, testValues[9], false}, + {keyer{testValues[9]}, testValues[9], true}, + {keyer{testValues[0]}, testValues[9], false}, + {keyer(testKeys(0, 6, 9)), testValues[9], true}, + {keyer(testKeys(0, 6, 7)), testValues[9], false}, + } + for _, test := range tests { + got := Contains(test.input, test.needle) + if got != test.want { + t.Errorf("Contains(%+v, %v): got %v, want %v", test.input, test.needle, got, test.want) + } + } +} + +func TestFromIndexed(t *testing.T) { + tests := []struct { + input []int + want Set + }{ + {nil, nil}, + {[]int{}, nil}, + {[]int{0}, testSet(0)}, + {[]int{1, 8, 2, 9}, testSet(1, 2, 8, 9)}, + {[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, New(testValues[:]...)}, + } + for _, test := range tests { + got := FromIndexed(len(test.input), func(i int) {{.Type}} { + return testValues[test.input[i]] + }) + if !got.Equals(test.want) { + t.Errorf("FromIndexed(%d, <...>): got %v, want %v", len(test.input), got, test.want) + } + } +} diff --git a/makeset/intset.toml b/makeset/intset.toml new file mode 100644 index 0000000..e5ff5c4 --- /dev/null +++ b/makeset/intset.toml @@ -0,0 +1,8 @@ +desc = "A set of int values." +type = "int" +package = "intset" +zero = "0" +toString = 'return strconv.Itoa(x)' +imports = ["strconv"] +transforms = true +testValues = [0, 1, 2, 3, 5, 7, 11, 13, 17, 19] diff --git a/makeset/makeset.go b/makeset/makeset.go new file mode 100644 index 0000000..a502ad2 --- /dev/null +++ b/makeset/makeset.go @@ -0,0 +1,182 @@ +// Program makeset generates source code for a set package. The type of the +// elements of the set is determined by a TOML configuration stored in a file +// named by the -config flag. +// +// Usage: +// go run makeset.go -output $DIR -config config.toml +// +package main + +//go:generate go run github.com/creachadair/staticfile/compiledata -out static.go *.go.in + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" + "path/filepath" + "sort" + "text/template" + + "github.com/BurntSushi/toml" + "github.com/creachadair/staticfile" +) + +// A Config describes the nature of the set to be constructed. +type Config struct { + // A human-readable description of the set this config defines. + // This is ignored by the code generator, but may serve as documentation. + Desc string + + // The name of the resulting set package, e.g., "intset" (required). + Package string + + // The name of the type contained in the set, e.g., "int" (required). + Type string + + // The spelling of the zero value for the set type, e.g., "0" (required). + Zero string + + // If set, a type definition is added to the package mapping Type to this + // structure, e.g., "struct { ... }". You may prefix Decl with "=" to + // generate a type alias (this requires Go ≥ 1.9). + Decl string + + // If set, the body of a function with signature func(x, y Type) bool + // reporting whether x is less than y. + // + // For example: + // if x[0] == y[0] { + // return x[1] < y[1] + // } + // return x[0] < y[0] + Less string + + // If set, the body of a function with signature func(x Type) string that + // converts x to a human-readable string. + // + // For example: + // return strconv.Itoa(x) + ToString string + + // If set, additional packages to import in the generated code. + Imports []string + + // If set, additional packages to import in the test. + TestImports []string + + // If true, include transformations, e.g., Map, Partition, Each. + Transforms bool + + // A list of exactly ten ordered test values used for the construction of + // unit tests. If omitted, unit tests are not generated. + TestValues []interface{} `json:"testValues,omitempty"` +} + +func (c *Config) validate() error { + if c.Package == "" { + return errors.New("invalid: missing package name") + } else if c.Type == "" { + return errors.New("invalid: missing type name") + } else if c.Zero == "" { + return errors.New("invalid: missing zero value") + } + return nil +} + +var ( + configPath = flag.String("config", "", "Path of configuration file (required)") + outDir = flag.String("output", "", "Output directory path (required)") + + baseImports = []string{"reflect", "sort", "strings"} +) + +func main() { + flag.Parse() + switch { + case *outDir == "": + log.Fatal("You must specify a non-empty -output directory") + case *configPath == "": + log.Fatal("You must specify a non-empty -config path") + } + conf, err := readConfig(*configPath) + if err != nil { + log.Fatalf("Error loading configuration: %v", err) + } + if len(conf.TestValues) > 0 && len(conf.TestValues) != 10 { + log.Fatalf("Wrong number of test values (%d); exactly 10 are required", len(conf.TestValues)) + } + if err := os.MkdirAll(*outDir, 0755); err != nil { + log.Fatalf("Unable to create output directory: %v", err) + } + + mainT, err := template.New("main").Parse(string(staticfile.MustReadFile("core.go.in"))) + if err != nil { + log.Fatalf("Invalid main source template: %v", err) + } + testT, err := template.New("test").Parse(string(staticfile.MustReadFile("core_test.go.in"))) + if err != nil { + log.Fatalf("Invalid test source template: %v", err) + } + + mainPath := filepath.Join(*outDir, conf.Package+".go") + if err := generate(mainT, conf, mainPath); err != nil { + log.Fatal(err) + } + if len(conf.TestValues) != 0 { + testPath := filepath.Join(*outDir, conf.Package+"_test.go") + if err := generate(testT, conf, testPath); err != nil { + log.Fatal(err) + } + } +} + +// readConfig loads a configuration from the specified path and reports whether +// it is valid. +func readConfig(path string) (*Config, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + var c Config + if err := toml.Unmarshal(data, &c); err != nil { + return nil, err + } + + // Deduplicate the import list, including all those specified by the + // configuration as well as those needed by the static code. + imps := make(map[string]bool) + for _, pkg := range baseImports { + imps[pkg] = true + } + for _, pkg := range c.Imports { + imps[pkg] = true + } + if c.ToString == "" { + imps["fmt"] = true // for fmt.Sprint + } + c.Imports = make([]string, 0, len(imps)) + for pkg := range imps { + c.Imports = append(c.Imports, pkg) + } + sort.Strings(c.Imports) + return &c, c.validate() +} + +// generate renders source text from t using the values in c, formats the +// output as Go source, and writes the result to path. +func generate(t *template.Template, c *Config, path string) error { + var buf bytes.Buffer + if err := t.Execute(&buf, c); err != nil { + return fmt.Errorf("generating source for %q: %v", path, err) + } + src, err := format.Source(buf.Bytes()) + if err != nil { + return fmt.Errorf("formatting source for %q: %v", path, err) + } + return ioutil.WriteFile(path, src, 0644) +} diff --git a/makeset/nodeset.toml b/makeset/nodeset.toml new file mode 100644 index 0000000..fd0f733 --- /dev/null +++ b/makeset/nodeset.toml @@ -0,0 +1,28 @@ +desc = "A set of Go AST nodes from the go/ast package." +package = "nodeset" +type = "ast.Node" +zero = "nil" + +less = """ +if x.Pos() == y.Pos() { + return x.End() > y.End() +} +return x.Pos() < y.Pos() +""" + +imports = ["go/ast"] +testImports = ["go/ast"] +transforms = true + +testValues = [ + '&ast.Ident{Name: "amy", NamePos: 1}', + '&ast.Ident{Name: "basil", NamePos: 3}', + '&ast.Ident{Name: "clara", NamePos: 5}', + '&ast.Ident{Name: "desmond", NamePos: 9}', + '&ast.Ident{Name: "ernest", NamePos: 10}', + '&ast.Ident{Name: "fanny", NamePos: 12}', + '&ast.Ident{Name: "george", NamePos: 14}', + '&ast.Ident{Name: "hector", NamePos: 17}', + '&ast.Ident{Name: "ida", NamePos: 19}', + '&ast.Ident{Name: "james", NamePos: 25}', +] diff --git a/makeset/static.go b/makeset/static.go new file mode 100644 index 0000000..390737a --- /dev/null +++ b/makeset/static.go @@ -0,0 +1,200 @@ +package main + +// This file was generated by compiledata -out static.go *.go.in +// DO NOT EDIT + +import "github.com/creachadair/staticfile" + +func init() { + staticfile.Register("core.go.in", _fileData1) + staticfile.Register("core_test.go.in", _fileData2) +} + +const ( + // 11316 bytes generated from core.go.in + _fileData1 = "" + + "x\xda\xb4Z\xcdr\xdcF\x92>\xb3\x9f\"\xc5\x88\xf5\x00\x12\x04rx\xb4LGhm\xed\x86\xd6\xf6\x8cb(\xfb\xb0\x1c\xc5F5\x90`\x97\x09" + + "T\xc1\xa8BS=P_7f\xf6)\xf6\xb0\x97\xdd'\xf0\xddo\xa2'\xd9\xc8\xac*\xfc5\x9a\"\xed\x99\x83\xc4\xeeFUV\xe6\x97_feV\xa1\xeb\xce" + + "\x9e\xc2\xdb\x0dB%\xa4\x02\xa3\xdb&C(t\x03v\x83P\x8b\xecV\xdc`\x02Ree\x9bKu\x03\xb9\xce \xd3U\x85\xca\x9a\x14\x9e\x9e\xc1" + + "\xf3\xfd~uv\x06o\xdcP\xe8\xba\xd4\x7f\xdc\xefAVu\x89<\x14\x04\x94\xf2fc\xef\x90\xfe\x87\xa8\x90JZ\x8c\xc1\xa0\x05]\xd0\xac" + + "\xb7\xbb\x9a\xa6lE\xd9\xa2!\x89ka0\x07\xad\xe0_\xf5\xef\x0c\xac[Y\xda\xe7RA%\xea\x14\xe0%\\\xa1\x85\xba\xd1[\x99\xa3\x01" + + "\xa3+\x84L\xab-*\x89*C\xa8\xd0ntn\xc8\x12\x92E\x1ak\xe5V\xab\xb1\x11Vje\xd2\xd5\xd9\x19=|\x09J\x96,O\x1ahP\xe4;\x06\xa05" + + "\x08\xc2\x80P\x80Umw49\x05\x86j-\x8c\xccXXX&\xfaZ\x16EB\xc2^+\x8b\x8d\xc1\xcc&\xf0\xbd\x92Z%\xf0\xda\\\xb5k\x836\x81\xef" + + "D\x9d\xc0W\x1b\xad\x0d&\xf0F4V\x92\x1e1\xe4\x1a\x94\xb6P\xb5VX$\xe0%+-\x9a\x9b\xd6\xe3L\xcb6\x08\x82\xfe\x95F\xbb\xa1\xe4" + + "\x8e\xc1\x1a\x88^\xe6y\x02_K\x93\x89&O\xe0\x8d\xae\x13\xf8\x13Vz\x8b\xac\xd8\xf7u.\x08q\xbb\x11\x16*\x9d\xcbb\xe7\x96\x82" + + "\x063\x94[l@\xaa\xe7u)2\x1c\x80!P2\xa1\xdc\xa2k\x04\xdb\x88-\x99\x97\x83P\xb9\x93\"1\x87\xd6\x90.\xc4\x18\xa5\x9bJ\x94\xe4" + + "\xa4\x19\xd0\xf0\xcfHc\x04=J@\x04\xb8\xc9n#\x0aG\xb9L\xab\xacm\x1aT\x16D\x96\xa11\xb0\xdeA\xd5\x96V\xd6%\xc2\x8dntk\xa5r" + + "\xechUI\x03DY\xf2\xba\x07S\xd10Z\xe4N\x93\xae\xea\x05v\xaeV\xb2\xaauc!Zu]#\xd4\x0dB\xfa\x9a\x7f1\xfb}\xd7\xd5\x8dT\xb6\x80" + + "\xd3\x7f\xfa\xe9\x14\xd2\xfd~\xd5u\xa8\xf2\xfd~\x15\xafV]'\x0bH\xbf\xc6\xact\xdc\x1f\xd8+\x0dkcw5\x12\xad\xe93\x86\x08\xf0" + + "\xdf\x89F+\x1e0L\xeb:/-,\xe2\x97\xf8\x16\x8dqKHC\x9f\xa1A\xd6\x0f\xee6h7\xd8\xc0{Z\x91\x81\xb0\x1b\xa1`\x07\x14\xc3V\xa8" + + "\\49\xe8&\xc7&]\x15\xad\xca\xfc\xfc\xe8}\x02\xbba\xdd\x18\xd6Z\x97\xd0\xadN\xba.\xac5W\xe1\xad\xbe\xb2\x8dT7\xfb=\xcb\xb1" + + "\xfek\xf4~,\xc6\xf0o\xd0\xad\x00\x80\x7f\xef'\x8d\xe4\xf5\x84j\xb0n\xd0\xf8\xacp$\x01p\x88\x8f\xc2R\xd0\xef2'!\xfdt&\x17" + + "M\x9e\x84\xa8\xc3\x96\xa6U\xa2\xbe\xee\xe5\xbe3\xb6i3\xdb9E\xd6\xbbW\xce-`\x84\x95\xa6\x90\x9cC\x1a\x9br\xf8\x16\"C\xb0\xda" + + "\x01\xe8\x15b\xf7M\xbc\xe6W\x1aD]\xbf\xeb\x9f1\xe8\xd1\xe8a\x0c\xdf\xa2\x8ab\x90\xcaB\x07\x0d\xda\xb6QP\xa2\x8a0\x067\x1a" + + "\xa6\xc3\xaf\xeeD\x1d\xc9\x04~\xa4)1t\x80\xd7\xf2]\x02x\xfd\xe3;\xb8\xe4?\x09\xff\xb4<\x9b\x9d=\xcc\x1e\xdc<\xb0\xca\xeb" + + "\xe0\x891H\x8f\xbb\x0eK\x83\xfd\x00^\xe4\x0b~\x14\\\xe90t.\x1e\xe7x\xa2wQ\xd9\xd4=\xe1\x84\xe2\xd1L\x01^\x93\xe3U\x8e\x8d" + + "\x013\xa1\xa9AK\xe2\x94v\x0eM\x00\xd3\x9b4\x81_~\xe6\x9c0\xf6m\x02\x9dH`\x9d@\xb6w\xcf@i\xe5\x9ej\x85\x9e\xe9\x91!\xe7\xc7" + + "^\xbdhD\xce\x13Y\x80I_\xd1\xf8(\xa6\xef'\xde\xc4\xd3_~>]\x9d\xecW'XZ\x03\x9f_B%n1\xba~\xe7f&\xec'\x13\xc7\xab\x13ZT&\x80" + + "\xa5\xa5Q.g\x98\xd4\xa3n\xbcP\x12B\x98]\xc2<\x82B\xf0\x04\x84\x19+\xce3\x1e\xd9\x08K\x1b\xb3&A\xb3\xee\x14\x9ey\x0bL\xfa" + + "oZ*\x1ab\x128M\xe04\x86gp\xba?\xf5\xee\xf8\x03\xdey^Q\xb4(\xbc\xe3\xd0\xca\xb4\xb2B*\x02\x00\xdf\x8b\xcc\x96;\x97\x84j\xcc" + + "\\\xea\x0e\xe9)\x05 )\x7f\x1a$h\xf5\x9c\x02\xd0\xe1\xcbaX\x80\xd2C>\xa3\xf4\xda\xcb\xf1\xe0\xff\x01\xefXAH\xd3t\x94\x1fh" + + "v\xb7:!\x85\x02\xbaW\xe4N\x0e\x80\xd2\xf6\xd8\xfe\xc7\x0c[\x16E\x98\x1a\xb4\xd7XZ\x025\xc4q\xb7\x1f\x03E$\xeaq\xb8\x92\x7f" + + "\xc1\x19\x16=\x87\xa0n\xf0\xb9\x91\x7f\xc1\x9c\"|\xa3\xcb\x1c\x84\x85\x12\x85\xb1\xa0\x064H\xd2\xdb\x8d4\x94{\xf0\xa7VnE" + + "I!n\xf5H{\x15\xf3.x'\xcb\x12j\xa1d\xc6\x08\xc1\x17p>\xa0A\xaaD\xcaE!\xa3\x10b\x7f\"\xc6i\xfe-\xaa^k\xdeK\xdbj\x8d\x0d%\x9e" + + "\x1es\x8a\x9b\x19\xcf\x8f\xe4\x15\x13\xa4\x06v\x0e\x80(\x97\xd80\x07S\xca\xecp\xa3ZXdD\xf1Q\x96#\xd7\x84\x901\xe9\xf7\xca" + + "\x8b\x8d\xe2\xd5\x09'\xd3+\xdd\xd8\xa8\xcfJ\xbd\xa7Cb)\xad\xf1>\xeb\xa7\x8e\x95l\xd5\xa3\xd5\x1c\xa90\xd7S\x16\x01\x96\xcb" + + "K8\x1fG\xbf\x92\xe5R\xf0\xf7\xb3\x138\x9f\xa5\x80i\xfc\xf7A\x0f\x97 \xea\x1aU\xeect\x1e\xcc#\x83\xbf*\xb5\x9aS\x94\xe8\x91" + + "Kc\xa5\xca,\x14\x8d\xae\xc0$\xe3\xf8\xe5\xb8\x15\x15\x8eH:1\x9eeF}\xb0mE\x03\x19}Y\x9dd\xa9+\xfe\"3\xa0\x9f\x05M\xdc\x02" + + "\xe6\xa5\xda\x1dT\x17&,o(\xbf\x82n\xa0\xd2M\xef\x88\x1b\xb9\xc5Y\xc4\xbc\xb6\xb3x\xa1z\x1d\x85S_\xd3\x08\x00\x93\xf6%\xb2" + + "\x89\xc6\xf5X\x1a\x92G\x9a\xa6q\xcc\xbbtk!\xd7\xe8\xaa\xc4L+\x17\xfa\xc4\x0d\xdeX*\xcc%\x15\xcc\xbc\xebO\xa1\x18\x8cZJGa" + + ";\xf4)\xe7\x16w\x0b)G\x16\xf4L\xdf2\xb9\xafoq\xf7\xee\x05}\xa3G\x01C\xdb\xb4\xb8:9\x99$\xa2B\x94\x06g\xd8\xde\x07l$\xca2" + + "\xfe-x\x8eQKC\xafA\x9e\xfe{\x00\xf8\x00\xf4\x96\x13\xf6\x14=J\xdd/\xe0\xc9\x0c>\x07\xd5\x0c?\xc6\xd4\xc1\x17\x8cY\x80\x8f" + + "+B\xe3\x9e\xea\x02\xccE\x02\x06>\xfe\xd7\x7f\x82\xb9\x98Y2 r\xe1~\x08\xea\x1f+\x06\x9cS\xf7@[4\x0cY\xe3K\xf7\xe1b2\xd6[\xb0" + + "w`\xdc\xce\x93\xc2\x04\x84\x8b\xeb\xdb_\x81\xc1\xab\x9fZQ\x9ae\x04\x90\x9e\xd1\x96D\xe6o\xc4\xf6`\x93\xbf'Y8\xb93Pf\xfb\xc7" + + "\xe5eo\xf3g\x9fQ\xdc\x0eP\xf6\x9b\x0bo\xab\xcb\xda\xd1\xa3\xf9\xaa\x1e\xeec\xcb\x9d{\xb1C\x868\x90M\x86\xc9\xf0\x98\xea\x7f" + + "\x03\x1f\xff\xfa\xbf`.\\7\xa9\x9e\xf3\xba\x09\xdcI\xbb\xd1-W\x96\xf8\xbe.e&\x09\x96>\x06BB\x1d\x8b\x9a3gHSs\xeeP\x15\xca" + + "NM\xc0\\\xf4{\xcb:\x86/\xf8\x83p$\xe1Q\x97T\xaf\x0a\xe0`\xa6~\x18Ao\xbd\x19\xa6\x12eIpQ\x8e>\xe4\x908\xe0\xd0\x9a)\xf4\xc8" + + "$\xc4\xa7\x10\x83\xe1\xae\xb4hU\x80\xee\xff\x0eC\x86g\xf46\xfb\xed\xe4X\xb8\x90\xfdC\xb0\x98\x8b\xc51\xac\xdb\xac\xfa\x8b" + + "\x8f\x04\x0d\x95z\xb7\x0b\x85\xde|\xf0\xc5\xbd\xa3\x0f\xca\xc2\xde\x9bs(\x16\xc9t\x8c\x0a\xf7\xa2\xf2\xe1\xc3\x11\xfbC\x85" + + "\xf1@\x04\x16\xd2F\xf0\xf9\xa2\xb9\xde\xf7!W\x91z\xc7j\x9c\x03X\xbe\x96E1G\xc4p!R\x14\xd8\xf0Q\x9a\x81?\x1f\"B\xf3~\x1d\x18" + + "\xe67B\xf1\xe4\x1f\x86\xc5\xd5\xaeZ\x84cWUh\x1b\x99MA\xf9\xf8W\xb7\xdb|r\x9b&\xcc\\\x9c\xc5\xf0g\xf7\x85(\x16\xcf\xfbU\xb7" + + "\xfa\x1c\xd4\xa0e\x1a\xa22N\xdd\xb0tL\xca8\x0e\xd1\xce\x85\x1e\x88<7\x07\xa7O\xe6\x82\xb4yj\xfas\xbe\x84\xfb\x97y\x86\x15" + + "jg7R\xdd\x90\xb8;aH\x16\xf5wdgA\xb3//\xf9`\x86\xa6\x9a\x0b\xf8\xf8\xb7\xff\x86_~NF-\xa7\xe4S9\x9d\x09K=\xd6F\xf8\xf3\x9b" + + "L\xd7;\xa7\xc5`\xf8S\x97o|q:\xdf\xa0\x15y\x9d\xdc\xf8\x94\xeaV9^\xfc\xb3\xcf\xfa\xad\xe9K\xef]z8\xe6\xd3\xd1\x94A\xe2\xee" + + "\xcb\x19~Exr\x09RyX_\xe6\xf9\x80\xe9a\xf3<\xc3\xf5\x11\xb0\xc2\x04\xd41\x8c\xd3\xae\x93\xaa<\xc35\x1e\xa1i\xac\xa6\xbeH*" + + "xj\xe6h\xbe\xcc\xf3\xc8\x1c\xab\xd9\xee\xc3\xf4\x1e\x10\xe7E\xb21#$q\xf78,\xdd\x9944\xfcg\x91\xa5\xae\xf9\xb9\x17N>\x1f\xf7" + + "\x882\x9cN\\\x1e\x8e\xae_M\xd0#\xab\x8c\x8f\x9b\x8b8\x99\xd6\xc5\x81\xac\x03\xf6\xb3\xc8t\x0a\x1f\xe1\xa7I\xb9\x01g,\x9f" + + "Lw\xc8e\xfa\x9d\xe4X\"\xd1=\x81\xdbx\xbeq{i\x13\xbc\xfc\x89\xfeQ\xc0\xb8\xe6\xfe\xfbC\x96z\xb3']\xd9Q\xe8\xa6\x1d\x05\x89" + + "#\x0e\x13\x02\xc6\x1cl\x1dl\xcf}\xad\xc5\x83\xa0=\xda{\x8c\x10v-\xf8\xa71~\xadr|?9|)dc\xa8\xb7(|\x8b\xa1\x10\xf3\x92\xaa" + + "\x05p\xcd\xbd,@Z\xd0Y\xd66\xe6\x05h\x02\xf8N\x1a\x84\xe7\xbf\xf7\xe6\xb2\xc8\xc8O\x1b\x1d&\xb0\x9a\xa3\xe3\x05\x7fv\xb3|" + + "\xb88\xee\xa7\xe8\x01\x85\xaa\x978*\x01\xe5\xdc\xc4\xe7\xbf\xffT\x07\xba\x1d:P\x93\xb0\x9f\xb6\xa1\x85\x08\xe7\xe7\xc9XK" + + "\xbeB\x9a\x1e\xa8\xbfM@7\xf0\x0d\xee\xb0I\xdd\xe9\xaeC\x8f\x8bO\xc2g\xfb;\xe3\x84\xf5|\xd9\x88-\xba\xa3\x84\x82\xe4\xd9\x0d" + + "\x1a\xbe\xf9\xa9\x02G\xfa\xbes;\x9c\x1dw{j\xee\x16Xb\xee\xa4\xcd6\xc0pm\xd3\x88\x96bvd\xc2\xe0X\xf7\xcf\x87\xbd\xdf9\xc5" + + "$`c\xf8\xf2\x12\xce\xfd\xe0+\xb4\xa3A6\xed\xd5\xa0$\xc9#\xd8\xccEA\xe97\xb83Q\x1c\xe4\xb9\xe2\xa3b\x17bQbf\xd3\x1fD\xd9\xe2" + + "\x1f\x8bh\x1b\xbf\x80*}m~\x10\xa5\xcc#n\xaa\xaa\xf4\x1b\xa9\xe8\xf3\xe50\xfc;Q\xbbG\xa4}\x14\x93\xfc~\xc0[\xbe\x81\x18\xd4" + + "\xa8h\xb4\xd3e\xbe\x9a\x89\xe3a\xadc\xcd\xc1Kg\xd8\xf4\xa2\x96\x0c\xf27\x9bn\xff\x1e\xc7\xc5-=\xd4\x05o\xe8e\x19\xca\xe6" + + "6\xdbpz1\xee\x82\x0f\xf8p\xfe\xaa\xbf\x8d\xf1\x8b\xf4w+\xdd\xea\xe4\xec\xcc\xad\xb3$\x9b>\x87K\xc9\x04\xee62\xdb@%v\xb0F" + + "\xda\xa8\xd2\xd5\x89\x83|r\xe1\xb2_\xad\xb6\xa2\xe91\x1a\xe0\xa4\xaf\x7f,\xa2\xe8\xe9\xc0\xa0H\xc92\x8e\xf9\xdc>\x8a\x19" + + "\x87\x7fit5Q\xc7]P\x8e/\xa6|\x8e\xdd\xf6\x0a\xb5\xc6\x02J\x8e\xa65wk\x93X\x11\x93\xe8q\xb0\x1c\x04\x8fX\x08\x1f\xda\x8a\xe7" + + "\xc1\xe3\xefG\xc6\xf1\xb3\x10<\xc1\x88i\xf0\x84J\x12\x00\xc0!d\xda\xd2\xba\x83\xc1{#h)~h3\xb0\xf1r\x88-\x15\x0a6\xe4)Z3\xa5" + + "\xd2\xe4\x16w>#\x07\x99\xee\xa1\x97\xb9|g\xf79\xf5\xcf\xee\x1d\x044Nu\xb7\xbb\xfe\xa6\xb5\xfa\x98^\xd4\xdc\xc7\xf5c\x85*" + + "Y~~\xd8k\x1c\xc9\x07.W\x84\x1c\xf0d\x9a\x03>|\x98\xe7\x80'\x8b9 \xac\xb1d\x05\xe7\x87\x91!S;\x86\xdb\xce(N\xa3!>&\xe9\xc2" + + "[\xb7\xef\xe3\x84\xd3\xcd\xf8\xa8\xde\xbd%\x10\x9a&\xcc]\xa0P\x10\x0fW\xa7E$\xe3\xf02\x06\x8al\x03\xe7\xf0\xf1o\xff\x03\x12" + + "\xbe\x00\x95R\x0d\xac\xf8\xfb\xb9\x8f}\xa6\xa84.\xdc{r\xfb\x95\xdd}J\x02\x05\xf0-\xab\xbb\x1f=\xb8l\"\xae\xd3\xde\xcdl\xe1" + + "\xbd\x95`9\x7f\xe1\xd6|\x01\xf2\xd9\xb3p\x82\xc0x\x90\x86\xf1rcHk\xff\xe0L9\xc8\x0f#3\xa7\xf9a#|\x00\x13\xa5\xdf\xbe\x1b" + + "]\x1b\x8f\xae\xd9\xb8\x96\xeb\xaf\xa6(\xecg\x9b\xa5\x18\xbdK \xf9u\x96j\x04\x88Sj9\xde\x89ZvL;\x9f\x08i\x17\xb2\xa1\xe6\xff" + + "\xf0\x81\x88~\x8c~\xd6\xa7\xc8O\x10o\x82\xf4Q\xa6?\x84\x9e\xc1\x17\xa3m\x8d\xc2\xedA<\xf5\xee\xf2w\xae\x8dP\x86s\xa3{}\x82" + + "\x0c\x1a\xef4\x04\x91\xdf\xda\x88j\xdew\xa2\xae\xcb\x1d\x95@\x05U\xc0LS_hsc2\xabc\xbf\x13u\xe4)8b\xdf2\x11u;\"\xe2\xc1)\x87" + + "n\x03\x05o\xa76\xe96P\xf0\x15)C\xfaI\"\xdaC\xd4\xa3\x19\x87\xfa\xc5\xa1\xd2<P\x82\x16\xa7\xb5\xfdY\x08\x92\x03'\x98\x8d\x8e" + + "\xdb\xb9`t4/\x861M{p\x07\xcfR\x16P\xa2\x12\xee\x11\x00IJ \xb7>\x17\x07\xb4\x0e\xbb\xa7\x01\xae\xfe\xe5\xaaA\xbb;\x0d\xb9" + + "4?j\xaa\xb6\x0dR\x09\xbfCsp\xbf6\xb5\x91{\xf5\x053\xb9\xbdR\xfa\xc8\xec%p\xb8\xe8\x9a\xa1\xd3+y\x14\xa0h\x87&\xa1\x85x|\xf7" + + "\x10tvhF\xe8\xb8\x83Y~\xa0\xf42j\xa1S\xe0\xd7\xd2\xc6\xf7\xafcn\x1du87BT\x91\xe0{i\xc2\x9bj\xae\xfd\xcb4\xf7\xa0\x9c\xca" + + "\x0fn\x08Z\xa2\xf3\xb0\x06\xb5\xa3\x85nU\x7f\xceT\xf4'\"\x19+\xc6\x1a\x89f-m#\x9a\xdd\x94\xf7\xfcN^\xf8%\xdbh\x83\xca\xbf" + + "ZF\xc2nZ\xd1\x08e\xd1]\xf8\xafq\xb8\x1a\x11Y\xa3\xdd;U\xc8gU\x99(\xcb\x83KU^\xfd\xb8\x83F%\x9e\xfb\xc5%\xdeb|\xa8\xb2\xe8" + + "\xb4\xc0\xd9\xdbdz\x82\xffi\x0f\x1f\x99\xe8\x7f\xee\xba\xf4\xdf\xb1\xd1\xa4\xd0\xb8\xd0\x7f\xa3\xeb\xfe\x00\xc1\x9d\x0d\xfc" + + ":?\xbb\xfb\x14r5Dh\x0c*+EY\xee\x02}\x9e\x85\xee>\xf6\xafJ\xdeK\x03\xe6\xc9Q&\xc0\x94\x07\xb5\xae\xef#\xc1,\xb4t\xfdh\x9f" + + "m\xfb\x83\xe64x=\xeeO\xde\x87#\x05\xda\xcb\x02\xd8\xdb\xe0\x83O\xe2\xff\x95n\x95\xfd\xd4\xdb\x1d\x8fI\xad,\xf0\xb8\x8d\xfe" + + "\x95\x93\x07\xa5\x0c\xf5\xec\xd9aN\xf0o$\xf1\xeb\xc1\xb6\xdfM\xe1\xe9\xd9~\xbf\xfa\xff\x00\x00\x00\xff\xff\x9f\xeeaD" + + // 18423 bytes generated from core_test.go.in + _fileData2 = "" + + "x\xda\xe4\\\xefn\x1bG\x92\xff\xccy\x8a2\x01.8\xf1x\xf8G\x92m1\xd1\x02A\xe2\x1c\x82\xf3\xe6\x8c\xd8\xce\x87\x93\x89\xdd!\xa7" + + ")5D\xf6\x8c\xa6\x9b\xb4u\xc4|\xb9\x0f\x87\xe0\xde\xe4\xf6\x09\xf2=o\x92'9T\xff\x99\xe9\x9eiR\xa4,/\x16XaW\x16\xa7\xab\xab" + + "\xab\xab~\xf5\xa7{\x8a\xd9n\x07_\xc1\xbf\x11F\x8aD\x90\x14\xd6\x8c\x0a\x10\x84\x0b\x0e\x8b\"[\x81\xb8&0\xcf\xd8\x82^\x01" + + "\xf9\x94\xac\xf2%\xe11|5\x80ge\x19\xe4\xc9\xfc&\xb9\"\xb0\xdd\xc6o\xd4\x9fe\x19\x04t\x95g\x85\x80~\xd0\xe9\x16d\xb1$s\xd1" + + "\x0d:]dI\xd9U7\x08\xb6\xdb\"aW\x04\xe2w\x84\x8b\x1f%1/\xcb\xed6/(\x13\x0b\xe8\xf6n\xbb\x10\x97e\xb0\xdd\x12\x96\x96e\x10" + + "\x06\xc1` E\xfa%Y\xae\x09GqDB\x19\x87\x84AV\xa4\xa4 )pr\xbb&lN [\x80 \x0c8\x11pC\xee8\xac9Ia\x91\x15\xa0\x05\x88\x91\xd9" + + "\xbbk\xa2fJ\xf2k\xa2HWk.@\x8b,\x9f\x92O9\x99\xa3R*\xda\x1br\x07K*\x19\xf18\xd8$\x85-\xd7\x05\\\x8e\x86\xd3\xed6~w\x97\x93" + + "\xb2\xdc\xba;UDe\x09\x80\xfa*\xcb\xa8\xda`\x19\x04\x8b5\x9bKV\xffN\xeex\x9f~\xe2\x10\xc71e\"\x84\xbe\x14\xed\xb2f\x1b\xc2" + + "6\xe8\xe0\x86\xfe\x1a\x01\x85\xc9\x05\xa85p\xce6\xe8t$\xf9\x05$yNX*'G\x96\x8c\x97t\x1a\x06\x9d2\xe8\x14D\xac\x0b\xe6,\xfd" + + "\x96\x08g\xe5\xb7D\xc0\x16\x14!\xfcD>\xf6m\xf9\xe28\x0e\xf1\xff`8\xdc\x90\xbb7\x19\xc7\x05\xc1\x12\x952a\xc4\xa5\x11ljq-" + + "\xb5\xa1\xd4t\x01\x1b\xb8\xb8\x90\xfa\xc5\xcfZ>\xa0A\x07\xa5\xad\x04\x86g\xa3JfT\xea\xabU.(#\x9c\xf7\x05|e,\xfcN\xaa\x08" + + "\x8d\xc3q\x13\x01r\x7f\xc2c\xa4\xbd\xeb\xcb\xb1\x8e\x88_\x15EV,\xfa]F\x97r\xa7\x94\x03\xcb\xd0\xfc\x88G\x92\x02A\xea\x09" + + "\xf46\xdd\x08\xb8\xd4Y\xd0A\xc5\xa2\"\xc2\xfd,\xe5\xd3\x03\x99\"#\x8e;G9,6\xfd\xae\\\x08\xd6\xcc\x80py\xa7mAR$\xeej\x99\xe4" + + "\xfc\xc9Ee\xc1a\xf85\xec\x12\xec\xa7\x8c\x11[\xb6=r\xd9J\xfen\x991\xd2Vp\x82\xcb\x1a\\hxM\xa6\x08\x8a\xa03\xb3E\x1aE\xf0" + + "2\x82\xb30\xe8\xcc\xf1q\x12+\x86\xf89\xfe\x99\xac\xb2\x0d\xe9\xcf\x94J\xe7\xf1\xab\xdbu\xb2\xe4\xfd\xa4)\xfa\xfbJ\x0f@\x90" + + "\x84*yQsR\xea$\x829\x0a\x0ed\xc9\x89\x9e\xfb:\xbbZ\xf4\xbb\xbd\x8dY\xcf,\xd6\xdb\x84\xf6\xbc\x99\x9e\x8a\xe2\xbc\xcf\xd3" + + "DT\xe2<9H\x1e\xca\x1c\x89\x9e4$\x0a\x9aP\xbc\xca\x04\xaa\x81\x1b\xb1\xbe\x96O\x9e\xb4 \xb0\xe8w%\x05\xc6\x1d\x1c\xe2DL$i" + + "o\x13\xc1\xc7\x84\x09\x09\x83\x08\x1f\xb5L\xf6\x9eQ\x8c\x87^\xc7\x18\x0cP\x14\x0e\xfc:[/S\x09P\x1dQ!]\xe7K:O\x04\xc6x\xf8" + + "\x8f\xd9\x86fk\xbe\xbc\x03qM9\xe2\x05#;\xe7t\xb6$\xf0\x91\x8ak\xc9\x0a\xe3\xe4*\xc9qlIV\x84\x89D\xd0\x8cE0[\x0b\xc8\xc45" + + ")\x10e\x05\xe1f\x84CR\x10\xd8\xd0d\xb6$1\xba\x94\x8d\xdd\x08\xce\"\x18E0\x96\xbfO$nN#8\x97\xbfO#x\x1e\xc1\x0b9:\x94\xff\x1b" + + "\xc9\x87\x9a&4\xca\xd5\xca\x91*~MX?\x8c`I\x98\x85\xd2Z\xe3\x92\xd0U\xb9\x9e\xa35\x9djf\xbd\x14.{\x9b\xa9\xd2\xb6zV\x87\x85" + + "\xc1\x000,\x1a\x85\xce\xb3\x15\x81l-\x80K\xe7\x8a]\xa3\xbfRZ\xe2h\xf7':\xd9\xc4\xdf\x13\x92K\xa4\xf5%{\xc7\xa1\xc2\x96\x80" + + "5\x8b\xc9\x07\xa6\xe4|\xba\x89>0%\xe9\xd3\x8d\x91\xd2\xdaq\x13\x1f\x7f!\xab\x19)\xf85\xcd\xdb\xf8h\x9aD\xd9\xe3$\x82\xd3" + + "\xf0\xb0H\xde\xb0\xc1w:]\x7f\xcb\xee\xfa\x9b\x10\x93\xd67p\xd66\x81\xb3E{No\x136p/\xfdkc\xd9\"4i\"\xd8n\xe9\x02\xe2wE\xc2" + + "\xf8\"+V\xbc,\xa5}p\xcf\xc02\xf6l\xb5F\x18\xb2+\xe0\x045O3\x16\xd7\xb8\xc9n\xb4\xc4\xd7Y\xc6I\x1f\xb5\xd5\xe7v:\x9be\x99" + + "rQ\x9d\x8dd\xe4\xb6\xac5\x9c\x06\x9d\x12\x0d\x9b\xdd\xb8\xc1\\s\x1c\x86\x13XQ\xceQ\x00\xa2\xac\xd8\xf5G\xad\x1f\xb25K1\x9c" + + "\xa0\xc25)\x0c-o\xdf+tKd\x93\xc6\x17\x09.\x84\"\xba\x12.\xfa\x7f\xd3\xf3k\x1foj}\xbb\x8d\xff\x93\x14YY\xfem\xa7\x142g\x19" + + "I\x18]\xeeY\x07G\x0fX\xa0\xb6\xdeA\x96{\x93\xe5\x0f0\xdbh\x97\xd9\x90\xdd\xe8\xf3l6\xb2m6\x18\xc0\xb7\x90gy\x8eiC\x90\x95" + + "J\xc2\x98\x94\xd2\xba\xda\xe6D\xa8\x9da\xd4\xe2!:\xc9i#\x06\xbc&\xecJ\\C\xb2\x10\xa4@~\xadh\xd5\x8d\xf4l\xe5\xb5j\xe97Y\x8e" + + "\xb9$AG \x9f(\x17(\x1fV\\J-\xb2Vy\xb6\xc0M\xec\xd3\xecC\xb0\x85\x93\x8f\x04\x96\x96Wj%a\xaaB\x91U\xfd\xfd\xc2*\x14\xe2\x9a" + + "; h\x86 c\xa6\xf4\xb9O\xa0f\xad.\xeb\"+H\xb5\xa3(\x11\x9e\xfahl\x0a$u\xc2\x9a\\\xc0\xe5\x94\x8bb=\x17u\xf1n\xd5\xfaA\xa7" + + "#\x05BE\xa3\x10A\xa7\xb3et\x19)]\x97\x11~\xb6\xc8\xb7\xa53R\xd5\xeb\xc3\xd0\xff|\xb4\xe39\x06\xfd\x1dC/\xc2\x08D\xb1n>~\xa9" + + "\x12\x04\xa6a\xef\xf8P&\xeeQ\x04Ck\xbc\x0c*\xd7\x91\x1e.\x0b\x1cU\x83\x12\x11V\x87\x1cd\xe2\xa6\x1b\x95iLJ%\xc2I\x17H\x10" + + "\xa3\"\x95\xa2M\xf2}\xa2<>\xf6\xe5\x1b'\xdb<\xf5\xa7\x9b\x8a\xad\x95\\c'\xf7x\xa1\xb1\\\xfa\x0a0\x0b\x1c\xdd\xa4\x1bA\x97" + + "\xe0/\x8a\xbf\xee0\xb8|Y\xf0\xd4\xf6i`\xc7c\xb8\xb1\xaa\xbb\xfcVU5\xdaY\x04/\xc2\xe3\x80w&g\x9d}Q,|\x0e\x10\x1e\x09\x05?" + + "\xf2\xb7\xeb\x19'\xc2\x7f8\xad\x8eb\x01\x9a\xaeQt\x8dM\xb1{\xbek\xf3\x97S\xdbz\xa8\xde2\x02\xdbn\xd2h\xae\x03\x9e\x87\xa8" + + "j\xb9\xeb\xbc\xc8f\xc4\x06Y\xad\xa5'r,\xae\xa4\xbf!waSSf\x10\xebM\xf8\xe3\x7f\xff[\xfeK\xb9\xb2v7\x02\xc9\"\xc2\xec\xa25" + + "#\x19\xcb-\xd7\x8c%Q\x08[\x18\x0c\xe0\xf7\xdfpz\x02\\\xb1\xcd\x16@6\xa4\xb8\x13\xd7\x94]E@\xd9|\xb9N1\x05S\xc1\xc9r\x11\xef" + + "\x90\xe6\xf7\xdfv\x0a\xe3\xb5\xd0O\x99\xd8e\"\xbf\x93\xd5\xfbR\x96S\x9a\xaf\x0f\xdf\x91\xca>5\xde\xcd\xe3\xea0\xdc\x1cS\xb1" + + "v\xcf\xf0\xd8\xa5PONB\xed5\xf78\x06](\x8c6Ljp|\xb0]\xd1O\x0d\xfe\xb5\x0e*\x1e>\xc5\xbe\xd2G\xe2\xb6^Y\"\xf6\\\x1cdi\xda\xb8" + + ":Py\xe5,\x82\x97;#\xdf\x92,D\x04\x05\xbd\xba\x16\xca*\x1dr\x0b\xd5O+\xfe5\x82 KD\x04\xf2\x97z\x06\xee\xcf`\x00f3\xf2\xd4" + + "\xabj\xb4dE`\x83\xa2\xef\xb2s\x9d\xe8\xda\x1c\xbe\xa7\x8b\x05)\xb0\xfa\x92,x\x93G3\xc5K\xe1\xb24u\x1f\xca]X\xb3\x1a\x81\xb6" + + "\x0d\xbc\x9a$\xe8`}\xf5KR\xe0\x01_VUYN\x0au>\x8f\xe3X\xaf\x19\xff\xc8\x04)8\x99\x8b~\x96\xa6\xa1\x16\xa1V\x9c\xfc\xe8\xa3" + + "sH\xacQ\x96\x88\x1d\\|t\x8e\x85Z\xa3\x96\xc1l#\xde\xc3\xe5=\xa3\x19\xd3R\xee\xe2`\xd3\xb8;A\xbb\xd9\x9b\xb04^\xed\xc2\xa2" + + "q\x87\xed\x91&\x02\xf1c\x83D\x8f6CE\x04\xe3P\x91\x99\xa7c\xe9\x1c\xcfC\xc7\xd4z\x135\x0e\xdcT{\x7f\xd0\xd0\x09U\xfa8\xba" + + "\x97\xb9\x08\x93\x0f\xa4\xa7\xd5\xb7(\xf2\x19\xb9m\x86\x92\xde\xc6L\xea\xedI\xa6\xcawk\xb6vb%\xb7\xde\xd8\xa2\xf7\xd6\xca" + + "\xa9\xb2\x00\xd2B\x9b\xa4\x87\xa7\x9fM\xf6\x91,\x9d[\x8dS,D\x83\xce<c<c\x09\x13\xbc\x11s\xc6\xa6\xb0Q9\xf8\xa5L\xc3\xf6=" + + "\x8eb\xa9\xb5\x8cG\x89\x83\xeeu\xa4\x84\xcd\xfb\x9c_\x94t\x7f\xfc\xfaw\xf8\xfd\xb7Iu\x9d\x03\xcd\xdb\x1c5\xd9:t\xd7G\x1d" + + "%\x86\x92\xe93$\xc1\xe4\xf9\xeb\xdfA\x09t\x90$\xed+7G1\xb5~m\xa9\x1a\xf7[\xbb\x85\x94u\xd5nm}Wq\xdf#\xab\xae\xcd\x1auY\x15" + + "#Z RE\xd9\xa4\xba\xe7\x7f@\xb6:\xab\xea\xb6\xbc\xa0+b\x13\x8c\xeb\x8298<\x99\xc9=\xe9\x1f\xa7\xbeW9M\x8a\x1c\x81\xfe\x87" + + "\xd1\xa5\x0a*\xe6\xb3\x0ck\xe6\xa1\xfc\xd4\xa4\xb4\x12\xa0\xbd\xc5f\xee\xf1\x94\xfd\xb8M7\x17\x1cB&\xd5b\x11\x1a\x95(\x12" + + "=\xea\xf2\xb2\x14wX\x08k\xc5/\xcb\xe6u\x08\xb3P\xa9+\xd4=7\xb2q\x8dG'\xca\xc1\x1f\xbf\xfe\x1f\xf46~\x10\xde\x1b\xe3\xaa\xc3" + + "\x83\xbaO\xa2\x0b}\xb9\xac\xae2\xcc\xc5\xb5^\xfb\xc9\x05\x0c#\xdf\xa6\x1a\x819\xbb\xd9u\xb5\xda{\xba\xb1g\xed=\xe7\xb4\xc5" + + "\xcen\x1a\xf7\xad\xb6W\xa9\xac\xf4/\xecP\xbb\xbc\xc73\xc3u(U\xd2\xab\xf7\x1b/[\x0eU\xcd\xf4:\xcf\xa8v.\xdbs\xf4\xa4\x078" + + "JU\\<\xaa\x8f|\xf8`\xb9\xc8m\xe5\"\xb7G\xb9H\x13qo\xefV~\xd0%\xfb^a\xcc\x1a\x83\xa7U\xa67o(\xcd\xe0\x89y\xbf\x84\xd8k\x00" + + "\xf9\x1f\x84\xb6$\x82\xc4\xb2\x80\x1a\x9bU\xe4\xb3\xd6X\"\xe7Tl\xd4\xfbM\x1b-NmS3L\xee\xa7J\"\x98\xbb\xa8U\x84/k\x08\xce" + + "]F;Hf.I\xab\xd4z r+8<~\x80\xff\x9fG\x08\xf0\xad2V\xbdk>\xf0\xe2aF\x16YA\"X\xcbY\x1eh5\xd1\xd5\x99_\xa3\xc2\xd2\xfd\xc7\xe0" + + "\xd6Q\xb3y\xaat\xee\xf2\xdc;@s\xaa\xac\xe6\xec\xbaT6\x91\xda:\xa2\xa8\xf7\xb9'a+\xc7\x9f\x1euRQYR\xeaY)\xc8\xbc\xc1\x97\x8f" + + "\x94\xae\xc2\xf6\x89F\xd3\x1e\xfa.\xd6\x0f\x0c\xb5\x92\x17\x18UX3V\xf3\xc2\x01\xa5R\x99Z\x8e\x18{\xedZ\xa6n\xd7P\x94\x17" + + "\x9e\x94m\xd6\xc3Dm3\xf5\x02\xf0\xdb4=\x0e}\x1aI\x0a{J\xb9Z\x82=\xc8;\x12w5\xd6\x1e\x09x\xd6\x05\x9b\x81\xd9\xb8\xf1\xc8" + + "\x1c\xac?\x0fyR\x9d5\xec\xdc[\xe7GE\xde\xb7i\xba?\x1e=\x02\xec\xd4\x1a\x8f\x8e9\xdd\x82\xf3O\x1e\xf4\x9a\x04f\xec\xac\x05" + + "\xba3?\xe8N\xf4\x9b03O\x02\xec\xdc\xc6\xdc\x89\x17\xd5\x16\x12\xeb@\xf9\xdc\x8f\xd5j\xd9\x87\x80\xd5\xd8\xe1\xcb\x87I\xb5" + + "\xd2\x17\xc7k\xb5\xcc\xa3C\xf6{\xca\xe7I\xf1\xcf\x1c*w\xc1\xf5Qc\xe4\x01x5\xa7\xf9\xd3/\x00\xd8\xca\x0a\xff\x90\x08\xabW" + + "\xfb\xe2\xa8\xad\xd7y,\xd8z\x1a\xa0\xea\xae\xaf\xc4\xd3\xeeE\xd9\x9e\xf3\xb8V\xec*\xb9!}\x0b\xb8\xad\x8e:<\xba\xaf%)e1.\xd3" + + "n\xc2\xa9\xfe4W\xcc9\x92\xeb\xb6a\x1e~\x0d9|\x03\xc3V\xf5\xc3nX\xf6\x91\x01e\xf9Z5\xabTM\xaav\x0b\x0e\x0az\x99O\xe1\x02\xb8" + + "V\xbfi\xf5\x09:\xa5n\xe6|H\xb3\xdd\x8fl\x9e\xad\xf2%\x11\xb2\xc91\xa7\xec\xcaj\xb9\xbb\xb7\xe3N.\x9b\xad\xab\xcbs\xca|\xfc" + + "\x8b\x82\xccE\x8b}\xc5\x1d\x99gk\x11\x01e\xad\x8b\xccW\xc9\xfc\xfaH\x8b\xf2\xe4ce\xd1U\x92_Vf\x99R\x89]\xcab\xc9U\x1a\x90" + + "%+\x02n\xe792\xb8\xc4\xe7\xd3\xa7O\x95n\xd1\x95\xcd\xf5\xaf\xeeEg\xc6\xc6H\x8ccSt\x84Q\xd3\xb8o\x93\x8f\xb2\xad\x12z)\x08" + + "\xba\"\\!\x9e\xa4\xb2kJ7Y\x1a\x0e\x15\xc8\xe5\x82R\xdd\xac^\x12\xb7\xa5\xd7\xfc\xab\xb9<\xa3\xec\xf2*\x13\xd3\xba\xaf\xeb" + + "\xa0\xa5\x87\xc6\x98\xcc\x7f\xe7`\xba\xcf\x8e\xd4\xbb\xd1\x90\xfb\x1a\xd7\xdc\xf9\xd8w\xfa\x94\xc5j\x91}\x9dly\xc6\x1d\xf7" + + "\xa9\x11\x8f#\x7f\xbe\x80!\xfc\xe9O\xf8wo\x0c\x17\x170\xd4\x0dnWY\x05F\xdf\xedzoc\x96&\x1b\xc2\xb8\xf7j\x90\xb2\xe6\xd5z" + + "\xeb}\x84-\xfe\xcev1L&P\x0b\xe5mX\xaf\xe5A\xea\xa68\xf2\x0e\xa4\xab\xdf\xb8\xb7z\x12=\xaa<\xa0u\xed a$\xf9.i\xb4~Z\xbe\xfa" + + "&)\x04}\x00p\xfcU\x06\xae\xd2\xbejZ4\xde]\xfbv\x1dt:)\xe1s\x9b\x8c\x8b\x82\xb2\xabV3C\xab\xef\xc0\xd4\x13\xe8J\x87\x18W\x12" + + "v\x93\xe5R\xb7\x0f\xa0?\xf9\xea\x0e\xb7\xfe\x96\xcf\xee_B\xdb\xac^Cw{T\x8bP&\xc7\xfc\xd7\x80\xce\x90{\xfbT\xaf\xedq=\xb3" + + "z\xe5x\xf0\x0d\x9c\x19)\xf2\xea\xc9n1\xdc+m\xd9\x1c\x90\xa5iCP+2H\x0a\xf4\xc6\xa3\xa4R^?2\x82ei:@\x1e\x95X\x07\xdd\xae\xbd" + + "\x96\xf8\xba\xca\xc4\xcf\x12b\xa6\xc0\xa2,\xb6\xb0\x8cO\x16\xe6rMOr^\x16#J[\xf5U\xc5\x00z\\\xe2x\xe7K\x08\xc4\xaa\x14\xe2" + + "u}\xc7&YZmEFD\xcfK\xea\xbd\x0bK\x92\xfbW\xfeY]\xe9Y\\\xf5\xda\xa6c\xcdb\xba\x81\x1e\xff\xc0>\x08\xbd)L\xe3\x1f\x84^\xa7f" + + "\x8d\xde\xeb\xdb\x9dYL\x07\x0f\xd3tj\xbf\xb9L\xc9\xa7CO\"\x8c\x90ti\xe5o\xd3#\x08\xbe.A\x00\xca\x9c~\xa6\xaa\xa5]\xfb\xe7" + + "\xb3Q\xed\xbbU\xdft\x04\x8d\x16B\x0f\xd5x\xda8\x0d\x84^\xb2\xa1\x87l\xe8]\xb2Iu\xff\x92\xe3\xea\x0c\xd2\xa6=\xf7\xd2\x9e" + + "\xabc\xf7I\x8b\xfct\xea{qs\xae\x0e=\xe3\xa3n\xae\xb51\x11\x07\xcaTV\x7f\xe1\x81\x0d\x8b\x8a\x85<\x9dX\xaf\xf2\x9c>p?\xff" + + "}7\xd4\xe2.\x97\xdf\x88$\x85\x03\x14\x85\xc2\xfe\x8d\x1a\x0a\xe5w^\xfa\xa1M\x82\xd2\xe5\xbb\xcf\x0b7\xa1\xec\xb9\xc8\xef" + + "\xfay\x047a\xf5\x9d\xbe\xbcZt\xcd\xe8\xad\xc4a\x8d\xf8\x1f\x8al\xa5T\x7f(\xec\xd5Q\x812A\x8aE2'\xdb\x1a\xe2\x9e\xb7/\xe6" + + "\x84\xad\xec\x8c\x05\xf1b\x99%\xe2\xf9\xa9\x0bk\x87\x822a\x8d\x8e&\xd0\xc0\xe7\xd8y\x82X<i<)\x1b/[\xc2\x9a\xb9\xca\xc5\x16" + + "\xff\xee\"\xcb\xba\xce|\x84`w\x96\xfcW\xf3\xa9\xcd\xf54t\xe5E\xcd\xa2\xa8\xf8o\x1f\x1d\xe1D\xff\x8d\xb0=\xd3\x7f?\x0f\x9b" + + "[\xfd\xca\xdd+\xa3\xcb\x89\xfb}\x97\xd2\xb9\"8\x0a\xfe\xb6eUP\xcc\xd7\x12\x87\xa6T\xae:S\xf1\x81\xd5\x9e\xea-b-\xa7\xf8K" + + "\x92\xeb/$U\xc7\xf7vt\x97\xab\xf9\xbeBT6\xc0'\xb7\xf6\xd9\xd0\xb3\x9aEO\xe23\xadeh\xfd\x0c\x06\xb0f7\xe4.\x99-\x09\xa0K\x18" + + "C\xa0}*#\x8e\xca\x8a\x81wB\x0dj\xef\x0a\xb2RU\xed\xdf\x0am\xdb\xb2Mn\xd3\xb9\xe7D\xed \xf5$\x9b\xd4\x13\xd0\xd5\xa5\xaa\xe7" + + "+\x06\xeak|\xcd\x12\xb3\x06\x9fs4u9Op\xb6\xe3v\x13\x0c\xbe>N2Z9\x85\xf5\xc9\xb4\xb1f\xed\x81\xd6\xad\xeb\x0bU}5\x9f\xf8\xc4" + + "S\x08\xd8\x96n\x0a\x9a\x80\xe9\xc8\xd6\x8f^\xc8G\x96\x94\xe3\xa3\xfbYj@:\x1e\xd3\xf0\x8a\xdd\xf7Zf\xfe\xa1\x9eq\xd8\xb7\x1c" + + "~\xc0\xb2\xf4(\x17i\xf8\x88\xa7R\xd1E\x89}%j\x85o\xd9s\x17A\x03m\xbb\xbf\x15\xb3\x9b\xb0\xf5\xce\xc9F\xaf\xe7+\x0f\xe1a\xcc" + + "L\x111\xde\xc9\xd5\x8b\xf2\xf6\xbe\xc6\xee\x0am\xbf(\x8f#o@t\xd4\x9e^\xefz\xef\xec\x93\xe9\x04\xcb\xa3\x83\x17\x97a\xe3d" + + "\xdc\x12 r\x93X\xed\xc7\x07\x88\xe4\xe1y6\x9d\x98\x1e)\xfd\xe4yk\x15t\xc4\xb1\xf3\xe4\xe5\xfe\xcd\xe0\xca\xe8\xb0-\xeb\x9c" + + "\xb8;\xd67\x11\xfb\x09\x9c\xd16u\xbd\xd7\x06\xf1\xd9t/k\xcf\xb1\xfd^\xc2\x13/\xad\x05P\x15<[\xdb>w9J\xaa\x06\x10\xbd$\xce" + + "x\x9b\xbe\xdez\x8b|8\xbd\x9f}\xdfv\xbf\xe7x\xbc\x0e\xef[\xa29\xe5E\xb8g\xa3\x07Gi\xf7+H:\x9eZu\xf8\x03\xbe\x8d\x14\xc1\xbe" + + "&\xea\xf6\x12\x07\xc5o\xcc\x06\xf2\x04A\xd2\xe3\xc2\xf7\xe5T\x1e\x16\xdb\xb5\x8d[N_\x9a\x00\xd5x2,\xdb\x95\x81\x1aQ\xff\xa9" + + "\x88q\x04\xe7\x16I\xab\x85Hs\xb1/t\x1a-De\xe4\xbfN;:\xd9\x1a\xf5T\x8d\x98*\xe7F\xeaj\x8d\x82\xfc/\xa5\xb8\xaf6\xcc\xb1\xc6" + + "Z\xbc\x9eyI\xa7S4\xc5\xd1Y\xdb\x08\x82\x87\xbbo\xe28\xfe\xb3\x17\x0d-1w\xa2\xe0\xff\x03\x00\x00\xff\xff\xcd:J(" +) + +// END OF GENERATED DATA diff --git a/makeset/stringset.toml b/makeset/stringset.toml new file mode 100644 index 0000000..400758b --- /dev/null +++ b/makeset/stringset.toml @@ -0,0 +1,11 @@ +desc = "A set of strings, the main package of this module." +package = "stringset" +type = "string" +zero = '""' +transforms = true +toString = "return strconv.Quote(x)" +imports = ["strconv"] +testValues = [ + '"eight"', '"five"', '"four"', '"nine"', '"one"', + '"seven"', '"six"', '"ten"', '"three"', '"two"' +] diff --git a/stringset.go b/stringset.go new file mode 100644 index 0000000..9045938 --- /dev/null +++ b/stringset.go @@ -0,0 +1,444 @@ +// Package stringset implements a lightweight (finite) set of string values +// based on Go's built-in map. A Set provides some convenience methods for +// common set operations. +// +// A nil Set is ready for use as an empty set. The basic set methods (Diff, +// Intersect, Union, IsSubset, Map, Choose, Partition) do not mutate their +// arguments. There are also mutating operations (Add, Discard, Pop, Remove, +// Update) that modify their receiver in-place. +// +// A Set can also be traversed and modified using the normal map operations. +// Being a map, a Set is not safe for concurrent access by multiple goroutines +// unless all the concurrent accesses are reads. +package stringset + +import ( + "reflect" + "sort" + "strconv" + "strings" +) + +func toString(x string) string { + return strconv.Quote(x) +} + +// A Set represents a set of string values. A nil Set is a valid +// representation of an empty set. +type Set map[string]struct{} + +// byElement satisfies sort.Interface to order values of type string. +type byElement []string + +func (e byElement) Len() int { return len(e) } +func (e byElement) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e byElement) Less(i, j int) bool { + return e[i] < e[j] +} + +// String implements the fmt.Stringer interface. It renders s in standard set +// notation, e.g., ø for an empty set, {a, b, c} for a nonempty one. +func (s Set) String() string { + if s.Empty() { + return "ø" + } + elts := make([]string, len(s)) + for i, elt := range s.Elements() { + elts[i] = toString(elt) + } + return "{" + strings.Join(elts, ", ") + "}" +} + +// New returns a new set containing exactly the specified elements. +// Returns a non-nil empty Set if no elements are specified. +func New(elts ...string) Set { + set := make(Set, len(elts)) + for _, elt := range elts { + set[elt] = struct{}{} + } + return set +} + +// NewSize returns a new empty set pre-sized to hold at least n elements. +// This is equivalent to make(Set, n) and will panic if n < 0. +func NewSize(n int) Set { return make(Set, n) } + +// Len returns the number of elements in s. +func (s Set) Len() int { return len(s) } + +// Elements returns an ordered slice of the elements in s. +func (s Set) Elements() []string { + elts := s.Unordered() + sort.Sort(byElement(elts)) + return elts +} + +// Unordered returns an unordered slice of the elements in s. +func (s Set) Unordered() []string { + if len(s) == 0 { + return nil + } + elts := make([]string, 0, len(s)) + for elt := range s { + elts = append(elts, elt) + } + return elts +} + +// Clone returns a new Set distinct from s, containing the same elements. +func (s Set) Clone() Set { + var c Set + c.Update(s) + return c +} + +// ContainsAny reports whether s contains one or more of the given elements. +// It is equivalent in meaning to +// s.Intersects(stringset.New(elts...)) +// but does not construct an intermediate set. +func (s Set) ContainsAny(elts ...string) bool { + for _, key := range elts { + if _, ok := s[key]; ok { + return true + } + } + return false +} + +// Contains reports whether s contains (all) the given elements. +// It is equivalent in meaning to +// New(elts...).IsSubset(s) +// but does not construct an intermediate set. +func (s Set) Contains(elts ...string) bool { + for _, elt := range elts { + if _, ok := s[elt]; !ok { + return false + } + } + return true +} + +// IsSubset reports whether s is a subset of s2, s ⊆ s2. +func (s Set) IsSubset(s2 Set) bool { + if s.Empty() { + return true + } else if len(s) > len(s2) { + return false + } + for k := range s { + if _, ok := s2[k]; !ok { + return false + } + } + return true +} + +// Equals reports whether s is equal to s2, having exactly the same elements. +func (s Set) Equals(s2 Set) bool { return len(s) == len(s2) && s.IsSubset(s2) } + +// Empty reports whether s is empty. +func (s Set) Empty() bool { return len(s) == 0 } + +// Intersects reports whether the intersection s ∩ s2 is non-empty, without +// explicitly constructing the intersection. +func (s Set) Intersects(s2 Set) bool { + a, b := s, s2 + if len(b) < len(a) { + a, b = b, a // Iterate over the smaller set + } + for k := range a { + if _, ok := b[k]; ok { + return true + } + } + return false +} + +// Union constructs the union s ∪ s2. +func (s Set) Union(s2 Set) Set { + if s.Empty() { + return s2 + } else if s2.Empty() { + return s + } + set := make(Set) + for k := range s { + set[k] = struct{}{} + } + for k := range s2 { + set[k] = struct{}{} + } + return set +} + +// Intersect constructs the intersection s ∩ s2. +func (s Set) Intersect(s2 Set) Set { + if s.Empty() || s2.Empty() { + return nil + } + set := make(Set) + for k := range s { + if _, ok := s2[k]; ok { + set[k] = struct{}{} + } + } + if len(set) == 0 { + return nil + } + return set +} + +// Diff constructs the set difference s \ s2. +func (s Set) Diff(s2 Set) Set { + if s.Empty() || s2.Empty() { + return s + } + set := make(Set) + for k := range s { + if _, ok := s2[k]; !ok { + set[k] = struct{}{} + } + } + if len(set) == 0 { + return nil + } + return set +} + +// SymDiff constructs the symmetric difference s ∆ s2. +// It is equivalent in meaning to (s ∪ s2) \ (s ∩ s2). +func (s Set) SymDiff(s2 Set) Set { + return s.Union(s2).Diff(s.Intersect(s2)) +} + +// Update adds the elements of s2 to *s in-place, and reports whether anything +// was added. +// If *s == nil and s2 ≠ ø, a new set is allocated that is a copy of s2. +func (s *Set) Update(s2 Set) bool { + in := len(*s) + if *s == nil && len(s2) > 0 { + *s = make(Set) + } + for k := range s2 { + (*s)[k] = struct{}{} + } + return len(*s) != in +} + +// Add adds the specified elements to *s in-place and reports whether anything +// was added. If *s == nil, a new set equivalent to New(ss...) is stored in *s. +func (s *Set) Add(ss ...string) bool { + in := len(*s) + if *s == nil { + *s = make(Set) + } + for _, key := range ss { + (*s)[key] = struct{}{} + } + return len(*s) != in +} + +// Remove removes the elements of s2 from s in-place and reports whether +// anything was removed. +// +// Equivalent to s = s.Diff(s2), but does not allocate a new set. +func (s Set) Remove(s2 Set) bool { + in := s.Len() + if !s.Empty() { + for k := range s2 { + delete(s, k) + } + } + return s.Len() != in +} + +// Discard removes the elements of elts from s in-place and reports whether +// anything was removed. +// +// Equivalent to s.Remove(New(elts...)), but does not allocate an intermediate +// set for ss. +func (s Set) Discard(elts ...string) bool { + in := s.Len() + if !s.Empty() { + for _, elt := range elts { + delete(s, elt) + } + } + return s.Len() != in +} + +// Index returns the first offset of needle in elts, if it occurs; otherwise -1. +func Index(needle string, elts []string) int { + for i, elt := range elts { + if elt == needle { + return i + } + } + return -1 +} + +// Contains reports whether v contains s, for v having type Set, []string, +// map[string]T, or Keyer. It returns false if v's type does not have one of +// these forms. +func Contains(v interface{}, s string) bool { + switch t := v.(type) { + case []string: + return Index(s, t) >= 0 + case Set: + return t.Contains(s) + case Keyer: + return Index(s, t.Keys()) >= 0 + } + if m := reflect.ValueOf(v); m.IsValid() && m.Kind() == reflect.Map && m.Type().Key() == refType { + return m.MapIndex(reflect.ValueOf(s)).IsValid() + } + return false +} + +// A Keyer implements a Keys method that returns the keys of a collection such +// as a map or a Set. +type Keyer interface { + // Keys returns the keys of the receiver, which may be nil. + Keys() []string +} + +var refType = reflect.TypeOf((*string)(nil)).Elem() + +// FromKeys returns a Set of strings from v, which must either be a string, +// a []string, a map[string]T, or a Keyer. It returns nil if v's type does +// not have one of these forms. +func FromKeys(v interface{}) Set { + var result Set + switch t := v.(type) { + case string: + return New(t) + case []string: + for _, key := range t { + result.Add(key) + } + return result + case map[string]struct{}: // includes Set + for key := range t { + result.Add(key) + } + return result + case Keyer: + for _, key := range t.Keys() { + result.Add(key) + } + return result + case nil: + return nil + } + m := reflect.ValueOf(v) + if m.Kind() != reflect.Map || m.Type().Key() != refType { + return nil + } + for _, key := range m.MapKeys() { + result.Add(key.Interface().(string)) + } + return result +} + +// FromIndexed returns a Set constructed from the values of f(i) for +// each 0 ≤ i < n. If n ≤ 0 the result is nil. +func FromIndexed(n int, f func(int) string) Set { + var set Set + for i := 0; i < n; i++ { + set.Add(f(i)) + } + return set +} + +// FromValues returns a Set of the values from v, which has type map[T]string. +// Returns the empty set if v does not have a type of this form. +func FromValues(v interface{}) Set { + if t := reflect.TypeOf(v); t == nil || t.Kind() != reflect.Map || t.Elem() != refType { + return nil + } + var set Set + m := reflect.ValueOf(v) + for _, key := range m.MapKeys() { + set.Add(m.MapIndex(key).Interface().(string)) + } + return set +} + +// Map returns the Set that results from applying f to each element of s. +func (s Set) Map(f func(string) string) Set { + var out Set + for k := range s { + out.Add(f(k)) + } + return out +} + +// Each applies f to each element of s. +func (s Set) Each(f func(string)) { + for k := range s { + f(k) + } +} + +// Select returns the subset of s for which f returns true. +func (s Set) Select(f func(string) bool) Set { + var out Set + for k := range s { + if f(k) { + out.Add(k) + } + } + return out +} + +// Partition returns two disjoint sets, yes containing the subset of s for +// which f returns true and no containing the subset for which f returns false. +func (s Set) Partition(f func(string) bool) (yes, no Set) { + for k := range s { + if f(k) { + yes.Add(k) + } else { + no.Add(k) + } + } + return +} + +// Choose returns an element of s for which f returns true, if one exists. The +// second result reports whether such an element was found. +// If f == nil, chooses an arbitrary element of s. The element chosen is not +// guaranteed to be the same across repeated calls. +func (s Set) Choose(f func(string) bool) (string, bool) { + if f == nil { + for k := range s { + return k, true + } + } + for k := range s { + if f(k) { + return k, true + } + } + return "", false +} + +// Pop removes and returns an element of s for which f returns true, if one +// exists (essentially Choose + Discard). The second result reports whether +// such an element was found. If f == nil, pops an arbitrary element of s. +func (s Set) Pop(f func(string) bool) (string, bool) { + if v, ok := s.Choose(f); ok { + delete(s, v) + return v, true + } + return "", false +} + +// Count returns the number of elements of s for which f returns true. +func (s Set) Count(f func(string) bool) (n int) { + for k := range s { + if f(k) { + n++ + } + } + return +} diff --git a/stringset_test.go b/stringset_test.go new file mode 100644 index 0000000..940fd66 --- /dev/null +++ b/stringset_test.go @@ -0,0 +1,681 @@ +package stringset + +import ( + "reflect" + "testing" +) + +// testValues contains an ordered sequence of ten set keys used for testing. +// The order of the keys must reflect the expected order of key listings. +var testValues = [10]string{ + "eight", + "five", + "four", + "nine", + "one", + "seven", + "six", + "ten", + "three", + "two", +} + +func testKeys(ixs ...int) (keys []string) { + for _, i := range ixs { + keys = append(keys, testValues[i]) + } + return +} + +func testSet(ixs ...int) Set { return New(testKeys(ixs...)...) } + +func keyPos(key string) int { + for i, v := range testValues { + if v == key { + return i + } + } + return -1 +} + +func TestEmptiness(t *testing.T) { + var s Set + if !s.Empty() { + t.Errorf("nil Set is not reported empty: %v", s) + } + + s = New() + if !s.Empty() { + t.Errorf("Empty Set is not reported empty: %v", s) + } + if s == nil { + t.Error("New() unexpectedly returned nil") + } + + if s := testSet(0); s.Empty() { + t.Errorf("Nonempty Set is reported empty: %v", s) + } +} + +func TestClone(t *testing.T) { + a := New(testValues[:]...) + b := testSet(1, 8, 5) + c := a.Clone() + c.Remove(b) + if c.Equals(a) { + t.Errorf("Unexpected equality: %v == %v", a, c) + } else { + t.Logf("%v.Clone().Remove(%v) == %v", a, b, c) + } + c.Update(b) + if !c.Equals(a) { + t.Errorf("Unexpected inequality: %v != %v", a, c) + } + + var s Set + if got := s.Clone(); got != nil { + t.Errorf("Clone of nil set: got %v, want nil", got) + } +} + +func TestUniqueness(t *testing.T) { + // Sets should not contain duplicates. Obviously this is impossible with + // the map implementation, but other representations are viable. + s := testSet(0, 5, 1, 2, 1, 3, 8, 4, 9, 4, 4, 6, 7, 2, 0, 0, 1, 4, 8, 4, 9) + if got, want := s.Len(), len(testValues); got != want { + t.Errorf("s.Len(): got %d, want %d [%v]", got, want, s) + } + + // Keys should come out sorted. + if got := s.Elements(); !reflect.DeepEqual(got, testValues[:]) { + t.Errorf("s.Elements():\n got %+v,\nwant %+v", got, testValues) + } +} + +func TestMembership(t *testing.T) { + s := testSet(0, 1, 2, 3, 4) + for i, v := range testValues { + if got, want := s.ContainsAny(v), i < 5; got != want { + t.Errorf("s.ContainsAny(%v): got %v, want %v", v, got, want) + } + } + + // Test non-mutating selection. + if got, ok := s.Choose(func(s string) bool { + return s == testValues[0] + }); !ok { + t.Error("Choose(0): missing element") + } else { + t.Logf("Found %v for element 0", got) + } + if got, ok := s.Choose(func(string) bool { return false }); ok { + t.Errorf(`Choose(impossible): got %v, want ""`, got) + } + if got, ok := New().Choose(nil); ok { + t.Errorf(`Choose(nil): got %v, want ""`, got) + } + + // Test mutating selection. + if got, ok := s.Pop(func(s string) bool { + return s == testValues[1] + }); !ok { + t.Error("Pop(1): missing element") + } else { + t.Logf("Found %v for element 1", got) + } + // A popped item is removed from the set. + if len(s) != 4 { + t.Errorf("Length after pop: got %d, want %d", len(s), 4) + } + // Pop of a nonexistent key returns not-found. + if got, ok := s.Pop(func(string) bool { return false }); ok { + t.Errorf(`Pop(impossible): got %v, want ""`, got) + } + // Pop from an empty set returns not-found. + if got, ok := New().Pop(nil); ok { + t.Errorf(`Pop(nil) on empty: got %v, want ""`, got) + } +} + +func TestContainsAny(t *testing.T) { + set := New(testValues[2:]...) + tests := []struct { + keys []string + want bool + }{ + {nil, false}, + {[]string{}, false}, + {testKeys(0), false}, + {testKeys(1), false}, + {testKeys(0, 1), false}, + {testKeys(7), true}, + {testKeys(8, 3, 4, 9), true}, + {testKeys(0, 7, 1, 0), true}, + } + t.Logf("Test set: %v", set) + for _, test := range tests { + got := set.ContainsAny(test.keys...) + if got != test.want { + t.Errorf("ContainsAny(%+v): got %v, want %v", test.keys, got, test.want) + } + } +} + +func TestContainsAll(t *testing.T) { + //set := New("a", "e", "i", "y") + set := New(testValues[2:]...) + tests := []struct { + keys []string + want bool + }{ + {nil, true}, + {[]string{}, true}, + {testKeys(2, 4, 6), true}, + {testKeys(1, 3, 5, 7), false}, + {testKeys(0), false}, + {testKeys(5, 5, 5), true}, + } + t.Logf("Test set: %v", set) + for _, test := range tests { + got := set.Contains(test.keys...) + if got != test.want { + t.Errorf("Contains(%+v): got %v, want %v", test.keys, got, test.want) + } + } +} + +func TestIsSubset(t *testing.T) { + var empty Set + key := testSet(0, 2, 6, 7, 9) + for _, test := range [][]string{ + {}, testKeys(2, 6), testKeys(0, 7, 9), + } { + probe := New(test...) + if !probe.IsSubset(key) { + t.Errorf("IsSubset %+v ⊂ %+v is false", probe, key) + } + if !empty.IsSubset(probe) { // ø is a subset of everything, including itself. + t.Errorf("IsSubset ø ⊂ %+v is false", probe) + } + } +} + +func TestNotSubset(t *testing.T) { + tests := []struct { + probe, key Set + }{ + {testSet(0), New()}, + {testSet(0), testSet(1)}, + {testSet(0, 1), testSet(1)}, + {testSet(0, 2, 1), testSet(0, 2, 3)}, + } + for _, test := range tests { + if test.probe.IsSubset(test.key) { + t.Errorf("IsSubset %+v ⊂ %+v is true", test.probe, test.key) + } + } +} + +func TestEquality(t *testing.T) { + nat := New(testValues[:]...) + odd := testSet(1, 3, 4, 5, 8) + tests := []struct { + left, right Set + eq bool + }{ + {nil, nil, true}, + {nat, nat, true}, // Equality with the same value + {testSet(0), testSet(0), true}, // Equality with Different values + {testSet(0), nil, false}, + {nat, odd, false}, + {nil, testSet(0), false}, + {testSet(0), testSet(1), false}, + + // Various set operations... + {nat.Intersect(odd), odd, true}, + {odd, nat.Intersect(odd), true}, + {odd.Intersect(nat), odd, true}, + {odd, odd.Intersect(nat), true}, + {nat.Intersect(nat), nat, true}, + {nat, nat.Intersect(nat), true}, + {nat.Union(odd), nat, true}, + {nat, nat.Union(odd), true}, + {odd.Diff(nat), odd, false}, + {odd, odd.Diff(nat), false}, + {odd.Diff(nat), nil, true}, + {nil, odd.Diff(nat), true}, + + {testSet(0, 1, 2).Diff(testSet(2, 5, 6)), testSet(1).Union(testSet(0)), true}, + } + for _, test := range tests { + if got := test.left.Equals(test.right); got != test.eq { + t.Errorf("%v.Equals(%v): got %v, want %v", test.left, test.right, got, test.eq) + } + } +} + +func TestUnion(t *testing.T) { + vkeys := testKeys(0, 4) + vowels := testSet(4, 0) + consonants := testSet(1, 2, 3, 5, 6, 7, 8, 9) + + if got := vowels.Union(nil).Elements(); !reflect.DeepEqual(got, vkeys) { + t.Errorf("Vowels ∪ ø: got %+v, want %+v", got, vkeys) + } + if got := New().Union(vowels).Elements(); !reflect.DeepEqual(got, vkeys) { + t.Errorf("ø ∪ Vowels: got %+v, want %+v", got, vkeys) + } + + if got, want := vowels.Union(consonants).Elements(), testValues[:]; !reflect.DeepEqual(got, want) { + t.Errorf("Vowels ∪ Consonants: got %+v, want %+v", got, want) + } +} + +func TestIntersect(t *testing.T) { + empty := New() + nat := New(testValues[:]...) + odd := testSet(1, 3, 5, 7, 9) + prime := testSet(2, 3, 5, 7) + + tests := []struct { + left, right Set + want []string + }{ + {empty, empty, nil}, + {empty, nat, nil}, + {nat, empty, nil}, + {nat, nat, testValues[:]}, + {nat, odd, testKeys(1, 3, 5, 7, 9)}, + {odd, nat, testKeys(1, 3, 5, 7, 9)}, + {odd, prime, testKeys(3, 5, 7)}, + {prime, nat, testKeys(2, 3, 5, 7)}, + } + for _, test := range tests { + got := test.left.Intersect(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v ∩ %v: got %+v, want %+v", test.left, test.right, got, test.want) + } else if want, ok := len(test.want) != 0, test.left.Intersects(test.right); ok != want { + t.Errorf("%+v.Intersects(%+v): got %v, want %v", test.left, test.right, ok, want) + } + } +} + +func TestDiff(t *testing.T) { + empty := New() + nat := New(testValues[:]...) + odd := testSet(1, 3, 5, 7, 9) + prime := testSet(2, 3, 5, 7) + + tests := []struct { + left, right Set + want []string + }{ + {empty, empty, nil}, + {empty, nat, nil}, + {nat, empty, testValues[:]}, + {nat, nat, nil}, + {nat, odd, testKeys(0, 2, 4, 6, 8)}, + {odd, nat, nil}, + {odd, prime, testKeys(1, 9)}, + {prime, nat, nil}, + } + for _, test := range tests { + got := test.left.Diff(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v \\ %v: got %+q, want %+q", test.left, test.right, got, test.want) + } + } +} + +func TestSymDiff(t *testing.T) { + a := testSet(0, 1, 2, 3, 4) + b := testSet(0, 4, 5, 6, 7) + c := testSet(3, 4, 8, 9) + empty := New() + + tests := []struct { + left, right Set + want []string + }{ + {empty, empty, nil}, + {empty, a, a.Elements()}, + {b, empty, b.Elements()}, + {a, a, nil}, + {a, b, testKeys(1, 2, 3, 5, 6, 7)}, + {b, a, testKeys(1, 2, 3, 5, 6, 7)}, + {a, c, testKeys(0, 1, 2, 8, 9)}, + {c, a, testKeys(0, 1, 2, 8, 9)}, + {c, b, testKeys(0, 3, 5, 6, 7, 8, 9)}, + } + for _, test := range tests { + got := test.left.SymDiff(test.right).Elements() + if !reflect.DeepEqual(got, test.want) { + t.Errorf("%v ∆ %v: got %+v, want %+v", test.left, test.right, got, test.want) + } + } +} + +func TestUpdate(t *testing.T) { + tests := []struct { + before, update Set + want []string + changed bool + }{ + {nil, nil, nil, false}, + {nil, testSet(0), testKeys(0), true}, + {testSet(1), nil, testKeys(1), false}, + {testSet(2, 3), testSet(4, 4, 3), testKeys(2, 3, 4), true}, + } + for _, test := range tests { + ok := test.before.Update(test.update) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Update %v: got %+v, want %+q", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Update %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestAdd(t *testing.T) { + tests := []struct { + before Set + update, want []string + changed bool + }{ + {nil, nil, nil, false}, + {nil, testKeys(0), testKeys(0), true}, + {testSet(1), nil, testKeys(1), false}, + {testSet(0, 1), testKeys(2, 2, 1), testKeys(0, 1, 2), true}, + } + for _, test := range tests { + ok := test.before.Add(test.update...) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Add %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Add %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestRemove(t *testing.T) { + tests := []struct { + before, update Set + want []string + changed bool + }{ + {nil, nil, nil, false}, + {nil, testSet(0), nil, false}, + {testSet(5), nil, testKeys(5), false}, + {testSet(3, 9), testSet(5, 1, 9), testKeys(3), true}, + {testSet(0, 1, 2), testSet(4, 6), testKeys(0, 1, 2), false}, + } + for _, test := range tests { + ok := test.before.Remove(test.update) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Remove %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Remove %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestDiscard(t *testing.T) { + tests := []struct { + before Set + update, want []string + changed bool + }{ + {nil, nil, nil, false}, + {nil, testKeys(0), nil, false}, + {testSet(1), nil, testKeys(1), false}, + {testSet(0, 1), testKeys(2, 2, 1), testKeys(0), true}, + {testSet(0, 1, 2), testKeys(3, 4), testKeys(0, 1, 2), false}, + } + for _, test := range tests { + ok := test.before.Discard(test.update...) + if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) { + t.Errorf("Discard %v: got %+v, want %+v", test.before, got, test.want) + } + if ok != test.changed { + t.Errorf("Discard %v reported change=%v, want %v", test.before, ok, test.changed) + } + } +} + +func TestMap(t *testing.T) { + in := New(testValues[:]...) + got := make([]string, len(testValues)) + out := in.Map(func(s string) string { + if p := keyPos(s); p < 0 { + t.Errorf("Unknown input key %v", s) + } else { + got[p] = s + } + return s + }) + if !reflect.DeepEqual(got, testValues[:]) { + t.Errorf("Incomplete mapping:\n got %+v\nwant %+v", got, testValues) + } + if !out.Equals(in) { + t.Errorf("Incorrect mapping:\n got %v\nwant %v", out, in) + } +} + +func TestEach(t *testing.T) { + in := New(testValues[:]...) + saw := make(map[string]int) + in.Each(func(name string) { + saw[name]++ + }) + for want := range in { + if saw[want] != 1 { + t.Errorf("Saw [%v] %d times, wanted 1", want, saw[want]) + } + } + for got, n := range saw { + if _, ok := in[got]; !ok { + t.Errorf("Saw [%v] %d times, wanted 0", got, n) + } + } +} + +func TestSelection(t *testing.T) { + in := New(testValues[:]...) + want := testSet(0, 2, 4, 6, 8) + if got := in.Select(func(s string) bool { + pos := keyPos(s) + return pos >= 0 && pos%2 == 0 + }); !got.Equals(want) { + t.Errorf("%v.Select(evens): got %v, want %v", in, got, want) + } + if got := New().Select(func(string) bool { return true }); !got.Empty() { + t.Errorf("%v.Select(true): got %v, want empty", New(), got) + } + if got := in.Select(func(string) bool { return false }); !got.Empty() { + t.Errorf("%v.Select(false): got %v, want empty", in, got) + } +} + +func TestPartition(t *testing.T) { + in := New(testValues[:]...) + tests := []struct { + in, left, right Set + f func(string) bool + desc string + }{ + {testSet(0, 1), testSet(0, 1), nil, + func(string) bool { return true }, + "all true", + }, + {testSet(0, 1), nil, testSet(0, 1), + func(string) bool { return false }, + "all false", + }, + {in, + testSet(0, 1, 2, 3, 4), + testSet(5, 6, 7, 8, 9), + func(s string) bool { return keyPos(s) < 5 }, + "pos(s) < 5", + }, + {in, + testSet(1, 3, 5, 7, 9), // odd + testSet(0, 2, 4, 6, 8), // even + func(s string) bool { return keyPos(s)%2 == 1 }, + "odd/even", + }, + } + for _, test := range tests { + gotLeft, gotRight := test.in.Partition(test.f) + if !gotLeft.Equals(test.left) { + t.Errorf("Partition %s left: got %v, want %v", test.desc, gotLeft, test.left) + } + if !gotRight.Equals(test.right) { + t.Errorf("Partition %s right: got %v, want %v", test.desc, gotRight, test.right) + } + t.Logf("Partition %v %s\n\t left: %v\n\tright: %v", test.in, test.desc, gotLeft, gotRight) + } +} + +func TestIndex(t *testing.T) { + tests := []struct { + needle string + keys []string + want int + }{ + {testValues[0], nil, -1}, + {testValues[1], []string{}, -1}, + {testValues[2], testKeys(0, 1), -1}, + {testValues[0], testKeys(0, 1), 0}, + {testValues[1], testKeys(0, 1), 1}, + {testValues[2], testKeys(0, 2, 1, 2), 1}, + {testValues[9], testKeys(0, 2, 1, 9, 6), 3}, + {testValues[4], testKeys(0, 2, 4, 9, 4), 2}, + } + for _, test := range tests { + got := Index(test.needle, test.keys) + if got != test.want { + t.Errorf("Index(%+v, %+v): got %d, want %d", test.needle, test.keys, got, test.want) + } + } +} + +type keyer []string + +func (k keyer) Keys() []string { + p := make([]string, len(k)) + copy(p, k) + return p +} + +type uniq int + +func TestFromValues(t *testing.T) { + tests := []struct { + input interface{} + want []string + }{ + {nil, nil}, + {map[float64]string{}, nil}, + {map[int]string{1: testValues[1], 2: testValues[2], 3: testValues[2]}, testKeys(1, 2)}, + {map[string]string{"foo": testValues[4], "baz": testValues[4]}, testKeys(4)}, + {map[int]uniq{1: uniq(2), 3: uniq(4), 5: uniq(6)}, nil}, + {map[*int]string{nil: testValues[0]}, testKeys(0)}, + } + for _, test := range tests { + got := FromValues(test.input) + want := New(test.want...) + if !got.Equals(want) { + t.Errorf("MapValues %v: got %v, want %v", test.input, got, want) + } + } +} + +func TestFromKeys(t *testing.T) { + tests := []struct { + input interface{} + want Set + }{ + {3.5, nil}, // unkeyable type + {map[uniq]uniq{1: 1}, nil}, // unkeyable type + {nil, nil}, // empty + {[]string{}, nil}, // empty + {map[string]float64{}, nil}, // empty + {testValues[0], testSet(0)}, + {testKeys(0, 1, 0, 0), testSet(0, 1)}, + {map[string]int{testValues[0]: 1, testValues[1]: 2}, testSet(0, 1)}, + {keyer(testValues[:3]), testSet(0, 1, 2)}, + {testSet(4, 7, 8), testSet(4, 7, 8)}, + {map[string]struct{}{testValues[2]: {}, testValues[7]: {}}, testSet(2, 7)}, + } + for _, test := range tests { + got := FromKeys(test.input) + if !got.Equals(test.want) { + t.Errorf("FromKeys %v: got %v, want %v", test.input, got, test.want) + } + } +} + +func TestContainsFunc(t *testing.T) { + tests := []struct { + input interface{} + needle string + want bool + }{ + {[]string(nil), testValues[0], false}, + {[]string{}, testValues[0], false}, + {testKeys(0), testValues[0], true}, + {testKeys(1), testValues[0], false}, + {testKeys(0, 1, 9, 2), testValues[0], true}, + + {map[string]int(nil), testValues[2], false}, + {map[string]int{}, testValues[2], false}, + {map[string]int{testValues[2]: 1}, testValues[2], true}, + {map[string]int{testValues[3]: 3}, testValues[2], false}, + {map[string]float32{testValues[2]: 1, testValues[4]: 2}, testValues[2], true}, + {map[string]float32{testValues[5]: 0, testValues[6]: 1, testValues[7]: 2, testValues[8]: 3}, testValues[2], false}, + + {Set(nil), testValues[3], false}, + {New(), testValues[3], false}, + {New(testValues[3]), testValues[3], true}, + {New(testValues[5]), testValues[3], false}, + {testSet(0, 1), testValues[3], false}, + {testSet(0, 3, 1), testValues[3], true}, + + {keyer(nil), testValues[9], false}, + {keyer{}, testValues[9], false}, + {keyer{testValues[9]}, testValues[9], true}, + {keyer{testValues[0]}, testValues[9], false}, + {keyer(testKeys(0, 6, 9)), testValues[9], true}, + {keyer(testKeys(0, 6, 7)), testValues[9], false}, + } + for _, test := range tests { + got := Contains(test.input, test.needle) + if got != test.want { + t.Errorf("Contains(%+v, %v): got %v, want %v", test.input, test.needle, got, test.want) + } + } +} + +func TestFromIndexed(t *testing.T) { + tests := []struct { + input []int + want Set + }{ + {nil, nil}, + {[]int{}, nil}, + {[]int{0}, testSet(0)}, + {[]int{1, 8, 2, 9}, testSet(1, 2, 8, 9)}, + {[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, New(testValues[:]...)}, + } + for _, test := range tests { + got := FromIndexed(len(test.input), func(i int) string { + return testValues[test.input[i]] + }) + if !got.Equals(test.want) { + t.Errorf("FromIndexed(%d, <...>): got %v, want %v", len(test.input), got, test.want) + } + } +} |