// 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, ", ")) }