diff --git a/.travis.yml b/.travis.yml index d4eee61..9413dc4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ branches: go: - 1.8 + - 1.9 - tip matrix: diff --git a/README.md b/README.md index d5817ad..6c62b20 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ As an alternative to BitSets, one should check out the 'big' package, which prov Godoc documentation is at: https://godoc.org/github.com/willf/bitset + +## Implementation Note + +Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed. + +It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `unit64`). If so, the version will be bumped. + ## Installation ```bash diff --git a/VERSION b/VERSION index 45a1b3f..781dcb0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.2 +1.1.3 diff --git a/bitset.go b/bitset.go index a6d236e..dbd1708 100644 --- a/bitset.go +++ b/bitset.go @@ -305,17 +305,6 @@ func (b *BitSet) Count() uint { return 0 } -var deBruijn = [...]byte{ - 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, - 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, - 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, - 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, -} - -func trailingZeroes64(v uint64) uint { - return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58]) -} - // Equal tests the equvalence of two BitSets. // False if they are of different sizes, otherwise true // only if all the same bits are set @@ -441,7 +430,6 @@ func (b *BitSet) InPlaceIntersection(compare *BitSet) { if compare.length > 0 { b.extendSetMaybe(compare.length - 1) } - return } // Union of base set and other set diff --git a/popcnt_19.go b/popcnt_19.go new file mode 100644 index 0000000..fc8ff4f --- /dev/null +++ b/popcnt_19.go @@ -0,0 +1,45 @@ +// +build go1.9 + +package bitset + +import "math/bits" + +func popcntSlice(s []uint64) uint64 { + var cnt int + for _, x := range s { + cnt += bits.OnesCount64(x) + } + return uint64(cnt) +} + +func popcntMaskSlice(s, m []uint64) uint64 { + var cnt int + for i := range s { + cnt += bits.OnesCount64(s[i] &^ m[i]) + } + return uint64(cnt) +} + +func popcntAndSlice(s, m []uint64) uint64 { + var cnt int + for i := range s { + cnt += bits.OnesCount64(s[i] & m[i]) + } + return uint64(cnt) +} + +func popcntOrSlice(s, m []uint64) uint64 { + var cnt int + for i := range s { + cnt += bits.OnesCount64(s[i] | m[i]) + } + return uint64(cnt) +} + +func popcntXorSlice(s, m []uint64) uint64 { + var cnt int + for i := range s { + cnt += bits.OnesCount64(s[i] ^ m[i]) + } + return uint64(cnt) +} diff --git a/popcnt_amd64.go b/popcnt_amd64.go index 665a864..4cf64f2 100644 --- a/popcnt_amd64.go +++ b/popcnt_amd64.go @@ -1,3 +1,4 @@ +// +build !go1.9 // +build amd64,!appengine package bitset diff --git a/popcnt_amd64.s b/popcnt_amd64.s index 18f5878..666c0dc 100644 --- a/popcnt_amd64.s +++ b/popcnt_amd64.s @@ -1,3 +1,4 @@ +// +build !go1.9 // +build amd64,!appengine TEXT ·hasAsm(SB),4,$0-1 diff --git a/popcnt_amd64_test.go b/popcnt_amd64_test.go index 611949b..c79d009 100644 --- a/popcnt_amd64_test.go +++ b/popcnt_amd64_test.go @@ -1,3 +1,4 @@ +// +build !go1.9 // +build amd64,!appengine // This file tests the popcnt funtions diff --git a/popcnt_cmp_test.go b/popcnt_cmp_test.go new file mode 100644 index 0000000..8a06dc9 --- /dev/null +++ b/popcnt_cmp_test.go @@ -0,0 +1,59 @@ +// +build !go1.9 +// +build amd64,!appengine + +// This file tests the popcnt funtions + +package bitset + +import ( + "testing" +) + +func TestComparePopcntSlice(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + resGo := popcntSliceGo(s) + resAsm := popcntSliceAsm(s) + if resGo != resAsm { + t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + } +} + +func TestComparePopcntMaskSlice(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + resGo := popcntMaskSliceGo(s, m) + resAsm := popcntMaskSliceAsm(s, m) + if resGo != resAsm { + t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + } +} + +func TestComparePopcntAndSlice(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + resGo := popcntAndSliceGo(s, m) + resAsm := popcntAndSliceAsm(s, m) + if resGo != resAsm { + t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + } +} + +func TestComparePopcntOrSlice(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + resGo := popcntOrSliceGo(s, m) + resAsm := popcntOrSliceAsm(s, m) + if resGo != resAsm { + t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + } +} + +func TestComparePopcntXorSlice(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + resGo := popcntXorSliceGo(s, m) + resAsm := popcntXorSliceAsm(s, m) + if resGo != resAsm { + t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + } +} diff --git a/popcnt_generic.go b/popcnt_generic.go index 6b21cb7..21e0ff7 100644 --- a/popcnt_generic.go +++ b/popcnt_generic.go @@ -1,3 +1,4 @@ +// +build !go1.9 // +build !amd64 appengine package bitset diff --git a/popcnt_go18_test.go b/popcnt_go18_test.go new file mode 100644 index 0000000..df6921c --- /dev/null +++ b/popcnt_go18_test.go @@ -0,0 +1,56 @@ +// This file tests the popcnt funtions + +package bitset + +import ( + "testing" +) + +func TestPopcntSliceGo(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + res := popcntSliceGo(s) + const l uint64 = 27 + if res != l { + t.Errorf("Wrong popcount %d != %d", res, l) + } +} + +func TestPopcntMaskSliceGo(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + res := popcntMaskSliceGo(s, m) + const l uint64 = 9 + if res != l { + t.Errorf("Wrong mask %d != %d", res, l) + } +} + +func TestPopcntAndSliceGo(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + res := popcntAndSliceGo(s, m) + const l uint64 = 18 + if res != l { + t.Errorf("Wrong And %d != %d", res, l) + } +} + +func TestPopcntOrSliceGo(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + res := popcntOrSliceGo(s, m) + const l uint64 = 50 + if res != l { + t.Errorf("Wrong OR %d != %d", res, l) + } +} + +func TestPopcntXorSliceGo(t *testing.T) { + s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} + m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} + res := popcntXorSliceGo(s, m) + const l uint64 = 32 + if res != l { + t.Errorf("Wrong OR %d != %d", res, l) + } +} diff --git a/popcnt_test.go b/popcnt_test.go index 08bfff5..d9fbb34 100644 --- a/popcnt_test.go +++ b/popcnt_test.go @@ -1,5 +1,3 @@ -// +build amd64,!appengine - // This file tests the popcnt funtions package bitset @@ -10,49 +8,49 @@ import ( func TestPopcntSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} - resGo := popcntSliceGo(s) - resAsm := popcntSliceAsm(s) - if resGo != resAsm { - t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + res := popcntSlice(s) + const l uint64 = 27 + if res != l { + t.Errorf("Wrong popcount %d != %d", res, l) } } func TestPopcntMaskSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} - resGo := popcntMaskSliceGo(s, m) - resAsm := popcntMaskSliceAsm(s, m) - if resGo != resAsm { - t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + res := popcntMaskSlice(s, m) + const l uint64 = 9 + if res != l { + t.Errorf("Wrong mask %d != %d", res, l) } } func TestPopcntAndSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} - resGo := popcntAndSliceGo(s, m) - resAsm := popcntAndSliceAsm(s, m) - if resGo != resAsm { - t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + res := popcntAndSlice(s, m) + const l uint64 = 18 + if res != l { + t.Errorf("Wrong And %d != %d", res, l) } } func TestPopcntOrSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} - resGo := popcntOrSliceGo(s, m) - resAsm := popcntOrSliceAsm(s, m) - if resGo != resAsm { - t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + res := popcntOrSlice(s, m) + const l uint64 = 50 + if res != l { + t.Errorf("Wrong OR %d != %d", res, l) } } func TestPopcntXorSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} - resGo := popcntXorSliceGo(s, m) - resAsm := popcntXorSliceAsm(s, m) - if resGo != resAsm { - t.Errorf("The implementations are different: GO %d != ASM %d", resGo, resAsm) + res := popcntXorSlice(s, m) + const l uint64 = 32 + if res != l { + t.Errorf("Wrong OR %d != %d", res, l) } } diff --git a/trailing_zeros_18.go b/trailing_zeros_18.go new file mode 100644 index 0000000..c52b61b --- /dev/null +++ b/trailing_zeros_18.go @@ -0,0 +1,14 @@ +// +build !go1.9 + +package bitset + +var deBruijn = [...]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +func trailingZeroes64(v uint64) uint { + return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58]) +} diff --git a/trailing_zeros_19.go b/trailing_zeros_19.go new file mode 100644 index 0000000..36a988e --- /dev/null +++ b/trailing_zeros_19.go @@ -0,0 +1,9 @@ +// +build go1.9 + +package bitset + +import "math/bits" + +func trailingZeroes64(v uint64) uint { + return uint(bits.TrailingZeros64(v)) +}