blob: 42f7368908f97de3083abcbd4ffeb6f11cb3e373 [file] [log] [blame] [edit]
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sets
import (
"fmt"
"sort"
"strings"
)
// IntSet stores a set of unique int elements.
type IntSet struct {
set map[int]present
}
// NewIntSet creates an IntSet containing the supplied initial int elements.
func NewIntSet(elements ...int) *IntSet {
s := &IntSet{}
s.set = make(map[int]present)
s.Insert(elements...)
return s
}
// Copy returns a newly allocated copy of the supplied IntSet.
func (s *IntSet) Copy() *IntSet {
c := NewIntSet()
if s != nil {
for e := range s.set {
c.set[e] = present{}
}
}
return c
}
// Insert zero or more int elements into the IntSet. As expected for a Set,
// elements already present in the IntSet are simply ignored.
func (s *IntSet) Insert(elements ...int) {
for _, e := range elements {
s.set[e] = present{}
}
}
// Delete zero or more int elements from the IntSet. Any elements not present
// in the IntSet are simply ignored.
func (s *IntSet) Delete(elements ...int) {
for _, e := range elements {
delete(s.set, e)
}
}
// Intersect returns a new IntSet containing the intersection of the receiver
// and argument IntSets. Returns an empty set if the argument is nil.
func (s *IntSet) Intersect(other *IntSet) *IntSet {
if other == nil {
return NewIntSet()
}
// Point a and b to the maps, setting a to the smaller of the two.
a, b := s.set, other.set
if len(b) < len(a) {
a, b = b, a
}
// Perform the intersection.
intersect := NewIntSet()
for e := range a {
if _, ok := b[e]; ok {
intersect.set[e] = present{}
}
}
return intersect
}
// Disjoint returns true if the intersection of the receiver and the argument
// IntSets is the empty set. Returns true if the argument is nil or either
// IntSet is the empty set.
func (s *IntSet) Disjoint(other *IntSet) bool {
if other == nil || len(other.set) == 0 || len(s.set) == 0 {
return true
}
// Point a and b to the maps, setting a to the smaller of the two.
a, b := s.set, other.set
if len(b) < len(a) {
a, b = b, a
}
// Check for non-empty intersection.
for e := range a {
if _, ok := b[e]; ok {
return false // Early-exit because intersecting.
}
}
return true
}
// Difference returns a new IntSet containing the elements in the receiver that
// are not present in the argument IntSet. Returns a copy of the receiver if
// the argument is nil.
func (s *IntSet) Difference(other *IntSet) *IntSet {
if other == nil {
return s.Copy()
}
// Insert only the elements in the receiver that are not present in the
// argument IntSet.
diff := NewIntSet()
for e := range s.set {
if _, ok := other.set[e]; !ok {
diff.set[e] = present{}
}
}
return diff
}
// Unique returns a new IntSet containing the elements in the receiver that are
// not present in the argument IntSet *and* the elements in the argument IntSet
// that are not in the receiver. Returns a copy of the receiver if the argument
// is nil.
func (s *IntSet) Unique(other *IntSet) *IntSet {
if other == nil {
return s.Copy()
}
sNotInOther := s.Difference(other)
otherNotInS := other.Difference(s)
// Duplicate Union implementation here to avoid extra Copy, since both
// sNotInOther and otherNotInS are already copies.
unique := sNotInOther
for e := range otherNotInS.set {
unique.set[e] = present{}
}
return unique
}
// Equal returns true if the receiver and the argument IntSet contain exactly
// the same elements. Returns false if the argument is nil.
func (s *IntSet) Equal(other *IntSet) bool {
if s == nil || other == nil {
return s == nil && other == nil
}
// Two sets of different length cannot have the exact same unique
// elements.
if len(s.set) != len(other.set) {
return false
}
// Only one loop is needed. If the two sets are known to be of equal
// length, then the two sets are equal only if exactly all of the
// elements in the first set are found in the second.
for e := range s.set {
if _, ok := other.set[e]; !ok {
return false
}
}
return true
}
// Union returns a new IntSet containing the union of the receiver and argument
// IntSets. Returns a copy of the receiver if the argument is nil.
func (s *IntSet) Union(other *IntSet) *IntSet {
union := s.Copy()
if other != nil {
for e := range other.set {
union.set[e] = present{}
}
}
return union
}
// Contains returns true if element is in the IntSet.
func (s *IntSet) Contains(element int) bool {
_, in := s.set[element]
return in
}
// Len returns the number of unique elements in the IntSet.
func (s *IntSet) Len() int {
return len(s.set)
}
// Empty returns true if the receiver is the empty set.
func (s *IntSet) Empty() bool {
return len(s.set) == 0
}
// Elements returns a []int of the elements in the IntSet, in no particular (or
// consistent) order.
func (s *IntSet) Elements() []int {
elements := []int{} // Return at least an empty slice rather than nil.
for e := range s.set {
elements = append(elements, e)
}
return elements
}
// Sorted returns a sorted []int of the elements in the IntSet.
func (s *IntSet) Sorted() []int {
elements := s.Elements()
sort.Ints(elements)
return elements
}
// String formats the IntSet elements as sorted ints, representing them in
// "array initializer" syntax.
func (s *IntSet) String() string {
elements := s.Sorted()
var quoted []string
for _, e := range elements {
quoted = append(quoted, fmt.Sprintf("\"%d\"", e))
}
return fmt.Sprintf("{%s}", strings.Join(quoted, ", "))
}