pax_global_header00006660000000000000000000000064132301612260014506gustar00rootroot0000000000000052 comment=708d80f4a27458f99f8ca12bd0e638c6ee65627f set-1.0.0/000077500000000000000000000000001323016122600122775ustar00rootroot00000000000000set-1.0.0/LICENSE000066400000000000000000000024171323016122600133100ustar00rootroot00000000000000Copyright (c) 2015 Kevin Gillette. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. 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 OWNER 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. set-1.0.0/README.md000066400000000000000000000007711323016122600135630ustar00rootroot00000000000000# set [![GoDoc](https://godoc.org/github.com/xtgo/set?status.png)](https://godoc.org/github.com/xtgo/set) [![Coverage Status](https://coveralls.io/repos/xtgo/set/badge.svg?branch=master&service=github)](https://coveralls.io/github/xtgo/set?branch=master) Package set provides type-safe, polymorphic mathematical set operations that operate on any sort.Interface implementation. Documentation: http://godoc.org/github.com/xtgo/set Talk: https://go-talks.appspot.com/github.com/xtblog/gotalks/sets.slide set-1.0.0/apply.go000066400000000000000000000073771323016122600137710ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" // Pivots transforms set-relative sizes into data-absolute pivots. Pivots is // mostly only useful in conjunction with Apply. The sizes slice sizes may // be modified by the call. func Pivots(sizes ...int) []int { n := 0 for i, l := range sizes { n += l sizes[i] = n } return sizes } // Apply concurrently applies op to all the sets terminated by pivots. // pivots must contain one higher than the final index in each set, with the // final element of pivots being equal to data.Len(); this deviates from the // pivot semantics of other functions (which treat pivot as a delimiter) in // order to make initializing the pivots slice simpler. // // data.Swap and data.Less are assumed to be concurrent-safe. Only // associative operations should be used (Diff is not associative); see the // Apply (Diff) example for a workaround. The result of applying SymDiff // will contain elements that exist in an odd number of sets. // // The implementation runs op concurrently on pairs of neighbor sets // in-place; when any pair has been merged, the resulting set is re-paired // with one of its neighbor sets and the process repeats until only one set // remains. The process is adaptive (large sets will not prevent small pairs // from being processed), and strives for data-locality (only adjacent // neighbors are paired and data shifts toward the zero index). func Apply(op Op, data sort.Interface, pivots []int) (size int) { switch len(pivots) { case 0: return 0 case 1: return pivots[0] case 2: return op(data, pivots[0]) } spans := make([]span, 0, len(pivots)+1) // convert pivots into spans (index intervals that represent sets) i := 0 for _, j := range pivots { spans = append(spans, span{i, j}) i = j } n := len(spans) // original number of spans m := n / 2 // original number of span pairs (rounded down) // true if the span is being used inuse := make([]bool, n) ch := make(chan span, m) // reverse iterate over every other span, starting with the last; // concurrent algo (further below) will pick available pairs operate on for i := range spans[:m] { i = len(spans) - 1 - i*2 ch <- spans[i] } for s := range ch { if len(spans) == 1 { if s.i != 0 { panic("impossible final span") } // this was the last operation return s.j } // locate the span we received (match on start of span only) i := sort.Search(len(spans), func(i int) bool { return spans[i].i >= s.i }) // store the result (this may change field j but not field i) spans[i] = s // mark the span as available for use inuse[i] = false // check the immediate neighbors for availability (prefer left) j, k := i-1, i+1 switch { case j >= 0 && !inuse[j]: i, j = j, i case k < len(spans) && !inuse[k]: j = k default: // nothing to do right now. wait for something else to finish continue } s, t := spans[i], spans[j] go func(s, t span) { // sizes of the respective sets k, l := s.j-s.i, t.j-t.i // shift the right-hand span to be adjacent to the left slide(data, s.j, t.i, l) // prepare a view of the data (abs -> rel indices) b := boundspan{data, span{s.i, s.j + l}} // store result of op, adjusting for view (rel -> abs) s.j = s.i + op(b, k) // send the result back to the coordinating goroutine ch <- s }(s, t) // account for the spawn merging that will occur s.j += t.j - t.i k = j + 1 // shrink the lists to account for the merger spans = append(append(spans[:i], s), spans[k:]...) // (and the merged span is now in use as well) inuse = append(append(inuse[:i], true), inuse[k:]...) } panic("unreachable") } set-1.0.0/apply_test.go000066400000000000000000000041751323016122600150210ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set_test import ( "fmt" "sort" "github.com/xtgo/set" ) func ExampleApply() { sets := []sort.IntSlice{ {1, 3, 5, 7, 9}, // odds from 1 {3, 5, 7, 9, 11}, // odds from 3 {5, 10, 15, 20}, // 5-multiples {2, 3, 5, 7, 11}, // primes } pivots := make([]int, len(sets)) var orig, data sort.IntSlice // concatenate the sets together for use with the set package for i, set := range sets { pivots[i] = len(set) orig = append(orig, set...) } // transform set sizes into pivots pivots = set.Pivots(pivots...) tasks := []struct { name string op set.Op }{ {"union", set.Union}, {"inter", set.Inter}, {"sdiff", set.SymDiff}, } for _, task := range tasks { // make a copy of the original data (Apply rearranges the input) data = append(data[:0], orig...) size := set.Apply(task.op, data, pivots) data = data[:size] fmt.Printf("%s: %v\n", task.name, data) } // Output: // union: [1 2 3 5 7 9 10 11 15 20] // inter: [5] // sdiff: [1 2 3 7 10 15 20] } func ExampleApply_diff() { // a - b - c - d cannot be used with Apply (Diff is non-associative) // a - (b + c + d) equivalent, using Apply (Union is associative) sets := []sort.IntSlice{ {0, 2, 4, 6, 8, 10}, // positive evens {0, 1, 2, 3, 5, 8}, // set of fibonacci numbers {5, 10, 15}, // positive 5-multiples {2, 3, 5, 7, 11, 13}, // primes } var data sort.IntSlice // for use with (b + c + d) exprsets := sets[1:] pivots := make([]int, len(exprsets)) // concatenate the sets together for use with the set package for i, set := range exprsets { pivots[i] = len(set) data = append(data, set...) } // transform set sizes into pivots pivots = set.Pivots(pivots...) // calculate x = (b + c + d) size := set.Apply(set.Union, data, pivots) // concatenate the result to the end of a data = append(sets[0], data[:size]...) // calculate a - x size = set.Diff(data, len(sets[0])) data = data[:size] fmt.Println("diff:", data) // Output: // diff: [4 6] } set-1.0.0/bench_test.go000066400000000000000000000065701323016122600147540ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set_test import ( "sort" "testing" "github.com/xtgo/set" "github.com/xtgo/set/internal/sliceset" td "github.com/xtgo/set/internal/testdata" ) func BenchmarkUnion64K_revcat(b *testing.B) { benchMut(b, "Union", td.RevCat(2, td.Large)) } func BenchmarkUnion32(b *testing.B) { benchMut(b, "Union", td.Overlap(2, td.Small)) } func BenchmarkUnion64K(b *testing.B) { benchMut(b, "Union", td.Overlap(2, td.Large)) } func BenchmarkUnion_alt32(b *testing.B) { benchMut(b, "Union", td.Alternate(2, td.Small)) } func BenchmarkUnion_alt64K(b *testing.B) { benchMut(b, "Union", td.Alternate(2, td.Large)) } func BenchmarkInter32(b *testing.B) { benchMut(b, "Inter", td.Overlap(2, td.Small)) } func BenchmarkInter64K(b *testing.B) { benchMut(b, "Inter", td.Overlap(2, td.Large)) } func BenchmarkInter_alt32(b *testing.B) { benchMut(b, "Inter", td.Alternate(2, td.Small)) } func BenchmarkInter_alt64K(b *testing.B) { benchMut(b, "Inter", td.Alternate(2, td.Large)) } func BenchmarkDiff32(b *testing.B) { benchMut(b, "Diff", td.Overlap(2, td.Small)) } func BenchmarkDiff64K(b *testing.B) { benchMut(b, "Diff", td.Overlap(2, td.Large)) } func BenchmarkDiff_alt32(b *testing.B) { benchMut(b, "Diff", td.Alternate(2, td.Small)) } func BenchmarkDiff_alt64K(b *testing.B) { benchMut(b, "Diff", td.Alternate(2, td.Large)) } func BenchmarkSymDiff32(b *testing.B) { benchMut(b, "SymDiff", td.Overlap(2, td.Small)) } func BenchmarkSymDiff64K(b *testing.B) { benchMut(b, "SymDiff", td.Overlap(2, td.Large)) } func BenchmarkSymDiff_alt32(b *testing.B) { benchMut(b, "SymDiff", td.Alternate(2, td.Small)) } func BenchmarkSymDiff_alt64K(b *testing.B) { benchMut(b, "SymDiff", td.Alternate(2, td.Large)) } func BenchmarkIsInter32(b *testing.B) { benchBool(b, "IsInter", td.Overlap(2, td.Small)) } func BenchmarkIsInter64K(b *testing.B) { benchBool(b, "IsInter", td.Overlap(2, td.Large)) } func BenchmarkIsInter_alt32(b *testing.B) { benchBool(b, "IsInter", td.Alternate(2, td.Small)) } func BenchmarkIsInter_alt64K(b *testing.B) { benchBool(b, "IsInter", td.Alternate(2, td.Large)) } func BenchmarkApply256_64K(b *testing.B) { benchApply(b, td.Rand(256, td.Large)) } func benchMut(b *testing.B, name string, sets [][]int) { var op mutOp td.ConvMethod(&op, sliceset.Set(nil), name) bench(b, func(a, b sliceset.Set) { op(a, b) }, sets) } func benchBool(b *testing.B, name string, sets [][]int) { var op boolOp td.ConvMethod(&op, sliceset.Set(nil), name) bench(b, func(a, b sliceset.Set) { op(a, b) }, sets) } func bench(b *testing.B, op func(a, b sliceset.Set), sets [][]int) { s, t := sets[0], sets[1] data := make([]int, 0, len(s)+len(t)) b.ResetTimer() for i := 0; i < b.N; i++ { data = append(data[:0], s...) op(data, t) } } func pivots(sets [][]int) []int { lengths := make([]int, len(sets)) for i, set := range sets { lengths[i] = len(set) } return set.Pivots(lengths...) } func benchApply(b *testing.B, sets [][]int) { pivots := pivots(sets) n := len(sets) - 1 data := make(sort.IntSlice, 0, pivots[n]) b.ResetTimer() for i := 0; i < b.N; i++ { data = data[:0] for _, set := range sets { data = append(data, set...) } set.Apply(set.Inter, data, pivots) } } set-1.0.0/doc.go000066400000000000000000000026731323016122600134030ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package set implements type-safe, non-allocating algorithms that operate // on ordered sets. // // Most functions take a data parameter of type sort.Interface and a pivot // parameter of type int; data represents two sets covering the ranges // [0:pivot] and [pivot:Len], each of which is expected to be sorted and // free of duplicates. sort.Sort may be used for sorting, and Uniq may be // used to filter away duplicates. // // All mutating functions swap elements as necessary from the two input sets // to form a single output set, returning its size: the output set will be // in the range [0:size], and will be in sorted order and free of // duplicates. Elements which were moved into the range [size:Len] will have // undefined order and may contain duplicates. // // All pivots must be in the range [0:Len]. A panic may occur when invalid // pivots are passed into any of the functions. // // Convenience functions exist for slices of int, float64, and string // element types, and also serve as examples for implementing utility // functions for other types. // // Elements will be considered equal if `!Less(i,j) && !Less(j,i)`. An // implication of this is that NaN values are equal to each other. package set // BUG(extemporalgenome): All ops should use binary search when runs are detected set-1.0.0/example_test.go000066400000000000000000000060141323016122600153210ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set_test import ( "fmt" "sort" "github.com/xtgo/set" ) func Example() { s := set.Strings([]string{"alpha", "gamma", "alpha"}) fmt.Println("set:", s) s = set.StringsDo(set.Union, s, "beta") fmt.Println("set + beta:", s) fmt.Println(s, "contains any [alpha delta]:", set.StringsChk(set.IsInter, s, "alpha", "delta")) fmt.Println(s, "contains all [alpha delta]:", set.StringsChk(set.IsSuper, s, "alpha", "delta")) // Output: // set: [alpha gamma] // set + beta: [alpha beta gamma] // [alpha beta gamma] contains any [alpha delta]: true // [alpha beta gamma] contains all [alpha delta]: false } func ExampleUniq() { data := sort.IntSlice{5, 7, 3, 3, 5} sort.Sort(data) // sort the data first n := set.Uniq(data) // Uniq returns the size of the set data = data[:n] // trim the duplicate elements fmt.Println(data) // Output: [3 5 7] } func ExampleInter() { data := sort.IntSlice{3, 5, 7} // create a set (it must be sorted) pivot := len(data) // store the length of our first set data = append(data, 1, 3, 5) // append a second set (which also must be sorted) size := set.Inter(data, pivot) // perform set intersection // trim data to contain just the result set data = data[:size] fmt.Println("inter:", data) // Output: // inter: [3 5] } func ExampleIsSuper() { data := sort.StringSlice{"b", "c", "d"} // create a set (it must be sorted) pivot := len(data) // store the length of our first set data = append(data, "c", "d") // append a second set (which also must be sorted) contained := set.IsSuper(data, pivot) // check the first set is a superset of the second fmt.Printf("%v superset of %v = %t\n", data[:pivot], data[pivot:], contained) data = data[:pivot] // trim off the second set data = append(data, "s") // append a new singleton set to compare against contained = set.IsSuper(data, pivot) // check for membership fmt.Printf("%v superset of %v = %t\n", data[:pivot], data[pivot:], contained) // Output: // [b c d] superset of [c d] = true // [b c d] superset of [s] = false } func ExampleIsInter() { data := sort.StringSlice{"b", "c", "d"} // create a set (it must be sorted) pivot := len(data) // store the length of our first set data = append(data, "d", "z") // append a second set (which also must be sorted) contained := set.IsInter(data, pivot) // check the first set is a superset of the second fmt.Printf("%v intersects %v = %t\n", data[:pivot], data[pivot:], contained) data = data[:pivot] // trim off the second set data = append(data, "s") // append a new singleton set to compare against contained = set.IsInter(data, pivot) // check for membership fmt.Printf("%v intersects %v = %t\n", data[:pivot], data[pivot:], contained) // Output: // [b c d] intersects [d z] = true // [b c d] intersects [s] = false } set-1.0.0/helpers.go000066400000000000000000000042041323016122600142700ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" // Ints sorts and deduplicates a slice of ints in place, returning the // resulting set. func Ints(data []int) []int { sort.Ints(data) n := Uniq(sort.IntSlice(data)) return data[:n] } // Float64s sorts and deduplicates a slice of float64s in place, returning // the resulting set. func Float64s(data []float64) []float64 { sort.Float64s(data) n := Uniq(sort.Float64Slice(data)) return data[:n] } // Strings sorts and deduplicates a slice of strings in place, returning // the resulting set. func Strings(data []string) []string { sort.Strings(data) n := Uniq(sort.StringSlice(data)) return data[:n] } // IntsDo applies op to the int sets, s and t, returning the result. // s and t must already be individually sorted and free of duplicates. func IntsDo(op Op, s []int, t ...int) []int { data := sort.IntSlice(append(s, t...)) n := op(data, len(s)) return data[:n] } // Float64sDo applies op to the float64 sets, s and t, returning the result. // s and t must already be individually sorted and free of duplicates. func Float64sDo(op Op, s []float64, t ...float64) []float64 { data := sort.Float64Slice(append(s, t...)) n := op(data, len(s)) return data[:n] } // StringsDo applies op to the string sets, s and t, returning the result. // s and t must already be individually sorted and free of duplicates. func StringsDo(op Op, s []string, t ...string) []string { data := sort.StringSlice(append(s, t...)) n := op(data, len(s)) return data[:n] } // IntsChk compares s and t according to cmp. func IntsChk(cmp Cmp, s []int, t ...int) bool { data := sort.IntSlice(append(s, t...)) return cmp(data, len(s)) } // Float64sChk compares s and t according to cmp. func Float64sChk(cmp Cmp, s []float64, t ...float64) bool { data := sort.Float64Slice(append(s, t...)) return cmp(data, len(s)) } // StringsChk compares s and t according to cmp. func StringsChk(cmp Cmp, s []string, t ...string) bool { data := sort.StringSlice(append(s, t...)) return cmp(data, len(s)) } set-1.0.0/internal/000077500000000000000000000000001323016122600141135ustar00rootroot00000000000000set-1.0.0/internal/mapset/000077500000000000000000000000001323016122600154045ustar00rootroot00000000000000set-1.0.0/internal/mapset/bench_test.go000066400000000000000000000054321323016122600200550ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package mapset_test import ( "testing" "github.com/xtgo/set/internal/mapset" td "github.com/xtgo/set/internal/testdata" ) func BenchmarkUnion64K_revcat(b *testing.B) { benchMut(b, "Union", td.RevCat(2, td.Large)) } func BenchmarkUnion32(b *testing.B) { benchMut(b, "Union", td.Overlap(2, td.Small)) } func BenchmarkUnion64K(b *testing.B) { benchMut(b, "Union", td.Overlap(2, td.Large)) } func BenchmarkUnion_alt32(b *testing.B) { benchMut(b, "Union", td.Alternate(2, td.Small)) } func BenchmarkUnion_alt64K(b *testing.B) { benchMut(b, "Union", td.Alternate(2, td.Large)) } func BenchmarkInter32(b *testing.B) { benchMut(b, "Inter", td.Overlap(2, td.Small)) } func BenchmarkInter64K(b *testing.B) { benchMut(b, "Inter", td.Overlap(2, td.Large)) } func BenchmarkInter_alt32(b *testing.B) { benchMut(b, "Inter", td.Alternate(2, td.Small)) } func BenchmarkInter_alt64K(b *testing.B) { benchMut(b, "Inter", td.Alternate(2, td.Large)) } func BenchmarkDiff32(b *testing.B) { benchMut(b, "Diff", td.Overlap(2, td.Small)) } func BenchmarkDiff64K(b *testing.B) { benchMut(b, "Diff", td.Overlap(2, td.Large)) } func BenchmarkDiff_alt32(b *testing.B) { benchMut(b, "Diff", td.Alternate(2, td.Small)) } func BenchmarkDiff_alt64K(b *testing.B) { benchMut(b, "Diff", td.Alternate(2, td.Large)) } func BenchmarkSymDiff32(b *testing.B) { benchMut(b, "SymDiff", td.Overlap(2, td.Small)) } func BenchmarkSymDiff64K(b *testing.B) { benchMut(b, "SymDiff", td.Overlap(2, td.Large)) } func BenchmarkSymDiff_alt32(b *testing.B) { benchMut(b, "SymDiff", td.Alternate(2, td.Small)) } func BenchmarkSymDiff_alt64K(b *testing.B) { benchMut(b, "SymDiff", td.Alternate(2, td.Large)) } func BenchmarkIsInter32(b *testing.B) { benchBool(b, "IsInter", td.Overlap(2, td.Small)) } func BenchmarkIsInter64K(b *testing.B) { benchBool(b, "IsInter", td.Overlap(2, td.Large)) } func BenchmarkIsInter_alt32(b *testing.B) { benchBool(b, "IsInter", td.Alternate(2, td.Small)) } func BenchmarkIsInter_alt64K(b *testing.B) { benchBool(b, "IsInter", td.Alternate(2, td.Large)) } func benchMut(b *testing.B, name string, sets [][]int) { var op mutOp td.ConvMethod(&op, mapset.Set(nil), name) bench(b, func(a, b mapset.Set) { op(a, b) }, sets) } func benchBool(b *testing.B, name string, sets [][]int) { var op boolOp td.ConvMethod(&op, mapset.Set(nil), name) bench(b, func(a, b mapset.Set) { op(a, b) }, sets) } func bench(b *testing.B, op func(a, b mapset.Set), sets [][]int) { s, t := mapset.New(sets[0]), mapset.New(sets[1]) b.ResetTimer() for i := 0; i < b.N; i++ { x, y := s.Copy(), t.Copy() op(x, y) } } set-1.0.0/internal/mapset/mapset.go000066400000000000000000000030461323016122600172270ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package mapset provides a reasonable map-based set implementation for // use in comparative benchmarks, and to check arbitrary fuzz outputs. // mapset is not intended for reuse. package mapset import "sort" func New(s []int) Set { t := make(Set, len(s)) for _, v := range s { t[v] = struct{}{} } return t } type Set map[int]struct{} func (s Set) Union(t Set) Set { for v := range t { s[v] = struct{}{} } return s } func (s Set) Inter(t Set) Set { for v := range s { _, ok := t[v] if !ok { delete(s, v) } } return s } func (s Set) Diff(t Set) Set { for v := range t { delete(s, v) } return s } func (s Set) SymDiff(t Set) Set { for v := range t { _, ok := s[v] if ok { delete(s, v) } else { s[v] = struct{}{} } } return s } func (s Set) IsSub(t Set) bool { if len(s) > len(t) { return false } for k := range s { _, ok := t[k] if !ok { return false } } return true } func (s Set) IsSuper(t Set) bool { return t.IsSub(s) } func (s Set) IsInter(t Set) bool { for k := range s { _, ok := t[k] if ok { return true } } return false } func (s Set) IsEqual(t Set) bool { if len(s) != len(t) { return false } return s.IsSub(t) } func (s Set) Elems() []int { t := make([]int, 0, len(s)) for v := range s { t = append(t, v) } sort.Ints(t) return t } func (s Set) Copy() Set { t := make(Set, len(s)) t.Union(s) return t } set-1.0.0/internal/mapset/mapset_test.go000066400000000000000000000027521323016122600202710ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package mapset_test import ( "testing" "github.com/xtgo/set/internal/mapset" "github.com/xtgo/set/internal/testdata" ) func TestUnion(t *testing.T) { testMut(t, "Union") } func TestInter(t *testing.T) { testMut(t, "Inter") } func TestDiff(t *testing.T) { testMut(t, "Diff") } func TestSymDiff(t *testing.T) { testMut(t, "SymDiff") } func TestIsSub(t *testing.T) { testBool(t, "IsSub") } func TestIsSuper(t *testing.T) { testBool(t, "IsSuper") } func TestIsInter(t *testing.T) { testBool(t, "IsInter") } func TestIsEqual(t *testing.T) { testBool(t, "IsEqual") } const format = "%s(%v, %v) = %v, want %v" type ( mutOp func(a, b mapset.Set) mapset.Set boolOp func(a, b mapset.Set) bool ) func testMut(t *testing.T, name string) { var op mutOp testdata.ConvMethod(&op, mapset.Set(nil), name) for _, tt := range testdata.BinTests { a, b := mapset.New(tt.A), mapset.New(tt.B) c := op(a, b).Elems() want := tt.SelSlice(name) if !testdata.IsEqual(c, want) { t.Errorf(format, name, tt.A, tt.B, c, want) } } } func testBool(t *testing.T, name string) { var op boolOp testdata.ConvMethod(&op, mapset.Set(nil), name) for _, tt := range testdata.BinTests { a, b := mapset.New(tt.A), mapset.New(tt.B) ok := op(a, b) want := tt.SelBool(name) if ok != want { t.Errorf(format, name, tt.A, tt.B, ok, want) } } } set-1.0.0/internal/sliceset/000077500000000000000000000000001323016122600157265ustar00rootroot00000000000000set-1.0.0/internal/sliceset/sliceset.go000066400000000000000000000030031323016122600200640ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package sliceset provides a convenient []int set wrapper to aid in // testing and benchmarks, and to serve as an example for those in need of // a (concrete) abstraction for simplifying code. It is not intended for // direct reuse. package sliceset import ( "sort" "github.com/xtgo/set" ) type Set []int func (s Set) Len() int { return len(s) } func (s Set) Less(i, j int) bool { return s[i] < s[j] } func (s Set) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s Set) Copy() Set { return append(Set(nil), s...) } func (s Set) Union(t Set) Set { return s.Do(set.Union, t) } func (s Set) Inter(t Set) Set { return s.Do(set.Inter, t) } func (s Set) Diff(t Set) Set { return s.Do(set.Diff, t) } func (s Set) SymDiff(t Set) Set { return s.Do(set.SymDiff, t) } func (s Set) IsSub(t Set) bool { return s.DoBool(set.IsSub, t) } func (s Set) IsSuper(t Set) bool { return s.DoBool(set.IsSuper, t) } func (s Set) IsInter(t Set) bool { return s.DoBool(set.IsInter, t) } func (s Set) IsEqual(t Set) bool { return s.DoBool(set.IsEqual, t) } func (s Set) Uniq() Set { n := set.Uniq(s) return s[:n] } func (s Set) Do(op set.Op, t Set) Set { data := append(s, t...) n := op(data, len(s)) return data[:n] } type BoolOp func(sort.Interface, int) bool func (s Set) DoBool(op BoolOp, t Set) bool { data := append(s, t...) return op(data, len(s)) } set-1.0.0/internal/testdata/000077500000000000000000000000001323016122600157245ustar00rootroot00000000000000set-1.0.0/internal/testdata/data.go000066400000000000000000000064701323016122600171730ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testdata import "reflect" type BinTest struct { A, B []int Inter []int Union []int Diff []int RevDiff []int SymDiff []int IsSub bool IsSuper bool IsInter bool IsEqual bool } func (t BinTest) sel(name string) interface{} { return reflect.ValueOf(t).FieldByName(name).Interface() } func (t BinTest) SelSlice(name string) []int { return t.sel(name).([]int) } func (t BinTest) SelBool(name string) bool { return t.sel(name).(bool) } var BinTests = []BinTest{ { // empty sets A: nil, B: nil, Inter: nil, Union: nil, Diff: nil, RevDiff: nil, SymDiff: nil, IsSub: true, IsSuper: true, IsInter: false, IsEqual: true, }, { // identical sets A: []int{1, 2, 3}, B: []int{1, 2, 3}, Inter: []int{1, 2, 3}, Union: []int{1, 2, 3}, Diff: nil, RevDiff: nil, SymDiff: nil, IsSub: true, IsSuper: true, IsInter: true, IsEqual: true, }, { // non-disjoint sets A: []int{1, 2, 3}, B: []int{2, 3, 4}, Inter: []int{2, 3}, Union: []int{1, 2, 3, 4}, Diff: []int{1}, RevDiff: []int{4}, SymDiff: []int{1, 4}, IsSub: false, IsSuper: false, IsInter: true, IsEqual: false, }, { // inverse non-disjoint sets A: []int{2, 3, 4}, B: []int{1, 2, 3}, Inter: []int{2, 3}, Union: []int{1, 2, 3, 4}, Diff: []int{4}, RevDiff: []int{1}, SymDiff: []int{1, 4}, IsSub: false, IsSuper: false, IsInter: true, IsEqual: false, }, { // disjoint sets A: []int{1, 2, 3}, B: []int{4, 5, 6}, Inter: nil, Union: []int{1, 2, 3, 4, 5, 6}, Diff: []int{1, 2, 3}, RevDiff: []int{4, 5, 6}, SymDiff: []int{1, 2, 3, 4, 5, 6}, IsSub: false, IsSuper: false, IsInter: false, IsEqual: false, }, { // inverse disjoint sets A: []int{4, 5, 6}, B: []int{1, 2, 3}, Inter: nil, Union: []int{1, 2, 3, 4, 5, 6}, Diff: []int{4, 5, 6}, RevDiff: []int{1, 2, 3}, SymDiff: []int{1, 2, 3, 4, 5, 6}, IsSub: false, IsSuper: false, IsInter: false, IsEqual: false, }, { // alternating disjoint sets A: []int{1, 3, 5}, B: []int{2, 4, 6}, Inter: nil, Union: []int{1, 2, 3, 4, 5, 6}, Diff: []int{1, 3, 5}, RevDiff: []int{2, 4, 6}, SymDiff: []int{1, 2, 3, 4, 5, 6}, IsSub: false, IsSuper: false, IsInter: false, IsEqual: false, }, { // inverse alternating disjoint sets A: []int{2, 4, 6}, B: []int{1, 3, 5}, Inter: nil, Union: []int{1, 2, 3, 4, 5, 6}, Diff: []int{2, 4, 6}, RevDiff: []int{1, 3, 5}, SymDiff: []int{1, 2, 3, 4, 5, 6}, IsSub: false, IsSuper: false, IsInter: false, IsEqual: false, }, { // subset A: []int{2}, B: []int{1, 2, 3}, Inter: []int{2}, Union: []int{1, 2, 3}, Diff: nil, RevDiff: []int{1, 3}, SymDiff: []int{1, 3}, IsSub: true, IsSuper: false, IsInter: true, IsEqual: false, }, { // superset A: []int{1, 2, 3}, B: []int{2}, Inter: []int{2}, Union: []int{1, 2, 3}, Diff: []int{1, 3}, RevDiff: nil, SymDiff: []int{1, 3}, IsSub: false, IsSuper: true, IsInter: true, IsEqual: false, }, } set-1.0.0/internal/testdata/funcs.go000066400000000000000000000020131323016122600173650ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testdata import "reflect" // IsEqual is a simple int slice equality implementation. func IsEqual(a, b []int) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } // ConvField converts and stores the value of src.field into dst. dst must // be a pointer. func ConvField(dst interface{}, src interface{}, field string) { v1 := reflect.ValueOf(dst).Elem() v2 := reflect.ValueOf(src) v2 = v2.FieldByName(field) v2 = v2.Convert(v1.Type()) v1.Set(v2) } // ConvMethod converts and stores the method expression of type(src).method // into dst. dst must be a pointer to function type. func ConvMethod(dst interface{}, src interface{}, method string) { v1 := reflect.ValueOf(dst).Elem() t := reflect.TypeOf(src) op, _ := t.MethodByName(method) v2 := op.Func.Convert(v1.Type()) v1.Set(v2) } set-1.0.0/internal/testdata/seq.go000066400000000000000000000026301323016122600170440ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testdata import "math/rand" const ( Small = 32 Large = 64 * 1024 ) func Seq(start, stop, skip int) []int { n := (stop - start) / skip s := make([]int, n) for i := range s { s[i] = start + (i * skip) } return s } func Interleave(n, k int) [][]int { l := n * k sets := make([][]int, n) for i := range sets { sets[i] = Seq(i, i+l, n) } return sets } func Concat(n, k, gap int) [][]int { l := k + gap sets := make([][]int, n) for i := range sets { start := i * l sets[i] = Seq(start, start+k, 1) } return sets } func Reverse(sets [][]int) [][]int { n := len(sets) for i := range sets[:n/2] { j := n - i - 1 sets[i], sets[j] = sets[j], sets[i] } return sets } func RevCat(n int, size int) [][]int { // union, inter: requires most Swap calls, fewest Less calls return Reverse(Concat(n, size, 0)) } func Alternate(n int, size int) [][]int { // union, inter: requires ~most Swap calls, most Less calls return Interleave(n, size) } func Overlap(n int, size int) [][]int { return Concat(n, size, -size/2) } func Rand(n int, size int) [][]int { rand.Seed(0) sets := make([][]int, n) for i := range sets { start, l := rand.Intn(size), rand.Intn(size)+1 stop := start + l sets[i] = Seq(start, stop, 1) } return sets } set-1.0.0/internal/testdata/seq_test.go000066400000000000000000000003251323016122600201020ustar00rootroot00000000000000package testdata import "fmt" func ExampleAlternate() { fmt.Println(Alternate(2, 4)) // Output: [[0 2 4 6] [1 3 5 7]] } func ExampleOverlap() { fmt.Println(Overlap(2, 4)) // Output: [[0 1 2 3] [2 3 4 5]] } set-1.0.0/internal/testdata/uniq.go000066400000000000000000000007361323016122600172350ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testdata type UniqTest struct { In, Out []int } var UniqTests = []UniqTest{ { In: nil, Out: nil, }, { In: []int{0, 1, 2, 3, 4, 5}, Out: []int{0, 1, 2, 3, 4, 5}, }, { In: []int{0, 0, 1, 2, 3, 3}, Out: []int{0, 1, 2, 3}, }, { In: []int{0, 1, 1, 1, 1, 2}, Out: []int{0, 1, 2}, }, } set-1.0.0/mutators.go000066400000000000000000000052151323016122600145070ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" // The Op type can be used to represent any of the mutating functions, such // as Inter. type Op func(data sort.Interface, pivot int) (size int) // Uniq swaps away duplicate elements in data, returning the size of the // unique set. data is expected to be pre-sorted, and the resulting set in // the range [0:size] will remain in sorted order. Uniq, following a // sort.Sort call, can be used to prepare arbitrary inputs for use as sets. func Uniq(data sort.Interface) (size int) { p, l := 0, data.Len() if l <= 1 { return l } for i := 1; i < l; i++ { if !data.Less(p, i) { continue } p++ if p < i { data.Swap(p, i) } } return p + 1 } // Inter performs an in-place intersection on the two sets [0:pivot] and // [pivot:Len]; the resulting set will occupy [0:size]. Inter is both // associative and commutative. func Inter(data sort.Interface, pivot int) (size int) { k, l := pivot, data.Len() p, i, j := 0, 0, k for i < k && j < l { switch { case data.Less(i, j): i++ case data.Less(j, i): j++ case p < i: data.Swap(p, i) fallthrough default: p, i, j = p+1, i+1, j+1 } } return p } // Union performs an in-place union on the two sets [0:pivot] and // [pivot:Len]; the resulting set will occupy [0:size]. Union is both // associative and commutative. func Union(data sort.Interface, pivot int) (size int) { // BUG(extemporalgenome): Union currently uses a multi-pass implementation sort.Sort(data) return Uniq(data) } // Diff performs an in-place difference on the two sets [0:pivot] and // [pivot:Len]; the resulting set will occupy [0:size]. Diff is neither // associative nor commutative. func Diff(data sort.Interface, pivot int) (size int) { k, l := pivot, data.Len() p, i, j := 0, 0, k for i < k && j < l { switch { case data.Less(i, j): if p < i { data.Swap(p, i) } p, i = p+1, i+1 case data.Less(j, i): j++ default: i, j = i+1, j+1 } } return xcopy(data, p, i, k, k) } // SymDiff performs an in-place symmetric difference on the two sets // [0:pivot] and [pivot:Len]; the resulting set will occupy [0:size]. // SymDiff is both associative and commutative. func SymDiff(data sort.Interface, pivot int) (size int) { // BUG(extemporalgenome): SymDiff currently uses a multi-pass implementation i := Inter(data, pivot) l := data.Len() b := boundspan{data, span{i, l}} sort.Sort(b) size = Uniq(b) slide(data, 0, i, size) l = i + size sort.Sort(boundspan{data, span{size, l}}) return Diff(data, size) } set-1.0.0/primitives.go000066400000000000000000000010211323016122600150130ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" func xcopy(data sort.Interface, i, j, k, l int) int { for i < k && j < l { data.Swap(i, j) i, j = i+1, j+1 } return i } func slide(data sort.Interface, i, j, n int) { xcopy(data, i, j, i+n, j+n) } /* func find(data sort.Interface, x, i, j int) int { return sort.Search(j-i, func(y int) bool { return !data.Less(x, i+y) }) } */ set-1.0.0/readonly.go000066400000000000000000000034741323016122600144530ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" // The Cmp type can be used to represent any of the comparison functions, // such as IsInter. type Cmp func(data sort.Interface, pivot int) bool // IsSub returns true only if all elements in the range [0:pivot] are // also present in the range [pivot:Len]. func IsSub(data sort.Interface, pivot int) bool { i, j, k, l := 0, pivot, pivot, data.Len() for i < k && j < l { switch { case data.Less(i, j): return false case data.Less(j, i): j++ default: i, j = i+1, j+1 } } return i == k } // IsSuper returns true only if all elements in the range [pivot:Len] are // also present in the range [0:pivot]. IsSuper is especially useful for // full membership testing. func IsSuper(data sort.Interface, pivot int) bool { i, j, k, l := 0, pivot, pivot, data.Len() for i < k && j < l { switch { case data.Less(i, j): i++ case data.Less(j, i): return false default: i, j = i+1, j+1 } } return j == l } // IsInter returns true if any element in the range [0:pivot] is also // present in the range [pivot:Len]. IsInter is especially useful for // partial membership testing. func IsInter(data sort.Interface, pivot int) bool { i, j, k, l := 0, pivot, pivot, data.Len() for i < k && j < l { switch { case data.Less(i, j): i++ case data.Less(j, i): j++ default: return true } } return false } // IsEqual returns true if the sets [0:pivot] and [pivot:Len] are equal. func IsEqual(data sort.Interface, pivot int) bool { k, l := pivot, data.Len() if k*2 != l { return false } for i := 0; i < k; i++ { p, q := k-i-1, l-i-1 if data.Less(p, q) || data.Less(q, p) { return false } } return true } set-1.0.0/set_test.go000066400000000000000000000032321323016122600144600ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set_test import ( "testing" "github.com/xtgo/set/internal/sliceset" "github.com/xtgo/set/internal/testdata" ) func TestUniq(t *testing.T) { for _, tt := range testdata.UniqTests { s := sliceset.Set(tt.In).Copy() s = s.Uniq() if !testdata.IsEqual(s, tt.Out) { t.Errorf("Uniq(%v) = %v, want %v", tt.In, s, tt.Out) } } } func TestUnion(t *testing.T) { testMut(t, "Union") } func TestInter(t *testing.T) { testMut(t, "Inter") } func TestDiff(t *testing.T) { testMut(t, "Diff") } func TestSymDiff(t *testing.T) { testMut(t, "SymDiff") } func TestIsSub(t *testing.T) { testBool(t, "IsSub") } func TestIsSuper(t *testing.T) { testBool(t, "IsSuper") } func TestIsInter(t *testing.T) { testBool(t, "IsInter") } func TestIsEqual(t *testing.T) { testBool(t, "IsEqual") } const format = "%s(%v, %v) = %v, want %v" type ( mutOp func(a, b sliceset.Set) sliceset.Set boolOp func(a, b sliceset.Set) bool ) func testMut(t *testing.T, name string) { var op mutOp testdata.ConvMethod(&op, sliceset.Set(nil), name) for _, tt := range testdata.BinTests { a := sliceset.Set(tt.A).Copy() c := op(a, tt.B) want := tt.SelSlice(name) if !testdata.IsEqual(c, want) { t.Errorf(format, name, tt.A, tt.B, c, want) } } } func testBool(t *testing.T, name string) { var op boolOp testdata.ConvMethod(&op, sliceset.Set(nil), name) for _, tt := range testdata.BinTests { ok := op(tt.A, tt.B) want := tt.SelBool(name) if ok != want { t.Errorf(format, name, tt.A, tt.B, ok, want) } } } set-1.0.0/types.go000066400000000000000000000007351323016122600137770ustar00rootroot00000000000000// Copyright 2015 Kevin Gillette. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package set import "sort" type span struct{ i, j int } type boundspan struct { data sort.Interface span } func (b boundspan) Len() int { return b.j - b.i } func (b boundspan) Less(i, j int) bool { return b.data.Less(b.i+i, b.i+j) } func (b boundspan) Swap(i, j int) { b.data.Swap(b.i+i, b.i+j) }