aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSasha Smundak <asmundak@google.com>2019-12-16 13:54:08 -0800
committerSasha Smundak <asmundak@google.com>2019-12-16 15:10:25 -0800
commit0bb095d895baddf8eb21c0347c091518b8aa0a18 (patch)
tree6989a259f6ea7965bbb55418c8ce004e2f3a92db
parent5e285379e96f481eeb0659e0ed3cbc3f92269a18 (diff)
parent7c6678a70f6812a0abfb9d71d21d74ee24af01f9 (diff)
downloadgo-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--LICENSE27
-rw-r--r--METADATA18
-rw-r--r--MODULE_LICENSE_BSD0
l---------NOTICE1
-rw-r--r--README.md25
-rw-r--r--bitbucket-pipelines.yml26
-rw-r--r--examples_test.go186
-rw-r--r--go.mod8
-rw-r--r--go.sum4
-rw-r--r--makeset/core.go.in454
-rw-r--r--makeset/core_test.go.in680
-rw-r--r--makeset/intset.toml8
-rw-r--r--makeset/makeset.go182
-rw-r--r--makeset/nodeset.toml28
-rw-r--r--makeset/static.go200
-rw-r--r--makeset/stringset.toml11
-rw-r--r--stringset.go444
-rw-r--r--stringset_test.go681
18 files changed, 2983 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b808dbf
--- /dev/null
+++ b/LICENSE
@@ -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
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -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"}
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..1c48f36
--- /dev/null
+++ b/go.mod
@@ -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
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5fdd127
--- /dev/null
+++ b/go.sum
@@ -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)
+ }
+ }
+}