diff --git a/set/set.go b/set/set.go index e2e010a..50e8532 100644 --- a/set/set.go +++ b/set/set.go @@ -193,14 +193,11 @@ func (s *Set) IsSuperset(strict bool, other *Set) bool { if strict && len(other.m) >= len(s.m) { return false } -A: + for v := range other.m { - for i := range s.m { - if v == i { - continue A - } + if _, found := s.m[v]; found == false { + return false } - return false } return true } @@ -210,14 +207,11 @@ func (s *Set) IsSubset(strict bool, other *Set) bool { if strict && len(s.m) >= len(other.m) { return false } -A: + for v := range s.m { - for i := range other.m { - if v == i { - continue A - } + if _, found := other.m[v]; found == false { + return false } - return false } return true } diff --git a/set/t/generate.go b/set/t/generate.go new file mode 100644 index 0000000..24053b8 --- /dev/null +++ b/set/t/generate.go @@ -0,0 +1,2 @@ +//go:generate gotemplate "gotemplate/set" IntSet(int) +package t \ No newline at end of file diff --git a/set/t/gotemplate_IntSet.go b/set/t/gotemplate_IntSet.go new file mode 100644 index 0000000..bf5133c --- /dev/null +++ b/set/t/gotemplate_IntSet.go @@ -0,0 +1,248 @@ +// Code generated by gotemplate. DO NOT EDIT. + +// Package set is a template Set type +// +// Tries to be similar to Python's set type +package t + +// An A is the element of the set +// +// template type Set(A) + +// SetNothing is used as a zero sized member in the map +type IntSetNothing struct{} + +// Set provides a general purpose set modeled on Python's set type. +type IntSet struct { + m map[int]IntSetNothing +} + +// NewSizedSet returns a new empty set with the given capacity +func NewSizedIntSet(capacity int) *IntSet { + return &IntSet{ + m: make(map[int]IntSetNothing, capacity), + } +} + +// NewSet returns a new empty set +func NewIntSet() *IntSet { + return NewSizedIntSet(0) +} + +// Len returns the number of elements in the set +func (s *IntSet) Len() int { + return len(s.m) +} + +// Contains returns whether elem is in the set or not +func (s *IntSet) Contains(elem int) bool { + _, found := s.m[elem] + return found +} + +// Add adds elem to the set, returning the set +// +// If the element already exists then it has no effect +func (s *IntSet) Add(elem int) *IntSet { + s.m[elem] = IntSetNothing{} + return s +} + +// AddList adds a list of elems to the set +// +// If the elements already exists then it has no effect +func (s *IntSet) AddList(elems []int) *IntSet { + for _, elem := range elems { + s.m[elem] = IntSetNothing{} + } + return s +} + +// Discard removes elem from the set +// +// If it wasn't in the set it does nothing +// +// It returns the set +func (s *IntSet) Discard(elem int) *IntSet { + delete(s.m, elem) + return s +} + +// Remove removes elem from the set +// +// It returns whether the elem was in the set or not +func (s *IntSet) Remove(elem int) bool { + _, found := s.m[elem] + if found { + delete(s.m, elem) + } + return found +} + +// Pop removes elem from the set and returns it +// +// It also returns whether the elem was found or not +func (s *IntSet) Pop(elem int) (int, bool) { + _, found := s.m[elem] + if found { + delete(s.m, elem) + } + return elem, found +} + +// AsList returns all the elements as a slice +func (s *IntSet) AsList() []int { + elems := make([]int, len(s.m)) + i := 0 + for elem := range s.m { + elems[i] = elem + i++ + } + return elems +} + +// Clear removes all the elements +func (s *IntSet) Clear() *IntSet { + s.m = make(map[int]IntSetNothing) + return s +} + +// Copy returns a shallow copy of the Set +func (s *IntSet) Copy() *IntSet { + newSet := NewSizedIntSet(len(s.m)) + for elem := range s.m { + newSet.m[elem] = IntSetNothing{} + } + return newSet +} + +// Difference returns a new set with all the elements that are in this +// set but not in the other +func (s *IntSet) Difference(other *IntSet) *IntSet { + newSet := NewSizedIntSet(len(s.m)) + for elem := range s.m { + if _, found := other.m[elem]; !found { + newSet.m[elem] = IntSetNothing{} + } + } + return newSet +} + +// DifferenceUpdate removes all the elements that are in the other set +// from this set. It returns the set. +func (s *IntSet) DifferenceUpdate(other *IntSet) *IntSet { + m := s.m + for elem := range other.m { + delete(m, elem) + } + return s +} + +// Intersection returns a new set with all the elements that are only in this +// set and the other set. It returns the new set. +func (s *IntSet) Intersection(other *IntSet) *IntSet { + newSet := NewSizedIntSet(len(s.m) + len(other.m)) + for elem := range s.m { + if _, found := other.m[elem]; found { + newSet.m[elem] = IntSetNothing{} + } + } + for elem := range other.m { + if _, found := s.m[elem]; found { + newSet.m[elem] = IntSetNothing{} + } + } + return newSet +} + +// IntersectionUpdate changes this set so that it only contains +// elements that are in both this set and the other set. It returns +// the set. +func (s *IntSet) IntersectionUpdate(other *IntSet) *IntSet { + for elem := range s.m { + if _, found := other.m[elem]; !found { + delete(s.m, elem) + } + } + return s +} + +// Union returns a new set with all the elements that are in either +// set. It returns the new set. +func (s *IntSet) Union(other *IntSet) *IntSet { + newSet := NewSizedIntSet(len(s.m) + len(other.m)) + for elem := range s.m { + newSet.m[elem] = IntSetNothing{} + } + for elem := range other.m { + newSet.m[elem] = IntSetNothing{} + } + return newSet +} + +// Update adds all the elements from the other set to this set. +// It returns the set. +func (s *IntSet) Update(other *IntSet) *IntSet { + for elem := range other.m { + s.m[elem] = IntSetNothing{} + } + return s +} + +// IsSuperset returns a bool indicating whether this set is a superset of other set. +func (s *IntSet) IsSuperset(strict bool, other *IntSet) bool { + if strict && len(other.m) >= len(s.m) { + return false + } + + for v := range other.m { + if _, found := s.m[v]; found == false { + return false + } + } + return true +} + +// IsSubset returns a bool indicating whether this set is a subset of other set. +func (s *IntSet) IsSubset(strict bool, other *IntSet) bool { + if strict && len(s.m) >= len(other.m) { + return false + } + + for v := range s.m { + if _, found := other.m[v]; found == false { + return false + } + } + return true +} + +// IsDisjoint returns a bool indicating whether this set and other set have no elements in common. +func (s *IntSet) IsDisjoint(other *IntSet) bool { + for v := range s.m { + if other.Contains(v) { + return false + } + } + return true +} + +// SymmetricDifference returns a new set of all elements that are a member of exactly +// one of this set and other set(elements which are in one of the sets, but not in both). +func (s *IntSet) SymmetricDifference(other *IntSet) *IntSet { + work1 := s.Union(other) + work2 := s.Intersection(other) + for v := range work2.m { + delete(work1.m, v) + } + return work1 +} + +// SymmetricDifferenceUpdate modifies this set to be a set of all elements that are a member +// of exactly one of this set and other set(elements which are in one of the sets, +// but not in both) and returns this set. +func (s *IntSet) SymmetricDifferenceUpdate(other *IntSet) *IntSet { + work := s.SymmetricDifference(other) + *s = *work + return s +} diff --git a/set/t/set_test.go b/set/t/set_test.go new file mode 100644 index 0000000..7b8f4e7 --- /dev/null +++ b/set/t/set_test.go @@ -0,0 +1,339 @@ +// This tests the set package +package t + +import ( + "sort" + "testing" +) + +// Check the set is equal to the literal slice +func assertEqual(t *testing.T, s *IntSet, b []int) { + a := make([]int, len(s.m)) + i := 0 + for elem := range s.m { + a[i] = int(elem) + i++ + } + sort.Ints(a) + sort.Ints(b) + if len(a) != len(b) { + t.Fatalf("Bad lengths %v vs %v", a, b) + } + for i := range a { + if a[i] != b[i] { + t.Fatalf("%v != %v", a, b) + } + } +} + +func TestNewSizedSet(t *testing.T) { + a := NewSizedIntSet(0) + if a.Len() != 0 { + t.Fatal("length nonzero") + } + a = NewSizedIntSet(100) + if a.Len() != 0 { + t.Fatal("length nonzero") + } +} + +func TestNewSet(t *testing.T) { + a := NewIntSet() + if a.Len() != 0 { + t.Fatal("length nonzero") + } +} + +func TestSetLen(t *testing.T) { + a := NewIntSet() + if a.Len() != 0 { + t.Fatal("length nonzero") + } + a.Add(1) + if a.Len() != 1 { + t.Fatal("length not 1") + } + a.Discard(1) + if a.Len() != 0 { + t.Fatal("length nonzero") + } +} + +func TestSetContains(t *testing.T) { + a := NewIntSet().Add(1) + if a.Contains(0) { + t.Fatal("0 found in set") + } + if !a.Contains(1) { + t.Fatal("1 not found in set") + } + if a.Contains(2) { + t.Fatal("2 found in set") + } +} + +func TestSetAdd(t *testing.T) { + a := NewIntSet().Add(1).Add(2).Add(3) + assertEqual(t, a, []int{1, 2, 3}) +} + +func TestSetAddList(t *testing.T) { + a := NewIntSet().AddList([]int{1, 2, 3}) + assertEqual(t, a, []int{1, 2, 3}) +} + +func TestSetDiscard(t *testing.T) { + a := NewIntSet().Add(1).Add(3).Discard(1).Discard(2) + assertEqual(t, a, []int{3}) +} + +func TestSetRemove(t *testing.T) { + a := NewIntSet().Add(1).Add(3) + assertEqual(t, a, []int{1, 3}) + if a.Remove(1) != true { + t.Fatal("1 not in set") + } + if a.Remove(2) != false { + t.Fatal("2 in set") + } + assertEqual(t, a, []int{3}) +} + +func TestSetPop(t *testing.T) { + a := NewIntSet().Add(1).Add(3) + assertEqual(t, a, []int{1, 3}) + elem, found := a.Pop(1) + if elem != 1 || found != true { + t.Fatal("pop existing element failed") + } + elem, found = a.Pop(2) + if elem != 2 || found != false { + t.Fatal("pop non-existing element failed") + } + assertEqual(t, a, []int{3}) +} + +func TestSetAsList(t *testing.T) { + a := NewIntSet().Add(1).Add(3) + assertEqual(t, a, []int{1, 3}) + as := a.AsList() + if len(as) != 2 { + t.Fatal("length != 2") + } + if as[1] < as[0] { + as[0], as[1] = as[1], as[0] + } + if as[0] != 1 || as[1] != 3 { + t.Fatal("set as list failed") + } +} + +func TestSetClear(t *testing.T) { + a := NewIntSet().Add(1).Add(3) + assertEqual(t, a, []int{1, 3}) + a.Clear() + assertEqual(t, a, []int{}) +} + +func TestSetCopy(t *testing.T) { + a := NewIntSet().Add(1).Add(3) + assertEqual(t, a, []int{1, 3}) + b := a.Copy() + assertEqual(t, b, []int{1, 3}) + if a == b || &a.m == &b.m { + t.Fatal("set copy failed") + } +} + +func TestSetDifference(t *testing.T) { + assertEqual(t, NewIntSet().Difference(NewIntSet()), []int{}) + a := NewIntSet().Add(1).Add(3) + b := NewIntSet().Add(1).Add(2) + assertEqual(t, a.Difference(b), []int{3}) + assertEqual(t, a, []int{1, 3}) + assertEqual(t, a.Difference(NewIntSet()), []int{1, 3}) + assertEqual(t, a, []int{1, 3}) + assertEqual(t, NewIntSet().Difference(a), []int{}) + assertEqual(t, a, []int{1, 3}) + assertEqual(t, b.Difference(a), []int{2}) + assertEqual(t, b, []int{1, 2}) +} + +func TestSetDifferenceUpdate(t *testing.T) { + assertEqual(t, NewIntSet().DifferenceUpdate(NewIntSet()), []int{}) + a := NewIntSet().Add(1).Add(3) + b := NewIntSet().Add(1).Add(2) + assertEqual(t, a.DifferenceUpdate(b), []int{3}) + a.DifferenceUpdate(b) + assertEqual(t, a, []int{3}) + assertEqual(t, a.DifferenceUpdate(NewIntSet()), []int{3}) + assertEqual(t, NewIntSet().DifferenceUpdate(a), []int{}) + a.Add(1) + assertEqual(t, b.DifferenceUpdate(a), []int{2}) + assertEqual(t, b, []int{2}) +} + +func TestSetIntersection(t *testing.T) { + a := NewIntSet() + b := NewIntSet() + assertEqual(t, a.Intersection(b), []int{}) + assertEqual(t, b.Intersection(a), []int{}) + a.Add(1) + a.Add(2) + b.Add(2) + b.Add(3) + assertEqual(t, a.Intersection(b), []int{2}) + assertEqual(t, a, []int{1, 2}) + assertEqual(t, b.Intersection(a), []int{2}) + assertEqual(t, b, []int{2, 3}) +} + +func TestSetIntersectionUpdate(t *testing.T) { + a := NewIntSet() + b := NewIntSet() + assertEqual(t, a.IntersectionUpdate(b), []int{}) + assertEqual(t, b.IntersectionUpdate(a), []int{}) + a.Add(1) + a.Add(2) + b.Add(2) + b.Add(3) + assertEqual(t, a.IntersectionUpdate(b), []int{2}) + assertEqual(t, a, []int{2}) + a.Add(1) + a.Add(2) + b.Add(2) + b.Add(3) + assertEqual(t, b.IntersectionUpdate(a), []int{2}) + assertEqual(t, b, []int{2}) +} + +func TestSetUnion(t *testing.T) { + a := NewIntSet() + b := NewIntSet() + assertEqual(t, a.Union(b), []int{}) + assertEqual(t, b.Union(a), []int{}) + a.Add(1) + a.Add(2) + b.Add(2) + b.Add(3) + assertEqual(t, a.Union(b), []int{1, 2, 3}) + assertEqual(t, a, []int{1, 2}) + a.Clear().Add(1).Add(2) + b.Clear().Add(2).Add(3) + assertEqual(t, b.Union(a), []int{1, 2, 3}) + assertEqual(t, b, []int{2, 3}) +} + +func TestSetUpdate(t *testing.T) { + a := NewIntSet() + b := NewIntSet() + assertEqual(t, a.Update(b), []int{}) + assertEqual(t, b.Update(a), []int{}) + a.Add(1) + a.Add(2) + b.Add(2) + b.Add(3) + assertEqual(t, a.Update(b), []int{1, 2, 3}) + assertEqual(t, a, []int{1, 2, 3}) + a.Clear().Add(1).Add(2) + b.Clear().Add(2).Add(3) + assertEqual(t, b.Update(a), []int{1, 2, 3}) + assertEqual(t, b, []int{1, 2, 3}) +} + +func TestSetIsSuperset(t *testing.T) { + a := NewIntSet().Add(1).Add(2).Add(3) + b := NewIntSet().Add(1).Add(2) + assertEqual(t, a, []int{1, 2, 3}) + assertEqual(t, b, []int{1, 2}) + //test if superset returns correctly with strict true + if a.IsSuperset(true, b) == false { + t.Fatal("strict superset failed") + } + //test if superset returns correctly with strict false + if a.IsSuperset(false, b) == false { + t.Fatal("non-strict superset failed") + } + + b.Add(3) + assertEqual(t, a, []int{1, 2, 3}) + assertEqual(t, b, []int{1, 2, 3}) + //test if superset returns correctly with strict true + if a.IsSuperset(true, b) == true { + t.Fatal("strict superset failed") + } + //test if superset returns correctly with strict false + if a.IsSuperset(false, b) == false { + t.Fatal("non-strict superset failed") + } +} + +func TestSetIsSubset(t *testing.T) { + a := NewIntSet().Add(1).Add(2).Add(3) + b := NewIntSet().Add(1).Add(2) + assertEqual(t, a, []int{1, 2, 3}) + assertEqual(t, b, []int{1, 2}) + //test if subset returns correctly with strict true + if b.IsSubset(true, a) == false { + t.Fatal("strict subset failed") + } + //test if subset returns correctly with strict false + if b.IsSubset(false, a) == false { + t.Fatal("non-strict subset failed") + } + b.Add(3) + assertEqual(t, a, []int{1, 2, 3}) + assertEqual(t, b, []int{1, 2, 3}) + //test if subset returns correctly with strict true + if b.IsSubset(true, a) == true { + t.Fatal("strict subset failed") + } + //test if subset returns correctly with strict false + if b.IsSubset(false, a) == false { + t.Fatal("non-strict subset failed") + } +} + +func TestSetIsDisjoint(t *testing.T) { + a := NewIntSet().Add(1) + b := NewIntSet().Add(2) + assertEqual(t, a, []int{1}) + assertEqual(t, b, []int{2}) + if a.IsDisjoint(b) == false || b.IsDisjoint(a) == false { + t.Fatal("disjoint failed #1") + } + + c := NewIntSet().Add(1).Add(2) + d := NewIntSet().Add(2) + assertEqual(t, c, []int{1, 2}) + assertEqual(t, d, []int{2}) + if c.IsDisjoint(d) == true || d.IsDisjoint(c) == true { + t.Fatal("disjoint failed #2") + } + + e := NewIntSet().Add(1) + f := NewIntSet().Add(1).Add(2) + assertEqual(t, e, []int{1}) + assertEqual(t, f, []int{1, 2}) + if e.IsDisjoint(f) == true || f.IsDisjoint(e) == true { + t.Fatal("disjoint failed #3") + } +} + +func TestSetSymmetricDifference(t *testing.T) { + a := NewIntSet().Add(1).Add(2) + b := NewIntSet().Add(2).Add(3) + c := a.SymmetricDifference(b) + assertEqual(t, a, []int{1, 2}) + assertEqual(t, b, []int{2, 3}) + assertEqual(t, c, []int{1, 3}) +} + +func TestSetSymmetricDifferenceUpdate(t *testing.T) { + a := NewIntSet().Add(1).Add(2) + b := NewIntSet().Add(2).Add(3) + assertEqual(t, a, []int{1, 2}) + assertEqual(t, b, []int{2, 3}) + a.SymmetricDifferenceUpdate(b) + assertEqual(t, a, []int{1, 3}) +}