Skip to content

Commit

Permalink
use stdlib math/bits
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Dec 7, 2024
1 parent 4e370e5 commit afff06b
Show file tree
Hide file tree
Showing 16 changed files with 58 additions and 601 deletions.
27 changes: 14 additions & 13 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"errors"
"fmt"
"io"
"math/bits"
"strconv"
)

Expand Down Expand Up @@ -491,7 +492,7 @@ func (b *BitSet) NextSet(i uint) (uint, bool) {
w := b.set[x]
w = w >> wordsIndex(i)
if w != 0 {
return i + trailingZeroes64(w), true
return i + uint(bits.TrailingZeros64(w)), true
}
x++
// bounds check elimination in the loop
Expand All @@ -500,7 +501,7 @@ func (b *BitSet) NextSet(i uint) (uint, bool) {
}
for x < len(b.set) {
if b.set[x] != 0 {
return uint(x)*wordSize + trailingZeroes64(b.set[x]), true
return uint(x)*wordSize + uint(bits.TrailingZeros64(b.set[x])), true
}
x++

Expand Down Expand Up @@ -541,7 +542,7 @@ func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
myanswer = myanswer[:capacity]
size := int(0)
for word != 0 {
r := trailingZeroes64(word)
r := uint(bits.TrailingZeros64(word))
t := word & ((^word) + 1)
myanswer[size] = r + i
size++
Expand All @@ -553,7 +554,7 @@ func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
x++
for idx, word := range b.set[x:] {
for word != 0 {
r := trailingZeroes64(word)
r := uint(bits.TrailingZeros64(word))
t := word & ((^word) + 1)
myanswer[size] = r + (uint(x+idx) << 6)
size++
Expand Down Expand Up @@ -581,7 +582,7 @@ func (b *BitSet) NextClear(i uint) (uint, bool) {
w := b.set[x]
w = w >> wordsIndex(i)
wA := allBits >> wordsIndex(i)
index := i + trailingZeroes64(^w)
index := i + uint(bits.TrailingZeros64(^w))
if w != wA && index < b.length {
return index, true
}
Expand All @@ -592,7 +593,7 @@ func (b *BitSet) NextClear(i uint) (uint, bool) {
}
for x < len(b.set) {
if b.set[x] != allBits {
index = uint(x)*wordSize + trailingZeroes64(^b.set[x])
index = uint(x)*wordSize + uint(bits.TrailingZeros64(^b.set[x]))
if index < b.length {
return index, true
}
Expand All @@ -614,12 +615,12 @@ func (b *BitSet) PreviousSet(i uint) (uint, bool) {
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
return uint(x<<log2WordSize) + uint(bits.Len64(w)) - 1, true
}
for x--; x >= 0; x-- {
w = b.set[x]
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
return uint(x<<log2WordSize) + uint(bits.Len64(w)) - 1, true
}
}
return 0, false
Expand All @@ -639,14 +640,14 @@ func (b *BitSet) PreviousClear(i uint) (uint, bool) {
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
return uint(x<<log2WordSize) + uint(bits.Len64(w)) - 1, true
}

for x--; x >= 0; x-- {
w = b.set[x]
w = ^w
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
return uint(x<<log2WordSize) + uint(bits.Len64(w)) - 1, true
}
}
return 0, false
Expand Down Expand Up @@ -1287,7 +1288,7 @@ func (b *BitSet) Rank(index uint) uint {
leftover := (index + 1) & 63
answer := uint(popcntSlice(b.set[:(index+1)>>6]))
if leftover != 0 {
answer += uint(popcount(b.set[(index+1)>>6] << (64 - leftover)))
answer += uint(bits.OnesCount64(b.set[(index+1)>>6] << (64 - leftover)))
}
return answer
}
Expand All @@ -1302,7 +1303,7 @@ func (b *BitSet) Rank(index uint) uint {
func (b *BitSet) Select(index uint) uint {
leftover := index
for idx, word := range b.set {
w := uint(popcount(word))
w := uint(bits.OnesCount64(word))
if w > leftover {
return uint(idx)*64 + select64(word, leftover)
}
Expand All @@ -1324,7 +1325,7 @@ func (b *BitSet) top() (uint, bool) {
return 0, false
}

return uint(idx)*wordSize + len64(b.set[idx]) - 1, true
return uint(idx)*wordSize + uint(bits.Len64(b.set[idx])) - 1, true
}

// ShiftLeft shifts the bitset like << operation would do.
Expand Down
3 changes: 2 additions & 1 deletion bitset_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package bitset
import (
"bytes"
"fmt"
"math/bits"
"math/rand"
"testing"
)
Expand Down Expand Up @@ -334,7 +335,7 @@ func good(set []uint64) (checksum uint) {
for wordIdx, word := range set {
var wordIdx = uint(wordIdx * 64)
for word != 0 {
var bitIdx = uint(trailingZeroes64(word))
var bitIdx = uint(bits.TrailingZeros64(word))
word ^= 1 << bitIdx
var index = wordIdx + bitIdx
checksum += index
Expand Down
43 changes: 0 additions & 43 deletions leading_zeros_18.go

This file was deleted.

14 changes: 0 additions & 14 deletions leading_zeros_19.go

This file was deleted.

18 changes: 0 additions & 18 deletions leading_zeros_test.go

This file was deleted.

68 changes: 37 additions & 31 deletions popcnt.go
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
package bitset

// bit population count, take from
// https://code.google.com/p/go/issues/detail?id=4988#c11
// credit: https://code.google.com/u/arnehormann/
func popcount(x uint64) (n uint64) {
x -= (x >> 1) & 0x5555555555555555
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
x += x >> 4
x &= 0x0f0f0f0f0f0f0f0f
x *= 0x0101010101010101
return x >> 56
}
import "math/bits"

func popcntSliceGo(s []uint64) uint64 {
cnt := uint64(0)
func popcntSlice(s []uint64) uint64 {
var cnt int
for _, x := range s {
cnt += popcount(x)
cnt += bits.OnesCount64(x)
}
return cnt
return uint64(cnt)
}

func popcntMaskSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
func popcntMaskSlice(s, m []uint64) uint64 {
var cnt int
// this explicit check eliminates a bounds check in the loop
if len(m) < len(s) {
panic("mask slice is too short")
}
for i := range s {
cnt += popcount(s[i] &^ m[i])
cnt += bits.OnesCount64(s[i] &^ m[i])
}
return cnt
return uint64(cnt)
}

func popcntAndSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
func popcntAndSlice(s, m []uint64) uint64 {
var cnt int
// this explicit check eliminates a bounds check in the loop
if len(m) < len(s) {
panic("mask slice is too short")
}
for i := range s {
cnt += popcount(s[i] & m[i])
cnt += bits.OnesCount64(s[i] & m[i])
}
return cnt
return uint64(cnt)
}

func popcntOrSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
func popcntOrSlice(s, m []uint64) uint64 {
var cnt int
// this explicit check eliminates a bounds check in the loop
if len(m) < len(s) {
panic("mask slice is too short")
}
for i := range s {
cnt += popcount(s[i] | m[i])
cnt += bits.OnesCount64(s[i] | m[i])
}
return cnt
return uint64(cnt)
}

func popcntXorSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
func popcntXorSlice(s, m []uint64) uint64 {
var cnt int
// this explicit check eliminates a bounds check in the loop
if len(m) < len(s) {
panic("mask slice is too short")
}
for i := range s {
cnt += popcount(s[i] ^ m[i])
cnt += bits.OnesCount64(s[i] ^ m[i])
}
return cnt
return uint64(cnt)
}
62 changes: 0 additions & 62 deletions popcnt_19.go

This file was deleted.

Loading

0 comments on commit afff06b

Please sign in to comment.