Skip to content

Commit

Permalink
Merge pull request #12 from lemire/master
Browse files Browse the repository at this point in the history
Implemented a way to iterate over the set bits
  • Loading branch information
willf committed Feb 10, 2014
2 parents 04c30b3 + 676aba9 commit d132ae0
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
50 changes: 50 additions & 0 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ func (b *BitSet) Flip(i uint) *BitSet {
return b
}

// return the next bit set from the specified index, including possibly the current index
// returns -1 if none is found
// inspired by the Java API: for i:=int64(0); i>=0; i = NextSet(i) {...}
func (b *BitSet) NextSet(i int64) int64 {
x := uint(i) >> log2WordSize
if x >= b.length {
return -1
}
w := b.set[x]
w = w >> (uint(i) & (wordSize - 1))
if w != 0 {
return int64(i) + int64(trailingZeroes32(w));
}
x = x + 1
for x < wordsNeeded(b.length) {
if b.set[x] != 0 {
return int64(x * wordSize) + int64(trailingZeroes32(b.set[x]));
}
x = x + 1

}
return -1
}

// Clear entire BitSet
func (b *BitSet) ClearAll() *BitSet {
if b != nil && b.set != nil {
Expand Down Expand Up @@ -207,6 +231,32 @@ func popCountUint32(x uint32) uint32 {
//x += x >> 32; //put count of each 64 bits into their lowest 8 bits
return x & 0x7f
}
// Stolen from http://graphics.stanford.edu/~seander/bithacks.html
func trailingZeroes32(v uint32) uint32 {
// NOTE: if 0 == v, then c = 31.
if v & 0x1 != 0 {
return 0
}
c := uint32(1)
if (v & 0xffff) == 0 {
v >>= 16
c += 16
}
if (v & 0xff) == 0 {
v >>= 8
c += 8
}
if (v & 0xf) == 0 {
v >>= 4
c += 4
}
if (v & 0x3) == 0 {
v >>= 2
c += 2
}
c -= v & 0x1
return c
}

// Count (number of set bits)
func (b *BitSet) Count() uint {
Expand Down
59 changes: 59 additions & 0 deletions bitset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,52 @@ func TestBitSetAndGet(t *testing.T) {
}
}

func TestIterate(t *testing.T) {
v := New(10000)
v.Set(0)
v.Set(1)
v.Set(2)
data := make([]uint, 3)
c := 0
for i:=v.NextSet(int64(0)); i>=0; i = v.NextSet(i + 1) {
data[c] = uint(i)
c++
}
if data[0] != 0 {
t.Errorf("bug 0")
}
if data[1] != 1 {
t.Errorf("bug 1")
}
if data[2] != 2 {
t.Errorf("bug 2")
}
v.Set(10)
v.Set(2000)
data = make([]uint, 5)
c = 0
for i:=v.NextSet(int64(0)); i>=0; i = v.NextSet(i + 1) {
data[c] = uint(i)
c++
}
if data[0] != 0 {
t.Errorf("bug 0")
}
if data[1] != 1 {
t.Errorf("bug 1")
}
if data[2] != 2 {
t.Errorf("bug 2")
}
if data[3] != 10 {
t.Errorf("bug 3")
}
if data[4] != 2000 {
t.Errorf("bug 4")
}

}

func TestSetTo(t *testing.T) {
v := New(1000)
v.SetTo(100, true)
Expand Down Expand Up @@ -540,3 +586,16 @@ func BenchmarkSetExpand(b *testing.B) {
s.Set(sz)
}
}

func BenchmarkCount(b *testing.B) {
b.StopTimer()
s := New(100000)
for i := 0; i < 100000; i+=100 {
s.Set(uint(i))
}
b.StartTimer()
for i := 0; i < b.N; i++ {
s.Count()
}
}

0 comments on commit d132ae0

Please sign in to comment.