Created
February 5, 2019 17:03
-
-
Save betandr/c40bb9ac9134901748d796f4cd2a1590 to your computer and use it in GitHub Desktop.
Go Bit Vector
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// bitvec is a bit vector (also known as bit array, bit map, bit set, or bit string) compactly stores bits. | |
// It can be used to implement a simple set data structure. A bit array is effective at exploiting bit-level | |
// parallelism in hardware to perform operations quickly. A typical bit array stores kw bits, where w is the | |
// number of bits in the unit of storage, such as a byte or word, and k is some nonnegative integer. If w | |
// does not divide the number of bits to be stored, some space is wasted due to internal fragmentation. | |
package bitvec | |
import ( | |
"fmt" | |
"strings" | |
) | |
const size = 32 << (^uint(0) >> 63) | |
// An IntSet is a set of small non-negative integers. | |
// Its zero value represents the empty set. | |
type IntSet struct { | |
words []uint | |
} | |
// Has reports whether the set contains the non-negative value x. | |
func (s *IntSet) Has(x int) bool { | |
word, bit := x/size, uint(x%size) | |
return word < len(s.words) && s.words[word]&(1<<bit) != 0 | |
} | |
// AddAll adds the non-negative values x to the set. | |
func (s *IntSet) AddAll(vals ...int) { | |
for _, val := range vals { | |
s.Add(val) | |
} | |
} | |
// Add adds the non-negative value x to the set. | |
func (s *IntSet) Add(x int) { | |
word, bit := x/size, uint(x%size) | |
for word >= len(s.words) { | |
s.words = append(s.words, 0) | |
} | |
s.words[word] |= 1 << bit | |
} | |
// UnionWith sets s to the union of s and t. | |
func (s *IntSet) UnionWith(t *IntSet) { | |
for i, tword := range t.words { | |
if i < len(s.words) { | |
s.words[i] |= tword | |
} else { | |
s.words = append(s.words, tword) | |
} | |
} | |
} | |
// IntersectWith computes the intersection between two sets | |
func (s *IntSet) IntersectWith(t *IntSet) { | |
for i, tword := range t.words { | |
if i < len(s.words) { | |
s.words[i] &= tword | |
} else { | |
s.words = append(s.words, tword) | |
} | |
} | |
} | |
// DifferenceWith computes the difference between two sets | |
func (s *IntSet) DifferenceWith(t *IntSet) { | |
for i, tword := range t.words { | |
if i < len(s.words) { | |
s.words[i] &^= tword | |
} else { | |
s.words = append(s.words, tword) | |
} | |
} | |
} | |
// SymmetricDifferenceWith computes the symmetric difference between two sets | |
func (s *IntSet) SymmetricDifferenceWith(t *IntSet) { | |
for i, tword := range t.words { | |
if i < len(s.words) { | |
s.words[i] ^= tword | |
} else { | |
s.words = append(s.words, tword) | |
} | |
} | |
} | |
// Len returns the number of elements by running a population count (by clearing) | |
// on each word. | |
func (s *IntSet) Len() int { | |
count := 0 | |
for _, w := range s.words { | |
for w != 0 { | |
w = w & (w - 1) // clear rightmost non-zero bit | |
count++ | |
} | |
} | |
return count | |
} | |
// Remove removes `x` from the set | |
func (s *IntSet) Remove(x int) { | |
word, bit := x/size, uint(x%size) | |
s.words[word] ^= 1 << bit | |
} | |
// Clear removes all elements from the set | |
func (s *IntSet) Clear() { | |
s.words = nil // is this a cheat? :thinking_face: | |
} | |
// Copy returns a copy of the set | |
func (s *IntSet) Copy() *IntSet { | |
ns := new(IntSet) | |
for _, word := range s.words { | |
ns.words = append(ns.words, word) | |
} | |
return ns | |
} | |
// Elems returns a slice containing the elements of the set, suitable for | |
// iterating over with a range loop. | |
func (s *IntSet) Elems() []int { | |
e := []int{} | |
for i, word := range s.words { | |
if word == 0 { | |
continue | |
} | |
for j := 0; j < size; j++ { | |
if word&(1<<uint(j)) != 0 { | |
e = append(e, size*i+j) | |
} | |
} | |
} | |
return e | |
} | |
// String returns the set as a string of the form "{1 2 3}". | |
func (s *IntSet) String() string { | |
var sb strings.Builder | |
sb.WriteString("{") | |
for i, word := range s.words { | |
if word == 0 { | |
continue | |
} | |
for j := 0; j < size; j++ { | |
if word&(1<<uint(j)) != 0 { | |
if sb.Len() > len("{") { | |
sb.WriteString(" ") | |
} | |
sb.WriteString(fmt.Sprintf("%d", size*i+j)) | |
} | |
} | |
} | |
sb.WriteString("}") | |
return sb.String() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment