├── go.sum
├── go.mod
├── SECURITY.md
├── .github
├── FUNDING.yml
├── workflows
│ ├── legacy86.yaml
│ ├── test.yml
│ └── scorecard.yml
├── dependabot.yml
└── SECURITY.md
├── .gitignore
├── bitset_iter.go
├── .travis.yml
├── select.go
├── azure-pipelines.yml
├── popcnt_test.go
├── LICENSE
├── popcnt.go
├── bitset_iter_test.go
├── cmd
└── pextgen
│ └── main.go
├── README.md
├── bitset_benchmark_test.go
├── bitset.go
└── bitset_test.go
/go.sum:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/bits-and-blooms/bitset
2 |
3 | go 1.16
4 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | You can report privately a vulnerability by email at daniel@lemire.me (current maintainer).
6 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # You can add one username per supported platform and one custom link
2 | patreon: # Replace with your Patreon username
3 | open_collective: # Replace with your Open Collective username
4 | ko_fi: # Replace with your Ko-fi username
5 | custom: https://donate.mcc.org/ # Replace with your custom donation URL
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 |
26 | target
27 |
--------------------------------------------------------------------------------
/.github/workflows/legacy86.yaml:
--------------------------------------------------------------------------------
1 | name: Go-legacy-CI
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | strategy:
7 | matrix:
8 | go-version: ['1.20.x', '1.21.x']
9 | steps:
10 | - name: Install Go
11 | uses: actions/setup-go@v5
12 | with:
13 | go-version: ${{ matrix.go-version }}
14 | - name: Checkout code
15 | uses: actions/checkout@v4
16 | - name: Test on 386
17 | run: |
18 | GOARCH=386 go test
19 |
--------------------------------------------------------------------------------
/bitset_iter.go:
--------------------------------------------------------------------------------
1 | //go:build go1.23
2 | // +build go1.23
3 |
4 | package bitset
5 |
6 | import (
7 | "iter"
8 | "math/bits"
9 | )
10 |
11 | func (b *BitSet) EachSet() iter.Seq[uint] {
12 | return func(yield func(uint) bool) {
13 | for wordIndex, word := range b.set {
14 | idx := 0
15 | for trail := bits.TrailingZeros64(word); trail != 64; trail = bits.TrailingZeros64(word >> idx) {
16 | if !yield(uint(wordIndex<> 32
12 | seen += 32
13 | j -= n
14 | }
15 | ww := part
16 |
17 | // Divide 32bit
18 | part = ww & 0xFFFF
19 |
20 | n = uint(bits.OnesCount64(part))
21 | if n <= j {
22 | part = ww >> 16
23 | seen += 16
24 | j -= n
25 | }
26 | ww = part
27 |
28 | // Divide 16bit
29 | part = ww & 0xFF
30 | n = uint(bits.OnesCount64(part))
31 | if n <= j {
32 | part = ww >> 8
33 | seen += 8
34 | j -= n
35 | }
36 | ww = part
37 |
38 | // Lookup in final byte
39 | counter := 0
40 | for ; counter < 8; counter++ {
41 | j -= uint((ww >> counter) & 1)
42 | if j+1 == 0 {
43 | break
44 | }
45 | }
46 | return uint(seen + counter)
47 | }
48 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Security updates are applied only to the latest release.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
10 |
11 | Please use the following contact information for reporting a vulnerability:
12 |
13 | - [Daniel Lemire](https://github.com/lemire) - [daniel@lemire.me](mailto:daniel@lemire.me)
14 |
15 | In your report, please include:
16 |
17 | - A description of the vulnerability and its impact
18 | - How to reproduce the it
19 | - Affected versions
20 |
21 | This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
22 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # Go
2 | # Build your Go project.
3 | # Add steps that test, save build artifacts, deploy, and more:
4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/go
5 |
6 | trigger:
7 | - master
8 |
9 | pool:
10 | vmImage: 'Ubuntu-16.04'
11 |
12 | variables:
13 | GOBIN: '$(GOPATH)/bin' # Go binaries path
14 | GOROOT: '/usr/local/go1.11' # Go installation path
15 | GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path
16 | modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
17 |
18 | steps:
19 | - script: |
20 | mkdir -p '$(GOBIN)'
21 | mkdir -p '$(GOPATH)/pkg'
22 | mkdir -p '$(modulePath)'
23 | shopt -s extglob
24 | shopt -s dotglob
25 | mv !(gopath) '$(modulePath)'
26 | echo '##vso[task.prependpath]$(GOBIN)'
27 | echo '##vso[task.prependpath]$(GOROOT)/bin'
28 | displayName: 'Set up the Go workspace'
29 |
30 | - script: |
31 | go version
32 | go get -v -t -d ./...
33 | if [ -f Gopkg.toml ]; then
34 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
35 | dep ensure
36 | fi
37 | go build -v .
38 | workingDirectory: '$(modulePath)'
39 | displayName: 'Get dependencies, then build'
40 |
--------------------------------------------------------------------------------
/popcnt_test.go:
--------------------------------------------------------------------------------
1 | // This file tests the popcnt functions
2 |
3 | package bitset
4 |
5 | import (
6 | "testing"
7 | )
8 |
9 | func TestPopcntSlice(t *testing.T) {
10 | s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
11 | res := popcntSlice(s)
12 | const l uint64 = 27
13 | if res != l {
14 | t.Errorf("Wrong popcount %d != %d", res, l)
15 | }
16 | }
17 |
18 | func TestPopcntMaskSlice(t *testing.T) {
19 | s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
20 | m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
21 | res := popcntMaskSlice(s, m)
22 | const l uint64 = 9
23 | if res != l {
24 | t.Errorf("Wrong mask %d != %d", res, l)
25 | }
26 | }
27 |
28 | func TestPopcntAndSlice(t *testing.T) {
29 | s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
30 | m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
31 | res := popcntAndSlice(s, m)
32 | const l uint64 = 18
33 | if res != l {
34 | t.Errorf("Wrong And %d != %d", res, l)
35 | }
36 | }
37 |
38 | func TestPopcntOrSlice(t *testing.T) {
39 | s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
40 | m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
41 | res := popcntOrSlice(s, m)
42 | const l uint64 = 50
43 | if res != l {
44 | t.Errorf("Wrong OR %d != %d", res, l)
45 | }
46 | }
47 |
48 | func TestPopcntXorSlice(t *testing.T) {
49 | s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
50 | m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71}
51 | res := popcntXorSlice(s, m)
52 | const l uint64 = 32
53 | if res != l {
54 | t.Errorf("Wrong OR %d != %d", res, l)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Will Fitzgerald. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/popcnt.go:
--------------------------------------------------------------------------------
1 | package bitset
2 |
3 | import "math/bits"
4 |
5 | func popcntSlice(s []uint64) (cnt uint64) {
6 | for _, x := range s {
7 | cnt += uint64(bits.OnesCount64(x))
8 | }
9 | return
10 | }
11 |
12 | func popcntMaskSlice(s, m []uint64) (cnt uint64) {
13 | // The next line is to help the bounds checker, it matters!
14 | _ = m[len(s)-1] // BCE
15 | for i := range s {
16 | cnt += uint64(bits.OnesCount64(s[i] &^ m[i]))
17 | }
18 | return
19 | }
20 |
21 | // popcntAndSlice computes the population count of the AND of two slices.
22 | // It assumes that len(m) >= len(s) > 0.
23 | func popcntAndSlice(s, m []uint64) (cnt uint64) {
24 | // The next line is to help the bounds checker, it matters!
25 | _ = m[len(s)-1] // BCE
26 | for i := range s {
27 | cnt += uint64(bits.OnesCount64(s[i] & m[i]))
28 | }
29 | return
30 | }
31 |
32 | // popcntOrSlice computes the population count of the OR of two slices.
33 | // It assumes that len(m) >= len(s) > 0.
34 | func popcntOrSlice(s, m []uint64) (cnt uint64) {
35 | // The next line is to help the bounds checker, it matters!
36 | _ = m[len(s)-1] // BCE
37 | for i := range s {
38 | cnt += uint64(bits.OnesCount64(s[i] | m[i]))
39 | }
40 | return
41 | }
42 |
43 | // popcntXorSlice computes the population count of the XOR of two slices.
44 | // It assumes that len(m) >= len(s) > 0.
45 | func popcntXorSlice(s, m []uint64) (cnt uint64) {
46 | // The next line is to help the bounds checker, it matters!
47 | _ = m[len(s)-1] // BCE
48 | for i := range s {
49 | cnt += uint64(bits.OnesCount64(s[i] ^ m[i]))
50 | }
51 | return
52 | }
53 |
--------------------------------------------------------------------------------
/bitset_iter_test.go:
--------------------------------------------------------------------------------
1 | //go:build go1.23
2 | // +build go1.23
3 |
4 | package bitset
5 |
6 | import (
7 | "testing"
8 | )
9 |
10 | func TestIter(t *testing.T) {
11 | var b BitSet
12 | b.Set(0).Set(3).Set(5).Set(6).Set(63).Set(64).Set(65).Set(127).Set(128).Set(1000000)
13 |
14 | // Expected values that should be set
15 | expected := []uint{0, 3, 5, 6, 63, 64, 65, 127, 128, 1000000}
16 | got := make([]uint, 0)
17 |
18 | // Collect all values from iterator
19 | for i := range b.EachSet() {
20 | got = append(got, i)
21 | }
22 |
23 | // Test 1: Check length matches expected
24 | if len(got) != len(expected) {
25 | t.Errorf("Expected %d elements, got %d", len(expected), len(got))
26 | }
27 |
28 | // Test 2: Check all expected values are present and in correct order
29 | for i, want := range expected {
30 | if i >= len(got) {
31 | t.Errorf("Missing expected value %d at position %d", want, i)
32 | continue
33 | }
34 | if got[i] != want {
35 | t.Errorf("At position %d: expected %d, got %d", i, want, got[i])
36 | }
37 | }
38 |
39 | // Test 3: Check no extra values
40 | if len(got) > len(expected) {
41 | t.Errorf("Got extra values: %v", got[len(expected):])
42 | }
43 | }
44 | func BenchmarkIter(b *testing.B) {
45 | b.StopTimer()
46 | s := New(10000)
47 | for i := 0; i < 10000; i += 3 {
48 | s.Set(uint(i))
49 | }
50 |
51 | b.StartTimer()
52 | for j := 0; j < b.N; j++ {
53 | c := uint(0)
54 | for range s.EachSet() {
55 | c++
56 | }
57 | }
58 | }
59 |
60 | func BenchmarkNonInter(b *testing.B) {
61 | b.StopTimer()
62 | s := New(10000)
63 | for i := 0; i < 10000; i += 3 {
64 | s.Set(uint(i))
65 | }
66 |
67 | b.StartTimer()
68 | for j := 0; j < b.N; j++ {
69 | c := uint(0)
70 | for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) {
71 | c++
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/.github/workflows/scorecard.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are provided
2 | # by a third-party and are governed by separate terms of service, privacy
3 | # policy, and support documentation.
4 |
5 | name: Scorecard supply-chain security
6 | on:
7 | # For Branch-Protection check. Only the default branch is supported. See
8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9 | branch_protection_rule:
10 | # To guarantee Maintained check is occasionally updated. See
11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12 | schedule:
13 | - cron: '40 9 * * 2'
14 | push:
15 | branches: [ "master" ]
16 |
17 | # Declare default permissions as read only.
18 | permissions: read-all
19 |
20 | jobs:
21 | analysis:
22 | name: Scorecard analysis
23 | runs-on: ubuntu-latest
24 | permissions:
25 | # Needed to upload the results to code-scanning dashboard.
26 | security-events: write
27 | # Needed to publish results and get a badge (see publish_results below).
28 | id-token: write
29 | # Uncomment the permissions below if installing in a private repository.
30 | # contents: read
31 | # actions: read
32 |
33 | steps:
34 | - name: "Checkout code"
35 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
36 | with:
37 | persist-credentials: false
38 |
39 | - name: "Run analysis"
40 | uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
41 | with:
42 | results_file: results.sarif
43 | results_format: sarif
44 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
45 | # - you want to enable the Branch-Protection check on a *public* repository, or
46 | # - you are installing Scorecard on a *private* repository
47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
48 | # repo_token: ${{ secrets.SCORECARD_TOKEN }}
49 |
50 | # Public repositories:
51 | # - Publish results to OpenSSF REST API for easy access by consumers
52 | # - Allows the repository to include the Scorecard badge.
53 | # - See https://github.com/ossf/scorecard-action#publishing-results.
54 | # For private repositories:
55 | # - `publish_results` will always be set to `false`, regardless
56 | # of the value entered here.
57 | publish_results: true
58 |
59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
60 | # format to the repository Actions tab.
61 | - name: "Upload artifact"
62 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
63 | with:
64 | name: SARIF file
65 | path: results.sarif
66 | retention-days: 5
67 |
68 | # Upload the results to GitHub's code scanning dashboard.
69 | - name: "Upload to code-scanning"
70 | uses: github/codeql-action/upload-sarif@8e0b1c74b1d5a0077b04d064c76ee714d3da7637 # v2.14.6
71 | with:
72 | sarif_file: results.sarif
73 |
--------------------------------------------------------------------------------
/cmd/pextgen/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "fmt"
7 | "go/format"
8 | "math/bits"
9 | "os"
10 | )
11 |
12 | // pextByte handles single-byte PEXT operation
13 | func pextByte(b, m uint8) uint8 {
14 | var result, bitPos uint8
15 | for i := uint8(0); i < 8; i++ {
16 | if m&(1<Mastering Programming: From Testing to Performance in Go
176 | 
177 |
--------------------------------------------------------------------------------
/bitset_benchmark_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 Will Fitzgerald. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // This file tests bit sets
6 |
7 | package bitset
8 |
9 | import (
10 | "bytes"
11 | "fmt"
12 | "math/bits"
13 | "math/rand"
14 | "testing"
15 | )
16 |
17 | func BenchmarkSet(b *testing.B) {
18 | b.StopTimer()
19 | r := rand.New(rand.NewSource(0))
20 | sz := 100000
21 | s := New(uint(sz))
22 | b.StartTimer()
23 | for i := 0; i < b.N; i++ {
24 | s.Set(uint(r.Int31n(int32(sz))))
25 | }
26 | }
27 |
28 | func BenchmarkGetTest(b *testing.B) {
29 | b.StopTimer()
30 | r := rand.New(rand.NewSource(0))
31 | sz := 100000
32 | s := New(uint(sz))
33 | b.StartTimer()
34 | for i := 0; i < b.N; i++ {
35 | s.Test(uint(r.Int31n(int32(sz))))
36 | }
37 | }
38 |
39 | func BenchmarkSetExpand(b *testing.B) {
40 | b.StopTimer()
41 | sz := uint(100000)
42 | b.StartTimer()
43 | for i := 0; i < b.N; i++ {
44 | var s BitSet
45 | s.Set(sz)
46 | }
47 | }
48 |
49 | // go test -bench=Count
50 | func BenchmarkCount(b *testing.B) {
51 | b.StopTimer()
52 | s := New(100000)
53 | for i := 0; i < 100000; i += 100 {
54 | s.Set(uint(i))
55 | }
56 | b.StartTimer()
57 | for i := 0; i < b.N; i++ {
58 | s.Count()
59 | }
60 | }
61 |
62 | // go test -bench=Iterate
63 | func BenchmarkIterate(b *testing.B) {
64 | b.StopTimer()
65 | s := New(10000)
66 | for i := 0; i < 10000; i += 3 {
67 | s.Set(uint(i))
68 | }
69 | b.StartTimer()
70 | for j := 0; j < b.N; j++ {
71 | c := uint(0)
72 | for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) {
73 | c++
74 | }
75 | }
76 | }
77 |
78 | // go test -bench=SparseIterate
79 | func BenchmarkSparseIterate(b *testing.B) {
80 | b.StopTimer()
81 | s := New(100000)
82 | for i := 0; i < 100000; i += 30 {
83 | s.Set(uint(i))
84 | }
85 | b.StartTimer()
86 | for j := 0; j < b.N; j++ {
87 | c := uint(0)
88 | for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) {
89 | c++
90 | }
91 | }
92 | }
93 |
94 | // go test -bench=BitsetOps
95 | func BenchmarkBitsetOps(b *testing.B) {
96 | // let's not write into s inside the benchmarks
97 | s := New(100000)
98 | for i := 0; i < 100000; i += 100 {
99 | s.Set(uint(i))
100 | }
101 | cpy := s.Clone()
102 |
103 | b.Run("Equal", func(b *testing.B) {
104 | for i := 0; i < b.N; i++ {
105 | s.Equal(cpy)
106 | }
107 | })
108 |
109 | b.Run("FlipRange", func(b *testing.B) {
110 | s = s.Clone()
111 | b.ResetTimer()
112 | for i := 0; i < b.N; i++ {
113 | s.FlipRange(0, 100000)
114 | }
115 | })
116 |
117 | b.Run("NextSet", func(b *testing.B) {
118 | s = New(100000)
119 | b.ResetTimer()
120 | for i := 0; i < b.N; i++ {
121 | s.NextSet(0)
122 | }
123 | })
124 |
125 | b.Run("NextClear", func(b *testing.B) {
126 | s = New(100000)
127 | s.FlipRange(0, 100000)
128 | b.ResetTimer()
129 | for i := 0; i < b.N; i++ {
130 | s.NextClear(0)
131 | }
132 | })
133 |
134 | b.Run("PreviousSet", func(b *testing.B) {
135 | s = New(100000)
136 | b.ResetTimer()
137 | for i := 0; i < b.N; i++ {
138 | s.PreviousSet(99999)
139 | }
140 | })
141 |
142 | b.Run("PreviousClear", func(b *testing.B) {
143 | s = New(100000)
144 | s.FlipRange(0, 100000)
145 | b.ResetTimer()
146 | for i := 0; i < b.N; i++ {
147 | s.PreviousClear(99999)
148 | }
149 | })
150 |
151 | b.Run("DifferenceCardinality", func(b *testing.B) {
152 | empty := New(100000)
153 | b.ResetTimer()
154 | for i := 0; i < b.N; i++ {
155 | s.DifferenceCardinality(empty)
156 | }
157 | })
158 |
159 | b.Run("InPlaceDifference", func(b *testing.B) {
160 | s = s.Clone()
161 | b.ResetTimer()
162 | for i := 0; i < b.N; i++ {
163 | s.InPlaceDifference(cpy)
164 | }
165 | })
166 |
167 | b.Run("InPlaceUnion", func(b *testing.B) {
168 | s = s.Clone()
169 | b.ResetTimer()
170 | for i := 0; i < b.N; i++ {
171 | s.InPlaceUnion(cpy)
172 | }
173 | })
174 |
175 | b.Run("InPlaceIntersection", func(b *testing.B) {
176 | s = s.Clone()
177 | b.ResetTimer()
178 | for i := 0; i < b.N; i++ {
179 | s.InPlaceIntersection(cpy)
180 | }
181 | })
182 |
183 | b.Run("InPlaceSymmetricDifference", func(b *testing.B) {
184 | s = s.Clone()
185 | b.ResetTimer()
186 | for i := 0; i < b.N; i++ {
187 | s.InPlaceSymmetricDifference(cpy)
188 | }
189 | })
190 | }
191 |
192 | // go test -bench=LemireCreate
193 | // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/
194 | func BenchmarkLemireCreate(b *testing.B) {
195 | for i := 0; i < b.N; i++ {
196 | bitmap := New(0) // we force dynamic memory allocation
197 | for v := uint(0); v <= 100000000; v += 100 {
198 | bitmap.Set(v)
199 | }
200 | }
201 | }
202 |
203 | // go test -bench=LemireCount
204 | // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/
205 | func BenchmarkLemireCount(b *testing.B) {
206 | bitmap := New(100000000)
207 | for v := uint(0); v <= 100000000; v += 100 {
208 | bitmap.Set(v)
209 | }
210 | b.ResetTimer()
211 | sum := uint(0)
212 | for i := 0; i < b.N; i++ {
213 | sum += bitmap.Count()
214 | }
215 | if sum == 0 { // added just to fool ineffassign
216 | return
217 | }
218 | }
219 |
220 | // go test -bench=LemireIterate
221 | // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/
222 | func BenchmarkLemireIterate(b *testing.B) {
223 | bitmap := New(100000000)
224 | for v := uint(0); v <= 100000000; v += 100 {
225 | bitmap.Set(v)
226 | }
227 | b.ResetTimer()
228 | sum := uint(0)
229 | for i := 0; i < b.N; i++ {
230 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
231 | sum++
232 | }
233 | }
234 | if sum == 0 { // added just to fool ineffassign
235 | return
236 | }
237 | }
238 |
239 | // go test -bench=LemireIterateb
240 | // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/
241 | func BenchmarkLemireIterateb(b *testing.B) {
242 | bitmap := New(100000000)
243 | for v := uint(0); v <= 100000000; v += 100 {
244 | bitmap.Set(v)
245 | }
246 | b.ResetTimer()
247 | sum := uint(0)
248 | for i := 0; i < b.N; i++ {
249 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
250 | sum += j
251 | }
252 | }
253 |
254 | if sum == 0 { // added just to fool ineffassign
255 | return
256 | }
257 | }
258 |
259 | // go test -bench=BenchmarkLemireIterateManyb
260 | // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/
261 | func BenchmarkLemireIterateManyb(b *testing.B) {
262 | bitmap := New(100000000)
263 | for v := uint(0); v <= 100000000; v += 100 {
264 | bitmap.Set(v)
265 | }
266 | buffer := make([]uint, 256)
267 | b.ResetTimer()
268 | sum := uint(0)
269 | for i := 0; i < b.N; i++ {
270 | j := uint(0)
271 | j, buffer = bitmap.NextSetMany(j, buffer)
272 | for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) {
273 | for k := range buffer {
274 | sum += buffer[k]
275 | }
276 | j++
277 | }
278 | }
279 |
280 | if sum == 0 { // added just to fool ineffassign
281 | return
282 | }
283 | }
284 |
285 | func setRnd(bits []uint64, halfings int) {
286 | rnd := rand.NewSource(0).(rand.Source64)
287 | for i := range bits {
288 | bits[i] = 0xFFFFFFFFFFFFFFFF
289 | for j := 0; j < halfings; j++ {
290 | bits[i] &= rnd.Uint64()
291 | }
292 | }
293 | }
294 |
295 | // go test -bench=BenchmarkFlorianUekermannIterateMany
296 | func BenchmarkFlorianUekermannIterateMany(b *testing.B) {
297 | input := make([]uint64, 68)
298 | setRnd(input, 4)
299 | bitmap := From(input)
300 | buffer := make([]uint, 256)
301 | b.ResetTimer()
302 | checksum := uint(0)
303 | for i := 0; i < b.N; i++ {
304 | last, batch := bitmap.NextSetMany(0, buffer)
305 | for len(batch) > 0 {
306 | for _, idx := range batch {
307 | checksum += idx
308 | }
309 | last, batch = bitmap.NextSetMany(last+1, batch)
310 | }
311 | }
312 | if checksum == 0 { // added just to fool ineffassign
313 | return
314 | }
315 | }
316 |
317 | func BenchmarkFlorianUekermannIterateManyReg(b *testing.B) {
318 | input := make([]uint64, 68)
319 | setRnd(input, 4)
320 | bitmap := From(input)
321 | b.ResetTimer()
322 | checksum := uint(0)
323 | for i := 0; i < b.N; i++ {
324 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
325 | checksum += j
326 | }
327 | }
328 | if checksum == 0 { // added just to fool ineffassign
329 | return
330 | }
331 | }
332 |
333 | // function provided by FlorianUekermann
334 | func good(set []uint64) (checksum uint) {
335 | for wordIdx, word := range set {
336 | wordIdx := uint(wordIdx * 64)
337 | for word != 0 {
338 | bitIdx := uint(bits.TrailingZeros64(word))
339 | word ^= 1 << bitIdx
340 | index := wordIdx + bitIdx
341 | checksum += index
342 | }
343 | }
344 | return checksum
345 | }
346 |
347 | func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) {
348 | input := make([]uint64, 68)
349 | setRnd(input, 4)
350 | b.ResetTimer()
351 | checksum := uint(0)
352 | for i := 0; i < b.N; i++ {
353 | checksum += good(input)
354 | }
355 | if checksum == 0 { // added just to fool ineffassign
356 | return
357 | }
358 | }
359 |
360 | /////// Mid density
361 |
362 | // go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany
363 | func BenchmarkFlorianUekermannLowDensityIterateMany(b *testing.B) {
364 | input := make([]uint64, 1000000)
365 | rnd := rand.NewSource(0).(rand.Source64)
366 | for i := 0; i < 50000; i++ {
367 | input[rnd.Uint64()%1000000] = 1
368 | }
369 | bitmap := From(input)
370 | buffer := make([]uint, 256)
371 | b.ResetTimer()
372 | sum := uint(0)
373 | for i := 0; i < b.N; i++ {
374 | j := uint(0)
375 | j, buffer = bitmap.NextSetMany(j, buffer)
376 | for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) {
377 | for k := range buffer {
378 | sum += buffer[k]
379 | }
380 | j++
381 | }
382 | }
383 | if sum == 0 { // added just to fool ineffassign
384 | return
385 | }
386 | }
387 |
388 | func BenchmarkFlorianUekermannLowDensityIterateManyReg(b *testing.B) {
389 | input := make([]uint64, 1000000)
390 | rnd := rand.NewSource(0).(rand.Source64)
391 | for i := 0; i < 50000; i++ {
392 | input[rnd.Uint64()%1000000] = 1
393 | }
394 | bitmap := From(input)
395 | b.ResetTimer()
396 | checksum := uint(0)
397 | for i := 0; i < b.N; i++ {
398 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
399 | checksum += j
400 | }
401 | }
402 | if checksum == 0 { // added just to fool ineffassign
403 | return
404 | }
405 | }
406 |
407 | func BenchmarkFlorianUekermannLowDensityIterateManyComp(b *testing.B) {
408 | input := make([]uint64, 1000000)
409 | rnd := rand.NewSource(0).(rand.Source64)
410 | for i := 0; i < 50000; i++ {
411 | input[rnd.Uint64()%1000000] = 1
412 | }
413 | b.ResetTimer()
414 | checksum := uint(0)
415 | for i := 0; i < b.N; i++ {
416 | checksum += good(input)
417 | }
418 | if checksum == 0 { // added just to fool ineffassign
419 | return
420 | }
421 | }
422 |
423 | /////// Mid density
424 |
425 | // go test -bench=BenchmarkFlorianUekermannMidDensityIterateMany
426 | func BenchmarkFlorianUekermannMidDensityIterateMany(b *testing.B) {
427 | input := make([]uint64, 1000000)
428 | rnd := rand.NewSource(0).(rand.Source64)
429 | for i := 0; i < 3000000; i++ {
430 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
431 | }
432 | bitmap := From(input)
433 | buffer := make([]uint, 256)
434 | b.ResetTimer()
435 | sum := uint(0)
436 | for i := 0; i < b.N; i++ {
437 | j := uint(0)
438 | j, buffer = bitmap.NextSetMany(j, buffer)
439 | for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) {
440 | for k := range buffer {
441 | sum += buffer[k]
442 | }
443 | j++
444 | }
445 | }
446 |
447 | if sum == 0 { // added just to fool ineffassign
448 | return
449 | }
450 | }
451 |
452 | func BenchmarkFlorianUekermannMidDensityIterateManyReg(b *testing.B) {
453 | input := make([]uint64, 1000000)
454 | rnd := rand.NewSource(0).(rand.Source64)
455 | for i := 0; i < 3000000; i++ {
456 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
457 | }
458 | bitmap := From(input)
459 | b.ResetTimer()
460 | checksum := uint(0)
461 | for i := 0; i < b.N; i++ {
462 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
463 | checksum += j
464 | }
465 | }
466 | if checksum == 0 { // added just to fool ineffassign
467 | return
468 | }
469 | }
470 |
471 | func BenchmarkFlorianUekermannMidDensityIterateManyComp(b *testing.B) {
472 | input := make([]uint64, 1000000)
473 | rnd := rand.NewSource(0).(rand.Source64)
474 | for i := 0; i < 3000000; i++ {
475 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
476 | }
477 | b.ResetTimer()
478 | checksum := uint(0)
479 | for i := 0; i < b.N; i++ {
480 | checksum += good(input)
481 | }
482 | if checksum == 0 { // added just to fool ineffassign
483 | return
484 | }
485 | }
486 |
487 | ////////// High density
488 |
489 | func BenchmarkFlorianUekermannMidStrongDensityIterateMany(b *testing.B) {
490 | input := make([]uint64, 1000000)
491 | rnd := rand.NewSource(0).(rand.Source64)
492 | for i := 0; i < 20000000; i++ {
493 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
494 | }
495 | bitmap := From(input)
496 | buffer := make([]uint, 256)
497 | b.ResetTimer()
498 | sum := uint(0)
499 | for i := 0; i < b.N; i++ {
500 | j := uint(0)
501 | j, buffer = bitmap.NextSetMany(j, buffer)
502 | for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) {
503 | for k := range buffer {
504 | sum += buffer[k]
505 | }
506 | j++
507 | }
508 | }
509 |
510 | if sum == 0 { // added just to fool ineffassign
511 | return
512 | }
513 | }
514 |
515 | func BenchmarkFlorianUekermannMidStrongDensityIterateManyReg(b *testing.B) {
516 | input := make([]uint64, 1000000)
517 | rnd := rand.NewSource(0).(rand.Source64)
518 | for i := 0; i < 20000000; i++ {
519 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
520 | }
521 | bitmap := From(input)
522 | b.ResetTimer()
523 | checksum := uint(0)
524 | for i := 0; i < b.N; i++ {
525 | for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
526 | checksum += j
527 | }
528 | }
529 | if checksum == 0 { // added just to fool ineffassign
530 | return
531 | }
532 | }
533 |
534 | func BenchmarkFlorianUekermannMidStrongDensityIterateManyComp(b *testing.B) {
535 | input := make([]uint64, 1000000)
536 | rnd := rand.NewSource(0).(rand.Source64)
537 | for i := 0; i < 20000000; i++ {
538 | input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
539 | }
540 | b.ResetTimer()
541 | checksum := uint(0)
542 | for i := 0; i < b.N; i++ {
543 | checksum += good(input)
544 | }
545 | if checksum == 0 { // added just to fool ineffassign
546 | return
547 | }
548 | }
549 |
550 | func BenchmarkBitsetReadWrite(b *testing.B) {
551 | s := New(100000)
552 | for i := 0; i < 100000; i += 100 {
553 | s.Set(uint(i))
554 | }
555 | buffer := bytes.Buffer{}
556 | temp := New(100000)
557 | b.ResetTimer()
558 | for i := 0; i < b.N; i++ {
559 | s.WriteTo(&buffer)
560 | temp.ReadFrom(&buffer)
561 | buffer.Reset()
562 | }
563 | }
564 |
565 | func BenchmarkIsSuperSet(b *testing.B) {
566 | new := func(len int, density float64) *BitSet {
567 | r := rand.New(rand.NewSource(42))
568 | bs := New(uint(len))
569 | for i := 0; i < len; i++ {
570 | bs.SetTo(uint(i), r.Float64() < density)
571 | }
572 | return bs
573 | }
574 |
575 | bench := func(name string, lenS, lenSS int, density float64, overrideS, overrideSS map[int]bool, f func(*BitSet, *BitSet) bool) {
576 | s := new(lenS, density)
577 | ss := new(lenSS, density)
578 |
579 | for i, v := range overrideS {
580 | s.SetTo(uint(i), v)
581 | }
582 | for i, v := range overrideSS {
583 | ss.SetTo(uint(i), v)
584 | }
585 |
586 | b.Run(name, func(b *testing.B) {
587 | for i := 0; i < b.N; i++ {
588 | _ = f(ss, s)
589 | }
590 | })
591 | }
592 |
593 | f := func(ss, s *BitSet) bool {
594 | return ss.IsSuperSet(s)
595 | }
596 | fStrict := func(ss, s *BitSet) bool {
597 | return ss.IsStrictSuperSet(s)
598 | }
599 |
600 | for _, len := range []int{1, 10, 100, 1000, 10000, 100000} {
601 | density := 0.5
602 | bench(fmt.Sprintf("equal, len=%d", len),
603 | len, len, density, nil, nil, f)
604 | bench(fmt.Sprintf("equal, len=%d, strict", len),
605 | len, len, density, nil, nil, fStrict)
606 | }
607 |
608 | for _, density := range []float64{0, 0.05, 0.2, 0.8, 0.95, 1} {
609 | len := 10000
610 | bench(fmt.Sprintf("equal, density=%.2f", density),
611 | len, len, density, nil, nil, f)
612 | bench(fmt.Sprintf("equal, density=%.2f, strict", density),
613 | len, len, density, nil, nil, fStrict)
614 | }
615 |
616 | for _, diff := range []int{0, 100, 1000, 9999} {
617 | len := 10000
618 | density := 0.5
619 | overrideS := map[int]bool{diff: true}
620 | overrideSS := map[int]bool{diff: false}
621 | bench(fmt.Sprintf("subset, len=%d, diff=%d", len, diff),
622 | len, len, density, overrideS, overrideSS, f)
623 | bench(fmt.Sprintf("subset, len=%d, diff=%d, strict", len, diff),
624 | len, len, density, overrideS, overrideSS, fStrict)
625 | }
626 |
627 | for _, diff := range []int{0, 100, 1000, 9999} {
628 | len := 10000
629 | density := 0.5
630 | overrideS := map[int]bool{diff: false}
631 | overrideSS := map[int]bool{diff: true}
632 | bench(fmt.Sprintf("superset, len=%d, diff=%d", len, diff),
633 | len, len, density, overrideS, overrideSS, f)
634 | bench(fmt.Sprintf("superset, len=%d, diff=%d, strict", len, diff),
635 | len, len, density, overrideS, overrideSS, fStrict)
636 | }
637 | }
638 |
639 | // clear the right most bit (C-RMS)
640 | // test two different algorithms
641 | func BenchmarkClearRMS(b *testing.B) {
642 | var word uint64
643 |
644 | // cryptic
645 | b.Run("cryptic", func(b *testing.B) {
646 | word = 0xaaaa_aaaa_aaaa_aaaa
647 | for i := 0; i < b.N; i++ {
648 | t := word & ((^word) + 1)
649 | word = word ^ t
650 | }
651 | })
652 |
653 | // less cryptic
654 | b.Run("simple", func(b *testing.B) {
655 | word = 0xaaaa_aaaa_aaaa_aaaa
656 | for i := 0; i < b.N; i++ {
657 | word &= word - 1
658 | }
659 | })
660 | }
661 |
662 | // go test -bench=Rank
663 | func BenchmarkRank(b *testing.B) {
664 | s := New(100000)
665 | for i := 0; i < 100000; i += 100 {
666 | s.Set(uint(i))
667 | }
668 | for _, u := range []uint{1, 20, 50, 100, 200, 500, 1_000, 10_000, 20_000, 50_000, 100_000, 200_000} {
669 | b.Run(fmt.Sprintf("Rank(%d)", u), func(b *testing.B) {
670 | b.ResetTimer()
671 | for i := 0; i < b.N; i++ {
672 | _ = s.Rank(u)
673 | }
674 | })
675 | }
676 | }
677 |
--------------------------------------------------------------------------------
/bitset.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package bitset implements bitsets, a mapping
3 | between non-negative integers and boolean values. It should be more
4 | efficient than map[uint] bool.
5 |
6 | It provides methods for setting, clearing, flipping, and testing
7 | individual integers.
8 |
9 | But it also provides set intersection, union, difference,
10 | complement, and symmetric operations, as well as tests to
11 | check whether any, all, or no bits are set, and querying a
12 | bitset's current length and number of positive bits.
13 |
14 | BitSets are expanded to the size of the largest set bit; the
15 | memory allocation is approximately Max bits, where Max is
16 | the largest set bit. BitSets are never shrunk. On creation,
17 | a hint can be given for the number of bits that will be used.
18 |
19 | Many of the methods, including Set,Clear, and Flip, return
20 | a BitSet pointer, which allows for chaining.
21 |
22 | Example use:
23 |
24 | import "bitset"
25 | var b BitSet
26 | b.Set(10).Set(11)
27 | if b.Test(1000) {
28 | b.Clear(1000)
29 | }
30 | if B.Intersection(bitset.New(100).Set(10)).Count() > 1 {
31 | fmt.Println("Intersection works.")
32 | }
33 |
34 | As an alternative to BitSets, one should check out the 'big' package,
35 | which provides a (less set-theoretical) view of bitsets.
36 | */
37 | package bitset
38 |
39 | import (
40 | "bytes"
41 | "encoding/base64"
42 | "encoding/binary"
43 | "encoding/json"
44 | "errors"
45 | "fmt"
46 | "io"
47 | "math/bits"
48 | "strconv"
49 | )
50 |
51 | // the wordSize of a bit set
52 | const wordSize = 64
53 |
54 | // the wordSize of a bit set in bytes
55 | const wordBytes = wordSize / 8
56 |
57 | // wordMask is wordSize-1, used for bit indexing in a word
58 | const wordMask = wordSize - 1
59 |
60 | // log2WordSize is lg(wordSize)
61 | const log2WordSize = 6
62 |
63 | // allBits has every bit set
64 | const allBits uint64 = 0xffffffffffffffff
65 |
66 | // default binary BigEndian
67 | var binaryOrder binary.ByteOrder = binary.BigEndian
68 |
69 | // default json encoding base64.URLEncoding
70 | var base64Encoding = base64.URLEncoding
71 |
72 | // Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding)
73 | func Base64StdEncoding() { base64Encoding = base64.StdEncoding }
74 |
75 | // LittleEndian sets Marshal/Unmarshal Binary as Little Endian (Default: binary.BigEndian)
76 | func LittleEndian() { binaryOrder = binary.LittleEndian }
77 |
78 | // BigEndian sets Marshal/Unmarshal Binary as Big Endian (Default: binary.BigEndian)
79 | func BigEndian() { binaryOrder = binary.BigEndian }
80 |
81 | // BinaryOrder returns the current binary order, see also LittleEndian()
82 | // and BigEndian() to change the order.
83 | func BinaryOrder() binary.ByteOrder { return binaryOrder }
84 |
85 | // A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0.
86 | type BitSet struct {
87 | length uint
88 | set []uint64
89 | }
90 |
91 | // Error is used to distinguish errors (panics) generated in this package.
92 | type Error string
93 |
94 | // safeSet will fixup b.set to be non-nil and return the field value
95 | func (b *BitSet) safeSet() []uint64 {
96 | if b.set == nil {
97 | b.set = make([]uint64, wordsNeeded(0))
98 | }
99 | return b.set
100 | }
101 |
102 | // SetBitsetFrom fills the bitset with an array of integers without creating a new BitSet instance
103 | func (b *BitSet) SetBitsetFrom(buf []uint64) {
104 | b.length = uint(len(buf)) * 64
105 | b.set = buf
106 | }
107 |
108 | // From is a constructor used to create a BitSet from an array of words
109 | func From(buf []uint64) *BitSet {
110 | return FromWithLength(uint(len(buf))*64, buf)
111 | }
112 |
113 | // FromWithLength constructs from an array of words and length in bits.
114 | // This function is for advanced users, most users should prefer
115 | // the From function.
116 | // As a user of FromWithLength, you are responsible for ensuring
117 | // that the length is correct: your slice should have length at
118 | // least (length+63)/64 in 64-bit words.
119 | func FromWithLength(length uint, set []uint64) *BitSet {
120 | if len(set) < wordsNeeded(length) {
121 | panic("BitSet.FromWithLength: slice is too short")
122 | }
123 | return &BitSet{length, set}
124 | }
125 |
126 | // Bytes returns the bitset as array of 64-bit words, giving direct access to the internal representation.
127 | // It is not a copy, so changes to the returned slice will affect the bitset.
128 | // It is meant for advanced users.
129 | //
130 | // Deprecated: Bytes is deprecated. Use [BitSet.Words] instead.
131 | func (b *BitSet) Bytes() []uint64 {
132 | return b.set
133 | }
134 |
135 | // Words returns the bitset as array of 64-bit words, giving direct access to the internal representation.
136 | // It is not a copy, so changes to the returned slice will affect the bitset.
137 | // It is meant for advanced users.
138 | func (b *BitSet) Words() []uint64 {
139 | return b.set
140 | }
141 |
142 | // wordsNeeded calculates the number of words needed for i bits
143 | func wordsNeeded(i uint) int {
144 | if i > (Cap() - wordMask) {
145 | return int(Cap() >> log2WordSize)
146 | }
147 | return int((i + wordMask) >> log2WordSize)
148 | }
149 |
150 | // wordsNeededUnbound calculates the number of words needed for i bits, possibly exceeding the capacity.
151 | // This function is useful if you know that the capacity cannot be exceeded (e.g., you have an existing BitSet).
152 | func wordsNeededUnbound(i uint) int {
153 | return (int(i) + wordMask) >> log2WordSize
154 | }
155 |
156 | // wordsIndex calculates the index of words in a `uint64`
157 | func wordsIndex(i uint) uint {
158 | return i & wordMask
159 | }
160 |
161 | // New creates a new BitSet with a hint that length bits will be required.
162 | // The memory usage is at least length/8 bytes.
163 | // In case of allocation failure, the function will return a BitSet with zero
164 | // capacity.
165 | func New(length uint) (bset *BitSet) {
166 | defer func() {
167 | if r := recover(); r != nil {
168 | bset = &BitSet{
169 | 0,
170 | make([]uint64, 0),
171 | }
172 | }
173 | }()
174 |
175 | bset = &BitSet{
176 | length,
177 | make([]uint64, wordsNeeded(length)),
178 | }
179 |
180 | return bset
181 | }
182 |
183 | // MustNew creates a new BitSet with the given length bits.
184 | // It panics if length exceeds the possible capacity or by a lack of memory.
185 | func MustNew(length uint) (bset *BitSet) {
186 | if length >= Cap() {
187 | panic("You are exceeding the capacity")
188 | }
189 |
190 | return &BitSet{
191 | length,
192 | make([]uint64, wordsNeeded(length)), // may panic on lack of memory
193 | }
194 | }
195 |
196 | // Cap returns the total possible capacity, or number of bits
197 | // that can be stored in the BitSet theoretically. Under 32-bit system,
198 | // it is 4294967295 and under 64-bit system, it is 18446744073709551615.
199 | // Note that this is further limited by the maximum allocation size in Go,
200 | // and your available memory, as any Go data structure.
201 | func Cap() uint {
202 | return ^uint(0)
203 | }
204 |
205 | // Len returns the number of bits in the BitSet.
206 | // Note that it differ from Count function.
207 | func (b *BitSet) Len() uint {
208 | return b.length
209 | }
210 |
211 | // extendSet adds additional words to incorporate new bits if needed
212 | func (b *BitSet) extendSet(i uint) {
213 | if i >= Cap() {
214 | panic("You are exceeding the capacity")
215 | }
216 | nsize := wordsNeeded(i + 1)
217 | if b.set == nil {
218 | b.set = make([]uint64, nsize)
219 | } else if cap(b.set) >= nsize {
220 | b.set = b.set[:nsize] // fast resize
221 | } else if len(b.set) < nsize {
222 | newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x
223 | copy(newset, b.set)
224 | b.set = newset
225 | }
226 | b.length = i + 1
227 | }
228 |
229 | // Test whether bit i is set.
230 | func (b *BitSet) Test(i uint) bool {
231 | if i >= b.length {
232 | return false
233 | }
234 | return b.set[i>>log2WordSize]&(1<> log2WordSize)
240 | subWordIndex := wordsIndex(i)
241 |
242 | // The word that the index falls within, shifted so the index is at bit 0
243 | var firstWord, secondWord uint64
244 | if firstWordIndex < len(b.set) {
245 | firstWord = b.set[firstWordIndex] >> subWordIndex
246 | }
247 |
248 | // The next word, masked to only include the necessary bits and shifted to cover the
249 | // top of the word
250 | if (firstWordIndex + 1) < len(b.set) {
251 | secondWord = b.set[firstWordIndex+1] << uint64(wordSize-subWordIndex)
252 | }
253 |
254 | return firstWord | secondWord
255 | }
256 |
257 | // Set bit i to 1, the capacity of the bitset is automatically
258 | // increased accordingly.
259 | // Warning: using a very large value for 'i'
260 | // may lead to a memory shortage and a panic: the caller is responsible
261 | // for providing sensible parameters in line with their memory capacity.
262 | // The memory usage is at least slightly over i/8 bytes.
263 | func (b *BitSet) Set(i uint) *BitSet {
264 | if i >= b.length { // if we need more bits, make 'em
265 | b.extendSet(i)
266 | }
267 | b.set[i>>log2WordSize] |= 1 << wordsIndex(i)
268 | return b
269 | }
270 |
271 | // Clear bit i to 0. This never cause a memory allocation. It is always safe.
272 | func (b *BitSet) Clear(i uint) *BitSet {
273 | if i >= b.length {
274 | return b
275 | }
276 | b.set[i>>log2WordSize] &^= 1 << wordsIndex(i)
277 | return b
278 | }
279 |
280 | // SetTo sets bit i to value.
281 | // Warning: using a very large value for 'i'
282 | // may lead to a memory shortage and a panic: the caller is responsible
283 | // for providing sensible parameters in line with their memory capacity.
284 | func (b *BitSet) SetTo(i uint, value bool) *BitSet {
285 | if value {
286 | return b.Set(i)
287 | }
288 | return b.Clear(i)
289 | }
290 |
291 | // Flip bit at i.
292 | // Warning: using a very large value for 'i'
293 | // may lead to a memory shortage and a panic: the caller is responsible
294 | // for providing sensible parameters in line with their memory capacity.
295 | func (b *BitSet) Flip(i uint) *BitSet {
296 | if i >= b.length {
297 | return b.Set(i)
298 | }
299 | b.set[i>>log2WordSize] ^= 1 << wordsIndex(i)
300 | return b
301 | }
302 |
303 | // FlipRange bit in [start, end).
304 | // Warning: using a very large value for 'end'
305 | // may lead to a memory shortage and a panic: the caller is responsible
306 | // for providing sensible parameters in line with their memory capacity.
307 | func (b *BitSet) FlipRange(start, end uint) *BitSet {
308 | if start >= end {
309 | return b
310 | }
311 |
312 | if end-1 >= b.length { // if we need more bits, make 'em
313 | b.extendSet(end - 1)
314 | }
315 |
316 | startWord := int(start >> log2WordSize)
317 | endWord := int(end >> log2WordSize)
318 |
319 | // b.set[startWord] ^= ^(^uint64(0) << wordsIndex(start))
320 | // e.g:
321 | // start = 71,
322 | // startWord = 1
323 | // wordsIndex(start) = 71 % 64 = 7
324 | // (^uint64(0) << 7) = 0b111111....11110000000
325 | //
326 | // mask = ^(^uint64(0) << 7) = 0b000000....00001111111
327 | //
328 | // flips the first 7 bits in b.set[1] and
329 | // in the range loop, the b.set[1] gets again flipped
330 | // so the two expressions flip results in a flip
331 | // in b.set[1] from [7,63]
332 | //
333 | // handle startWord special, get's reflipped in range loop
334 | b.set[startWord] ^= ^(^uint64(0) << wordsIndex(start))
335 |
336 | for idx := range b.set[startWord:endWord] {
337 | b.set[startWord+idx] = ^b.set[startWord+idx]
338 | }
339 |
340 | // handle endWord special
341 | // e.g.
342 | // end = 135
343 | // endWord = 2
344 | //
345 | // wordsIndex(-7) = 57
346 | // see the golang spec:
347 | // "For unsigned integer values, the operations +, -, *, and << are computed
348 | // modulo 2n, where n is the bit width of the unsigned integer's type."
349 | //
350 | // mask = ^uint64(0) >> 57 = 0b00000....0001111111
351 | //
352 | // flips in b.set[2] from [0,7]
353 | //
354 | // is end at word boundary?
355 | if idx := wordsIndex(-end); idx != 0 {
356 | b.set[endWord] ^= ^uint64(0) >> wordsIndex(idx)
357 | }
358 |
359 | return b
360 | }
361 |
362 | // Shrink shrinks BitSet so that the provided value is the last possible
363 | // set value. It clears all bits > the provided index and reduces the size
364 | // and length of the set.
365 | //
366 | // Note that the parameter value is not the new length in bits: it is the
367 | // maximal value that can be stored in the bitset after the function call.
368 | // The new length in bits is the parameter value + 1. Thus it is not possible
369 | // to use this function to set the length to 0, the minimal value of the length
370 | // after this function call is 1.
371 | //
372 | // A new slice is allocated to store the new bits, so you may see an increase in
373 | // memory usage until the GC runs. Normally this should not be a problem, but if you
374 | // have an extremely large BitSet its important to understand that the old BitSet will
375 | // remain in memory until the GC frees it.
376 | // If you are memory constrained, this function may cause a panic.
377 | func (b *BitSet) Shrink(lastbitindex uint) *BitSet {
378 | length := lastbitindex + 1
379 | idx := wordsNeeded(length)
380 | if idx > len(b.set) {
381 | return b
382 | }
383 | shrunk := make([]uint64, idx)
384 | copy(shrunk, b.set[:idx])
385 | b.set = shrunk
386 | b.length = length
387 | lastWordUsedBits := length % 64
388 | if lastWordUsedBits != 0 {
389 | b.set[idx-1] &= allBits >> uint64(64-wordsIndex(lastWordUsedBits))
390 | }
391 | return b
392 | }
393 |
394 | // Compact shrinks BitSet to so that we preserve all set bits, while minimizing
395 | // memory usage. Compact calls Shrink.
396 | // A new slice is allocated to store the new bits, so you may see an increase in
397 | // memory usage until the GC runs. Normally this should not be a problem, but if you
398 | // have an extremely large BitSet its important to understand that the old BitSet will
399 | // remain in memory until the GC frees it.
400 | // If you are memory constrained, this function may cause a panic.
401 | func (b *BitSet) Compact() *BitSet {
402 | idx := len(b.set) - 1
403 | for ; idx >= 0 && b.set[idx] == 0; idx-- {
404 | }
405 | newlength := uint((idx + 1) << log2WordSize)
406 | if newlength >= b.length {
407 | return b // nothing to do
408 | }
409 | if newlength > 0 {
410 | return b.Shrink(newlength - 1)
411 | }
412 | // We preserve one word
413 | return b.Shrink(63)
414 | }
415 |
416 | // InsertAt takes an index which indicates where a bit should be
417 | // inserted. Then it shifts all the bits in the set to the left by 1, starting
418 | // from the given index position, and sets the index position to 0.
419 | //
420 | // Depending on the size of your BitSet, and where you are inserting the new entry,
421 | // this method could be extremely slow and in some cases might cause the entire BitSet
422 | // to be recopied.
423 | func (b *BitSet) InsertAt(idx uint) *BitSet {
424 | insertAtElement := idx >> log2WordSize
425 |
426 | // if length of set is a multiple of wordSize we need to allocate more space first
427 | if b.isLenExactMultiple() {
428 | b.set = append(b.set, uint64(0))
429 | }
430 |
431 | var i uint
432 | for i = uint(len(b.set) - 1); i > insertAtElement; i-- {
433 | // all elements above the position where we want to insert can simply by shifted
434 | b.set[i] <<= 1
435 |
436 | // we take the most significant bit of the previous element and set it as
437 | // the least significant bit of the current element
438 | b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63
439 | }
440 |
441 | // generate a mask to extract the data that we need to shift left
442 | // within the element where we insert a bit
443 | dataMask := uint64(1)< 0x40000 {
473 | buffer.WriteString("...")
474 | break
475 | }
476 | buffer.WriteString(strconv.FormatInt(int64(i), 10))
477 | i, e = b.NextSet(i + 1)
478 | if e {
479 | buffer.WriteString(",")
480 | }
481 | }
482 | buffer.WriteString("}")
483 | return buffer.String()
484 | }
485 |
486 | // DeleteAt deletes the bit at the given index position from
487 | // within the bitset
488 | // All the bits residing on the left of the deleted bit get
489 | // shifted right by 1
490 | // The running time of this operation may potentially be
491 | // relatively slow, O(length)
492 | func (b *BitSet) DeleteAt(i uint) *BitSet {
493 | // the index of the slice element where we'll delete a bit
494 | deleteAtElement := i >> log2WordSize
495 |
496 | // generate a mask for the data that needs to be shifted right
497 | // within that slice element that gets modified
498 | dataMask := ^((uint64(1) << wordsIndex(i)) - 1)
499 |
500 | // extract the data that we'll shift right from the slice element
501 | data := b.set[deleteAtElement] & dataMask
502 |
503 | // set the masked area to 0 while leaving the rest as it is
504 | b.set[deleteAtElement] &= ^dataMask
505 |
506 | // shift the previously extracted data to the right and then
507 | // set it in the previously masked area
508 | b.set[deleteAtElement] |= (data >> 1) & dataMask
509 |
510 | // loop over all the consecutive slice elements to copy each
511 | // lowest bit into the highest position of the previous element,
512 | // then shift the entire content to the right by 1
513 | for i := int(deleteAtElement) + 1; i < len(b.set); i++ {
514 | b.set[i-1] |= (b.set[i] & 1) << 63
515 | b.set[i] >>= 1
516 | }
517 |
518 | b.length = b.length - 1
519 |
520 | return b
521 | }
522 |
523 | // AppendTo appends all set bits to buf and returns the (maybe extended) buf.
524 | // In case of allocation failure, the function will panic.
525 | //
526 | // See also [BitSet.AsSlice] and [BitSet.NextSetMany].
527 | func (b *BitSet) AppendTo(buf []uint) []uint {
528 | // In theory, we could overflow uint, but in practice, we will not.
529 | for idx, word := range b.set {
530 | for word != 0 {
531 | // In theory idx<> log2WordSize)
576 | if x >= len(b.set) {
577 | return 0, false
578 | }
579 |
580 | // process first (partial) word
581 | word := b.set[x] >> wordsIndex(i)
582 | if word != 0 {
583 | return i + uint(bits.TrailingZeros64(word)), true
584 | }
585 |
586 | // process the following full words until next bit is set
587 | // x < len(b.set), no out-of-bounds panic in following slice expression
588 | x++
589 | for idx, word := range b.set[x:] {
590 | if word != 0 {
591 | return uint((x+idx)< 0; j, buffer = bitmap.NextSetMany(j,buffer) {
606 | // for k := range buffer {
607 | // do something with buffer[k]
608 | // }
609 | // j += 1
610 | // }
611 | //
612 | // It is possible to retrieve all set bits as follow:
613 | //
614 | // indices := make([]uint, bitmap.Count())
615 | // bitmap.NextSetMany(0, indices)
616 | //
617 | // It is also possible to retrieve all set bits with [BitSet.AppendTo]
618 | // or [BitSet.AsSlice].
619 | //
620 | // However if Count() is large, it might be preferable to
621 | // use several calls to NextSetMany for memory reasons.
622 | func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
623 | // In theory, we could overflow uint, but in practice, we will not.
624 | capacity := cap(buffer)
625 | result := buffer[:capacity]
626 |
627 | x := int(i >> log2WordSize)
628 | if x >= len(b.set) || capacity == 0 {
629 | return 0, result[:0]
630 | }
631 |
632 | // process first (partial) word
633 | word := b.set[x] >> wordsIndex(i)
634 |
635 | size := 0
636 | for word != 0 {
637 | result[size] = i + uint(bits.TrailingZeros64(word))
638 |
639 | size++
640 | if size == capacity {
641 | return result[size-1], result[:size]
642 | }
643 |
644 | // clear the rightmost set bit
645 | word &= word - 1
646 | }
647 |
648 | // process the following full words
649 | // x < len(b.set), no out-of-bounds panic in following slice expression
650 | x++
651 | for idx, word := range b.set[x:] {
652 | for word != 0 {
653 | result[size] = uint((x+idx)< 0 {
666 | return result[size-1], result[:size]
667 | }
668 | return 0, result[:0]
669 | }
670 |
671 | // NextClear returns the next clear bit from the specified index,
672 | // including possibly the current index
673 | // along with an error code (true = valid, false = no bit found i.e. all bits are set)
674 | func (b *BitSet) NextClear(i uint) (uint, bool) {
675 | x := int(i >> log2WordSize)
676 | if x >= len(b.set) {
677 | return 0, false
678 | }
679 |
680 | // process first (maybe partial) word
681 | word := b.set[x]
682 | word = word >> wordsIndex(i)
683 | wordAll := allBits >> wordsIndex(i)
684 |
685 | index := i + uint(bits.TrailingZeros64(^word))
686 | if word != wordAll && index < b.length {
687 | return index, true
688 | }
689 |
690 | // process the following full words until next bit is cleared
691 | // x < len(b.set), no out-of-bounds panic in following slice expression
692 | x++
693 | for idx, word := range b.set[x:] {
694 | if word != allBits {
695 | index = uint((x+idx)*wordSize + bits.TrailingZeros64(^word))
696 | if index < b.length {
697 | return index, true
698 | }
699 | }
700 | }
701 |
702 | return 0, false
703 | }
704 |
705 | // PreviousSet returns the previous set bit from the specified index,
706 | // including possibly the current index
707 | // along with an error code (true = valid, false = no bit found i.e. all bits are clear)
708 | func (b *BitSet) PreviousSet(i uint) (uint, bool) {
709 | x := int(i >> log2WordSize)
710 | if x >= len(b.set) {
711 | return 0, false
712 | }
713 | word := b.set[x]
714 |
715 | // Clear the bits above the index
716 | word = word & ((1 << (wordsIndex(i) + 1)) - 1)
717 | if word != 0 {
718 | return uint(x<= 0; x-- {
722 | word = b.set[x]
723 | if word != 0 {
724 | return uint(x<> log2WordSize)
735 | if x >= len(b.set) {
736 | return 0, false
737 | }
738 | word := b.set[x]
739 |
740 | // Flip all bits and find the highest one bit
741 | word = ^word
742 |
743 | // Clear the bits above the index
744 | word = word & ((1 << (wordsIndex(i) + 1)) - 1)
745 |
746 | if word != 0 {
747 | return uint(x<= 0; x-- {
751 | word = b.set[x]
752 | word = ^word
753 | if word != 0 {
754 | return uint(x< b.wordCount() {
891 | l = b.wordCount()
892 | }
893 | for i := 0; i < l; i++ {
894 | result.set[i] = b.set[i] &^ compare.set[i]
895 | }
896 | return
897 | }
898 |
899 | // DifferenceCardinality computes the cardinality of the difference
900 | func (b *BitSet) DifferenceCardinality(compare *BitSet) uint {
901 | panicIfNull(b)
902 | panicIfNull(compare)
903 | l := compare.wordCount()
904 | if l > b.wordCount() {
905 | l = b.wordCount()
906 | }
907 | cnt := uint64(0)
908 | if l > 0 {
909 | cnt += popcntMaskSlice(b.set[:l], compare.set[:l])
910 | }
911 | cnt += popcntSlice(b.set[l:])
912 | return uint(cnt)
913 | }
914 |
915 | // InPlaceDifference computes the difference of base set and other set
916 | // This is the BitSet equivalent of &^ (and not)
917 | func (b *BitSet) InPlaceDifference(compare *BitSet) {
918 | panicIfNull(b)
919 | panicIfNull(compare)
920 | l := compare.wordCount()
921 | if l > b.wordCount() {
922 | l = b.wordCount()
923 | }
924 | if l <= 0 {
925 | return
926 | }
927 | // bounds check elimination
928 | data, cmpData := b.set, compare.set
929 | _ = data[l-1]
930 | _ = cmpData[l-1]
931 | for i := 0; i < l; i++ {
932 | data[i] &^= cmpData[i]
933 | }
934 | }
935 |
936 | // Convenience function: return two bitsets ordered by
937 | // increasing length. Note: neither can be nil
938 | func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) {
939 | if a.length <= b.length {
940 | ap, bp = a, b
941 | } else {
942 | ap, bp = b, a
943 | }
944 | return
945 | }
946 |
947 | // Intersection of base set and other set
948 | // This is the BitSet equivalent of & (and)
949 | // In case of allocation failure, the function will return an empty BitSet.
950 | func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) {
951 | panicIfNull(b)
952 | panicIfNull(compare)
953 | b, compare = sortByLength(b, compare)
954 | result = New(b.length)
955 | for i, word := range b.set {
956 | result.set[i] = word & compare.set[i]
957 | }
958 | return
959 | }
960 |
961 | // IntersectionCardinality computes the cardinality of the intersection
962 | func (b *BitSet) IntersectionCardinality(compare *BitSet) uint {
963 | panicIfNull(b)
964 | panicIfNull(compare)
965 | if b.length == 0 || compare.length == 0 {
966 | return 0
967 | }
968 | b, compare = sortByLength(b, compare)
969 | cnt := popcntAndSlice(b.set, compare.set)
970 | return uint(cnt)
971 | }
972 |
973 | // InPlaceIntersection destructively computes the intersection of
974 | // base set and the compare set.
975 | // This is the BitSet equivalent of & (and)
976 | func (b *BitSet) InPlaceIntersection(compare *BitSet) {
977 | panicIfNull(b)
978 | panicIfNull(compare)
979 | l := compare.wordCount()
980 | if l > b.wordCount() {
981 | l = b.wordCount()
982 | }
983 | if l > 0 {
984 | // bounds check elimination
985 | data, cmpData := b.set, compare.set
986 | _ = data[l-1]
987 | _ = cmpData[l-1]
988 |
989 | for i := 0; i < l; i++ {
990 | data[i] &= cmpData[i]
991 | }
992 | }
993 | if l >= 0 {
994 | for i := l; i < len(b.set); i++ {
995 | b.set[i] = 0
996 | }
997 | }
998 | if compare.length > 0 {
999 | if compare.length-1 >= b.length {
1000 | b.extendSet(compare.length - 1)
1001 | }
1002 | }
1003 | }
1004 |
1005 | // Union of base set and other set
1006 | // This is the BitSet equivalent of | (or)
1007 | func (b *BitSet) Union(compare *BitSet) (result *BitSet) {
1008 | panicIfNull(b)
1009 | panicIfNull(compare)
1010 | b, compare = sortByLength(b, compare)
1011 | result = compare.Clone()
1012 | for i, word := range b.set {
1013 | result.set[i] = word | compare.set[i]
1014 | }
1015 | return
1016 | }
1017 |
1018 | // UnionCardinality computes the cardinality of the uniton of the base set
1019 | // and the compare set.
1020 | func (b *BitSet) UnionCardinality(compare *BitSet) uint {
1021 | panicIfNull(b)
1022 | panicIfNull(compare)
1023 | b, compare = sortByLength(b, compare)
1024 | cnt := uint64(0)
1025 | if len(b.set) > 0 {
1026 | cnt += popcntOrSlice(b.set, compare.set)
1027 | }
1028 | if len(compare.set) > len(b.set) {
1029 | cnt += popcntSlice(compare.set[len(b.set):])
1030 | }
1031 | return uint(cnt)
1032 | }
1033 |
1034 | // InPlaceUnion creates the destructive union of base set and compare set.
1035 | // This is the BitSet equivalent of | (or).
1036 | func (b *BitSet) InPlaceUnion(compare *BitSet) {
1037 | panicIfNull(b)
1038 | panicIfNull(compare)
1039 | l := compare.wordCount()
1040 | if l > b.wordCount() {
1041 | l = b.wordCount()
1042 | }
1043 | if compare.length > 0 && compare.length-1 >= b.length {
1044 | b.extendSet(compare.length - 1)
1045 | }
1046 | if l > 0 {
1047 | // bounds check elimination
1048 | data, cmpData := b.set, compare.set
1049 | _ = data[l-1]
1050 | _ = cmpData[l-1]
1051 |
1052 | for i := 0; i < l; i++ {
1053 | data[i] |= cmpData[i]
1054 | }
1055 | }
1056 | if len(compare.set) > l {
1057 | for i := l; i < len(compare.set); i++ {
1058 | b.set[i] = compare.set[i]
1059 | }
1060 | }
1061 | }
1062 |
1063 | // SymmetricDifference of base set and other set
1064 | // This is the BitSet equivalent of ^ (xor)
1065 | func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
1066 | panicIfNull(b)
1067 | panicIfNull(compare)
1068 | b, compare = sortByLength(b, compare)
1069 | // compare is bigger, so clone it
1070 | result = compare.Clone()
1071 | for i, word := range b.set {
1072 | result.set[i] = word ^ compare.set[i]
1073 | }
1074 | return
1075 | }
1076 |
1077 | // SymmetricDifferenceCardinality computes the cardinality of the symmetric difference
1078 | func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint {
1079 | panicIfNull(b)
1080 | panicIfNull(compare)
1081 | b, compare = sortByLength(b, compare)
1082 | cnt := uint64(0)
1083 | if len(b.set) > 0 {
1084 | cnt += popcntXorSlice(b.set, compare.set)
1085 | }
1086 | if len(compare.set) > len(b.set) {
1087 | cnt += popcntSlice(compare.set[len(b.set):])
1088 | }
1089 | return uint(cnt)
1090 | }
1091 |
1092 | // InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set
1093 | // This is the BitSet equivalent of ^ (xor)
1094 | func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) {
1095 | panicIfNull(b)
1096 | panicIfNull(compare)
1097 | l := compare.wordCount()
1098 | if l > b.wordCount() {
1099 | l = b.wordCount()
1100 | }
1101 | if compare.length > 0 && compare.length-1 >= b.length {
1102 | b.extendSet(compare.length - 1)
1103 | }
1104 | if l > 0 {
1105 | // bounds check elimination
1106 | data, cmpData := b.set, compare.set
1107 | _ = data[l-1]
1108 | _ = cmpData[l-1]
1109 | for i := 0; i < l; i++ {
1110 | data[i] ^= cmpData[i]
1111 | }
1112 | }
1113 | if len(compare.set) > l {
1114 | for i := l; i < len(compare.set); i++ {
1115 | b.set[i] = compare.set[i]
1116 | }
1117 | }
1118 | }
1119 |
1120 | // Is the length an exact multiple of word sizes?
1121 | func (b *BitSet) isLenExactMultiple() bool {
1122 | return wordsIndex(b.length) == 0
1123 | }
1124 |
1125 | // Clean last word by setting unused bits to 0
1126 | func (b *BitSet) cleanLastWord() {
1127 | if !b.isLenExactMultiple() {
1128 | b.set[len(b.set)-1] &= allBits >> (wordSize - wordsIndex(b.length))
1129 | }
1130 | }
1131 |
1132 | // Complement computes the (local) complement of a bitset (up to length bits)
1133 | // In case of allocation failure, the function will return an empty BitSet.
1134 | func (b *BitSet) Complement() (result *BitSet) {
1135 | panicIfNull(b)
1136 | result = New(b.length)
1137 | for i, word := range b.set {
1138 | result.set[i] = ^word
1139 | }
1140 | result.cleanLastWord()
1141 | return
1142 | }
1143 |
1144 | // All returns true if all bits are set, false otherwise. Returns true for
1145 | // empty sets.
1146 | func (b *BitSet) All() bool {
1147 | panicIfNull(b)
1148 | return b.Count() == b.length
1149 | }
1150 |
1151 | // None returns true if no bit is set, false otherwise. Returns true for
1152 | // empty sets.
1153 | func (b *BitSet) None() bool {
1154 | panicIfNull(b)
1155 | if b != nil && b.set != nil {
1156 | for _, word := range b.set {
1157 | if word > 0 {
1158 | return false
1159 | }
1160 | }
1161 | }
1162 | return true
1163 | }
1164 |
1165 | // Any returns true if any bit is set, false otherwise
1166 | func (b *BitSet) Any() bool {
1167 | panicIfNull(b)
1168 | return !b.None()
1169 | }
1170 |
1171 | // IsSuperSet returns true if this is a superset of the other set
1172 | func (b *BitSet) IsSuperSet(other *BitSet) bool {
1173 | l := other.wordCount()
1174 | if b.wordCount() < l {
1175 | l = b.wordCount()
1176 | }
1177 | for i, word := range other.set[:l] {
1178 | if b.set[i]&word != word {
1179 | return false
1180 | }
1181 | }
1182 | return popcntSlice(other.set[l:]) == 0
1183 | }
1184 |
1185 | // IsStrictSuperSet returns true if this is a strict superset of the other set
1186 | func (b *BitSet) IsStrictSuperSet(other *BitSet) bool {
1187 | return b.Count() > other.Count() && b.IsSuperSet(other)
1188 | }
1189 |
1190 | // DumpAsBits dumps a bit set as a string of bits. Following the usual convention in Go,
1191 | // the least significant bits are printed last (index 0 is at the end of the string).
1192 | // This is useful for debugging and testing. It is not suitable for serialization.
1193 | func (b *BitSet) DumpAsBits() string {
1194 | if b.set == nil {
1195 | return "."
1196 | }
1197 | buffer := bytes.NewBufferString("")
1198 | i := len(b.set) - 1
1199 | for ; i >= 0; i-- {
1200 | fmt.Fprintf(buffer, "%064b.", b.set[i])
1201 | }
1202 | return buffer.String()
1203 | }
1204 |
1205 | // BinaryStorageSize returns the binary storage requirements (see WriteTo) in bytes.
1206 | func (b *BitSet) BinaryStorageSize() int {
1207 | return wordBytes + wordBytes*b.wordCount()
1208 | }
1209 |
1210 | func readUint64Array(reader io.Reader, data []uint64) error {
1211 | length := len(data)
1212 | bufferSize := 128
1213 | buffer := make([]byte, bufferSize*wordBytes)
1214 | for i := 0; i < length; i += bufferSize {
1215 | end := i + bufferSize
1216 | if end > length {
1217 | end = length
1218 | buffer = buffer[:wordBytes*(end-i)]
1219 | }
1220 | chunk := data[i:end]
1221 | if _, err := io.ReadFull(reader, buffer); err != nil {
1222 | return err
1223 | }
1224 | for i := range chunk {
1225 | chunk[i] = uint64(binaryOrder.Uint64(buffer[8*i:]))
1226 | }
1227 | }
1228 | return nil
1229 | }
1230 |
1231 | func writeUint64Array(writer io.Writer, data []uint64) error {
1232 | bufferSize := 128
1233 | buffer := make([]byte, bufferSize*wordBytes)
1234 | for i := 0; i < len(data); i += bufferSize {
1235 | end := i + bufferSize
1236 | if end > len(data) {
1237 | end = len(data)
1238 | buffer = buffer[:wordBytes*(end-i)]
1239 | }
1240 | chunk := data[i:end]
1241 | for i, x := range chunk {
1242 | binaryOrder.PutUint64(buffer[8*i:], x)
1243 | }
1244 | _, err := writer.Write(buffer)
1245 | if err != nil {
1246 | return err
1247 | }
1248 | }
1249 | return nil
1250 | }
1251 |
1252 | // WriteTo writes a BitSet to a stream. The format is:
1253 | // 1. uint64 length
1254 | // 2. []uint64 set
1255 | // The length is the number of bits in the BitSet.
1256 | //
1257 | // The set is a slice of uint64s containing between length and length + 63 bits.
1258 | // It is interpreted as a big-endian array of uint64s by default (see BinaryOrder())
1259 | // meaning that the first 8 bits are stored at byte index 7, the next 8 bits are stored
1260 | // at byte index 6... the bits 64 to 71 are stored at byte index 8, etc.
1261 | // If you change the binary order, you need to do so for both reading and writing.
1262 | // We recommend using the default binary order.
1263 | //
1264 | // Upon success, the number of bytes written is returned.
1265 | //
1266 | // Performance: if this function is used to write to a disk or network
1267 | // connection, it might be beneficial to wrap the stream in a bufio.Writer.
1268 | // E.g.,
1269 | //
1270 | // f, err := os.Create("myfile")
1271 | // w := bufio.NewWriter(f)
1272 | func (b *BitSet) WriteTo(stream io.Writer) (int64, error) {
1273 | length := uint64(b.length)
1274 | // Write length
1275 | err := binary.Write(stream, binaryOrder, &length)
1276 | if err != nil {
1277 | // Upon failure, we do not guarantee that we
1278 | // return the number of bytes written.
1279 | return int64(0), err
1280 | }
1281 | err = writeUint64Array(stream, b.set[:b.wordCount()])
1282 | if err != nil {
1283 | // Upon failure, we do not guarantee that we
1284 | // return the number of bytes written.
1285 | return int64(wordBytes), err
1286 | }
1287 | return int64(b.BinaryStorageSize()), nil
1288 | }
1289 |
1290 | // ReadFrom reads a BitSet from a stream written using WriteTo
1291 | // The format is:
1292 | // 1. uint64 length
1293 | // 2. []uint64 set
1294 | // See WriteTo for details.
1295 | // Upon success, the number of bytes read is returned.
1296 | // If the current BitSet is not large enough to hold the data,
1297 | // it is extended. In case of error, the BitSet is either
1298 | // left unchanged or made empty if the error occurs too late
1299 | // to preserve the content.
1300 | //
1301 | // Performance: if this function is used to read from a disk or network
1302 | // connection, it might be beneficial to wrap the stream in a bufio.Reader.
1303 | // E.g.,
1304 | //
1305 | // f, err := os.Open("myfile")
1306 | // r := bufio.NewReader(f)
1307 | func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) {
1308 | var length uint64
1309 | err := binary.Read(stream, binaryOrder, &length)
1310 | if err != nil {
1311 | if err == io.EOF {
1312 | err = io.ErrUnexpectedEOF
1313 | }
1314 | return 0, err
1315 | }
1316 | newlength := uint(length)
1317 |
1318 | if uint64(newlength) != length {
1319 | return 0, errors.New("unmarshalling error: type mismatch")
1320 | }
1321 | nWords := wordsNeeded(uint(newlength))
1322 | if cap(b.set) >= nWords {
1323 | b.set = b.set[:nWords]
1324 | } else {
1325 | b.set = make([]uint64, nWords)
1326 | }
1327 |
1328 | b.length = newlength
1329 |
1330 | err = readUint64Array(stream, b.set)
1331 | if err != nil {
1332 | if err == io.EOF {
1333 | err = io.ErrUnexpectedEOF
1334 | }
1335 | // We do not want to leave the BitSet partially filled as
1336 | // it is error prone.
1337 | b.set = b.set[:0]
1338 | b.length = 0
1339 | return 0, err
1340 | }
1341 |
1342 | return int64(b.BinaryStorageSize()), nil
1343 | }
1344 |
1345 | // MarshalBinary encodes a BitSet into a binary form and returns the result.
1346 | // Please see WriteTo for details.
1347 | func (b *BitSet) MarshalBinary() ([]byte, error) {
1348 | var buf bytes.Buffer
1349 | _, err := b.WriteTo(&buf)
1350 | if err != nil {
1351 | return []byte{}, err
1352 | }
1353 |
1354 | return buf.Bytes(), err
1355 | }
1356 |
1357 | // UnmarshalBinary decodes the binary form generated by MarshalBinary.
1358 | // Please see WriteTo for details.
1359 | func (b *BitSet) UnmarshalBinary(data []byte) error {
1360 | buf := bytes.NewReader(data)
1361 | _, err := b.ReadFrom(buf)
1362 | return err
1363 | }
1364 |
1365 | // MarshalJSON marshals a BitSet as a JSON structure
1366 | func (b BitSet) MarshalJSON() ([]byte, error) {
1367 | buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize()))
1368 | _, err := b.WriteTo(buffer)
1369 | if err != nil {
1370 | return nil, err
1371 | }
1372 |
1373 | // URLEncode all bytes
1374 | return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes()))
1375 | }
1376 |
1377 | // UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON
1378 | func (b *BitSet) UnmarshalJSON(data []byte) error {
1379 | // Unmarshal as string
1380 | var s string
1381 | err := json.Unmarshal(data, &s)
1382 | if err != nil {
1383 | return err
1384 | }
1385 |
1386 | // URLDecode string
1387 | buf, err := base64Encoding.DecodeString(s)
1388 | if err != nil {
1389 | return err
1390 | }
1391 |
1392 | _, err = b.ReadFrom(bytes.NewReader(buf))
1393 | return err
1394 | }
1395 |
1396 | // Rank returns the number of set bits up to and including the index
1397 | // that are set in the bitset.
1398 | // See https://en.wikipedia.org/wiki/Ranking#Ranking_in_statistics
1399 | func (b *BitSet) Rank(index uint) (rank uint) {
1400 | index++ // Rank is up to and including
1401 |
1402 | // needed more than once
1403 | length := len(b.set)
1404 |
1405 | // TODO: built-in min requires go1.21 or later
1406 | // idx := min(int(index>>6), len(b.set))
1407 | idx := int(index >> 6)
1408 | if idx > length {
1409 | idx = length
1410 | }
1411 |
1412 | // sum up the popcounts until idx ...
1413 | // TODO: cannot range over idx (...): requires go1.22 or later
1414 | // for j := range idx {
1415 | for j := 0; j < idx; j++ {
1416 | if w := b.set[j]; w != 0 {
1417 | rank += uint(bits.OnesCount64(w))
1418 | }
1419 | }
1420 |
1421 | // ... plus partial word at idx,
1422 | // make Rank inlineable and faster in the end
1423 | // don't test index&63 != 0, just add, less branching
1424 | if idx < length {
1425 | rank += uint(bits.OnesCount64(b.set[idx] << (64 - index&63)))
1426 | }
1427 |
1428 | return
1429 | }
1430 |
1431 | // Select returns the index of the jth set bit, where j is the argument.
1432 | // The caller is responsible to ensure that 0 <= j < Count(): when j is
1433 | // out of range, the function returns the length of the bitset (b.length).
1434 | //
1435 | // Note that this function differs in convention from the Rank function which
1436 | // returns 1 when ranking the smallest value. We follow the conventional
1437 | // textbook definition of Select and Rank.
1438 | func (b *BitSet) Select(index uint) uint {
1439 | leftover := index
1440 | for idx, word := range b.set {
1441 | w := uint(bits.OnesCount64(word))
1442 | if w > leftover {
1443 | return uint(idx)*64 + select64(word, leftover)
1444 | }
1445 | leftover -= w
1446 | }
1447 | return b.length
1448 | }
1449 |
1450 | // top detects the top bit set
1451 | func (b *BitSet) top() (uint, bool) {
1452 | for idx := len(b.set) - 1; idx >= 0; idx-- {
1453 | if word := b.set[idx]; word != 0 {
1454 | return uint(idx<= b.length {
1492 | b.length = top + bits + 1
1493 | }
1494 |
1495 | pad, idx := top%wordSize, top>>log2WordSize
1496 | shift, pages := bits%wordSize, bits>>log2WordSize
1497 | if bits%wordSize == 0 { // happy case: just add pages
1498 | copy(dst[pages:nsize], b.set)
1499 | } else {
1500 | if pad+shift >= wordSize {
1501 | dst[idx+pages+1] = b.set[idx] >> (wordSize - shift)
1502 | }
1503 |
1504 | for i := int(idx); i >= 0; i-- {
1505 | if i > 0 {
1506 | dst[i+int(pages)] = (b.set[i] << shift) | (b.set[i-1] >> (wordSize - shift))
1507 | } else {
1508 | dst[i+int(pages)] = b.set[i] << shift
1509 | }
1510 | }
1511 | }
1512 |
1513 | // zeroing extra pages
1514 | for i := 0; i < int(pages); i++ {
1515 | dst[i] = 0
1516 | }
1517 |
1518 | b.set = dst
1519 | }
1520 |
1521 | // ShiftRight shifts the bitset like >> operation would do.
1522 | func (b *BitSet) ShiftRight(bits uint) {
1523 | panicIfNull(b)
1524 |
1525 | if bits == 0 {
1526 | return
1527 | }
1528 |
1529 | top, ok := b.top()
1530 | if !ok {
1531 | return
1532 | }
1533 |
1534 | if bits > top {
1535 | b.set = make([]uint64, wordsNeeded(b.length))
1536 | return
1537 | }
1538 |
1539 | pad, idx := top%wordSize, top>>log2WordSize
1540 | shift, pages := bits%wordSize, bits>>log2WordSize
1541 | if bits%wordSize == 0 { // happy case: just clear pages
1542 | b.set = b.set[pages:]
1543 | b.length -= pages * wordSize
1544 | } else {
1545 | for i := 0; i <= int(idx-pages); i++ {
1546 | if i < int(idx-pages) {
1547 | b.set[i] = (b.set[i+int(pages)] >> shift) | (b.set[i+int(pages)+1] << (wordSize - shift))
1548 | } else {
1549 | b.set[i] = b.set[i+int(pages)] >> shift
1550 | }
1551 | }
1552 |
1553 | if pad < shift {
1554 | b.set[int(idx-pages)] = 0
1555 | }
1556 | }
1557 |
1558 | for i := int(idx-pages) + 1; i < len(b.set); i++ {
1559 | b.set[i] = 0
1560 | }
1561 | }
1562 |
1563 | // OnesBetween returns the number of set bits in the range [from, to).
1564 | // The range is inclusive of 'from' and exclusive of 'to'.
1565 | // Returns 0 if from >= to.
1566 | func (b *BitSet) OnesBetween(from, to uint) uint {
1567 | panicIfNull(b)
1568 |
1569 | if from >= to {
1570 | return 0
1571 | }
1572 |
1573 | // Calculate indices and masks for the starting and ending words
1574 | startWord := from >> log2WordSize // Divide by wordSize
1575 | endWord := to >> log2WordSize
1576 | startOffset := from & wordMask // Mod wordSize
1577 | endOffset := to & wordMask
1578 |
1579 | // Case 1: Bits lie within a single word
1580 | if startWord == endWord {
1581 | // Create mask for bits between from and to
1582 | mask := uint64((1<= startOffset
1591 | count = uint(bits.OnesCount64(b.set[startWord] & startMask))
1592 |
1593 | // 2b: Count all bits in complete words between start and end
1594 | if endWord > startWord+1 {
1595 | count += uint(popcntSlice(b.set[startWord+1 : endWord]))
1596 | }
1597 |
1598 | // 2c: Count bits in last word (from start of word to endOffset)
1599 | if endOffset > 0 {
1600 | endMask := uint64(1<> log2WordSize
1655 | bitOffset := outPos & wordMask
1656 |
1657 | // Write extracted bits, handling word boundary crossing
1658 | dst.set[wordIdx] |= extracted << bitOffset
1659 | if bitOffset+bitsExtracted > wordSize {
1660 | dst.set[wordIdx+1] = extracted >> (wordSize - bitOffset)
1661 | }
1662 |
1663 | outPos += bitsExtracted
1664 | }
1665 | }
1666 |
1667 | // Deposit creates a new BitSet and deposits bits according to a mask.
1668 | // See DepositTo for details.
1669 | func (b *BitSet) Deposit(mask *BitSet) *BitSet {
1670 | dst := New(mask.length)
1671 | b.DepositTo(mask, dst)
1672 | return dst
1673 | }
1674 |
1675 | // DepositTo spreads bits from a compacted form in the BitSet into positions
1676 | // specified by mask in dst. This is the inverse operation of Extract.
1677 | //
1678 | // For example, if mask has bits set at positions 1,4,5, then DepositTo will
1679 | // take consecutive bits 0,1,2 from the source BitSet and place them into
1680 | // positions 1,4,5 in the destination BitSet.
1681 | func (b *BitSet) DepositTo(mask *BitSet, dst *BitSet) {
1682 | panicIfNull(b)
1683 | panicIfNull(mask)
1684 | panicIfNull(dst)
1685 |
1686 | if len(dst.set) == 0 || len(mask.set) == 0 || len(b.set) == 0 {
1687 | return
1688 | }
1689 |
1690 | inPos := uint(0)
1691 | length := len(mask.set)
1692 | if len(dst.set) < length {
1693 | length = len(dst.set)
1694 | }
1695 |
1696 | // Process each word
1697 | for i := 0; i < length; i++ {
1698 | if mask.set[i] == 0 {
1699 | continue // Skip words with no bits to deposit
1700 | }
1701 |
1702 | // Calculate source word index
1703 | wordIdx := inPos >> log2WordSize
1704 | if wordIdx >= uint(len(b.set)) {
1705 | break // No more source bits available
1706 | }
1707 |
1708 | // Get source bits, handling word boundary crossing
1709 | sourceBits := b.set[wordIdx]
1710 | bitOffset := inPos & wordMask
1711 | if wordIdx+1 < uint(len(b.set)) && bitOffset != 0 {
1712 | // Combine bits from current and next word
1713 | sourceBits = (sourceBits >> bitOffset) |
1714 | (b.set[wordIdx+1] << (wordSize - bitOffset))
1715 | } else {
1716 | sourceBits >>= bitOffset
1717 | }
1718 |
1719 | // Deposit bits according to mask
1720 | dst.set[i] = (dst.set[i] &^ mask.set[i]) | pdep(sourceBits, mask.set[i])
1721 | inPos += uint(bits.OnesCount64(mask.set[i]))
1722 | }
1723 | }
1724 |
1725 | //go:generate go run cmd/pextgen/main.go -pkg=bitset
1726 |
1727 | func pext(w, m uint64) (result uint64) {
1728 | var outPos uint
1729 |
1730 | // Process byte by byte
1731 | for i := 0; i < 8; i++ {
1732 | shift := i << 3 // i * 8 using bit shift
1733 | b := uint8(w >> shift)
1734 | mask := uint8(m >> shift)
1735 |
1736 | extracted := pextLUT[b][mask]
1737 | bits := popLUT[mask]
1738 |
1739 | result |= uint64(extracted) << outPos
1740 | outPos += uint(bits)
1741 | }
1742 |
1743 | return result
1744 | }
1745 |
1746 | func pdep(w, m uint64) (result uint64) {
1747 | var inPos uint
1748 |
1749 | // Process byte by byte
1750 | for i := 0; i < 8; i++ {
1751 | shift := i << 3 // i * 8 using bit shift
1752 | mask := uint8(m >> shift)
1753 | bits := popLUT[mask]
1754 |
1755 | // Get the bits we'll deposit from the source
1756 | b := uint8(w >> inPos)
1757 |
1758 | // Deposit them according to the mask for this byte
1759 | deposited := pdepLUT[b][mask]
1760 |
1761 | // Add to result
1762 | result |= uint64(deposited) << shift
1763 | inPos += uint(bits)
1764 | }
1765 |
1766 | return result
1767 | }
1768 |
--------------------------------------------------------------------------------
/bitset_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 Will Fitzgerald. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // This file tests bit sets
6 |
7 | package bitset
8 |
9 | import (
10 | "bytes"
11 | "compress/gzip"
12 | "encoding"
13 | "encoding/base64"
14 | "encoding/binary"
15 | "encoding/json"
16 | "errors"
17 | "fmt"
18 | "io"
19 | "math"
20 | "math/bits"
21 | "math/rand"
22 | "reflect"
23 | "strconv"
24 | "testing"
25 | "time"
26 | )
27 |
28 | func TestStringer(t *testing.T) {
29 | v := New(0)
30 | for i := uint(0); i < 10; i++ {
31 | v.Set(i)
32 | }
33 | if v.String() != "{0,1,2,3,4,5,6,7,8,9}" {
34 | t.Error("bad string output")
35 | }
36 | }
37 |
38 | func TestStringLong(t *testing.T) {
39 | v := New(0)
40 | for i := uint(0); i < 262145; i++ {
41 | v.Set(i)
42 | }
43 | str := v.String()
44 | if len(str) != 1723903 {
45 | t.Error("Unexpected string length: ", len(str))
46 | }
47 | }
48 |
49 | func TestEmptyBitSet(t *testing.T) {
50 | defer func() {
51 | if r := recover(); r != nil {
52 | t.Error("A zero-length bitset should be fine")
53 | }
54 | }()
55 | b := New(0)
56 | if b.Len() != 0 {
57 | t.Errorf("Empty set should have capacity 0, not %d", b.Len())
58 | }
59 | }
60 |
61 | func TestZeroValueBitSet(t *testing.T) {
62 | defer func() {
63 | if r := recover(); r != nil {
64 | t.Error("A zero-length bitset should be fine")
65 | }
66 | }()
67 | var b BitSet
68 | if b.Len() != 0 {
69 | t.Errorf("Empty set should have capacity 0, not %d", b.Len())
70 | }
71 | }
72 |
73 | func TestBitSetNew(t *testing.T) {
74 | v := New(16)
75 | if v.Test(0) {
76 | t.Errorf("Unable to make a bit set and read its 0th value.")
77 | }
78 | }
79 |
80 | func TestBitSetHuge(t *testing.T) {
81 | v := New(uint(math.MaxUint32))
82 | if v.Test(0) {
83 | t.Errorf("Unable to make a huge bit set and read its 0th value.")
84 | }
85 | }
86 |
87 | func TestLen(t *testing.T) {
88 | v := New(1000)
89 | if v.Len() != 1000 {
90 | t.Errorf("Len should be 1000, but is %d.", v.Len())
91 | }
92 | }
93 |
94 | func TestLenIsNumberOfBitsNotBytes(t *testing.T) {
95 | var b BitSet
96 | if b.Len() != 0 {
97 | t.Errorf("empty bitset should have Len 0, got %v", b.Len())
98 | }
99 |
100 | b.Set(0)
101 | if b.Len() != 1 {
102 | t.Errorf("bitset with first bit set should have Len 1, got %v", b.Len())
103 | }
104 |
105 | b.Set(8)
106 | if b.Len() != 9 {
107 | t.Errorf("bitset with 0th and 8th bit set should have Len 9, got %v", b.Len())
108 | }
109 |
110 | b.Set(1)
111 | if b.Len() != 9 {
112 | t.Errorf("bitset with 0th, 1st and 8th bit set should have Len 9, got %v", b.Len())
113 | }
114 | }
115 |
116 | func ExampleBitSet_Len() {
117 | var b BitSet
118 | b.Set(8)
119 | fmt.Println("len", b.Len())
120 | fmt.Println("count", b.Count())
121 | // Output:
122 | // len 9
123 | // count 1
124 | }
125 |
126 | func TestBitSetIsClear(t *testing.T) {
127 | v := New(1000)
128 | for i := uint(0); i < 1000; i++ {
129 | if v.Test(i) {
130 | t.Errorf("Bit %d is set, and it shouldn't be.", i)
131 | }
132 | }
133 | }
134 |
135 | func TestExtendOnBoundary(t *testing.T) {
136 | v := New(32)
137 | defer func() {
138 | if r := recover(); r != nil {
139 | t.Error("Border out of index error should not have caused a panic")
140 | }
141 | }()
142 | v.Set(32)
143 | }
144 |
145 | func TestExceedCap(t *testing.T) {
146 | defer func() {
147 | if r := recover(); r == nil {
148 | t.Error("Set to capacity should have caused a panic")
149 | }
150 | }()
151 | NumHosts := uint(32768)
152 | bmp := New(NumHosts)
153 | bmp.ClearAll()
154 | d := Cap()
155 | bmp.Set(d)
156 | }
157 |
158 | func TestExpand(t *testing.T) {
159 | v := New(0)
160 | defer func() {
161 | if r := recover(); r != nil {
162 | t.Error("Expansion should not have caused a panic")
163 | }
164 | }()
165 | for i := uint(0); i < 1000; i++ {
166 | v.Set(i)
167 | }
168 | }
169 |
170 | func TestBitSetAndGet(t *testing.T) {
171 | v := New(1000)
172 | v.Set(100)
173 | if !v.Test(100) {
174 | t.Errorf("Bit %d is clear, and it shouldn't be.", 100)
175 | }
176 | }
177 |
178 | func TestNextClear(t *testing.T) {
179 | v := New(1000)
180 | v.Set(0).Set(1)
181 | next, found := v.NextClear(0)
182 | if !found || next != 2 {
183 | t.Errorf("Found next clear bit as %d, it should have been 2", next)
184 | }
185 |
186 | v = New(1000)
187 | for i := uint(0); i < 66; i++ {
188 | v.Set(i)
189 | }
190 | next, found = v.NextClear(0)
191 | if !found || next != 66 {
192 | t.Errorf("Found next clear bit as %d, it should have been 66", next)
193 | }
194 |
195 | v = New(1000)
196 | for i := uint(0); i < 64; i++ {
197 | v.Set(i)
198 | }
199 | v.Clear(45)
200 | v.Clear(52)
201 | next, found = v.NextClear(10)
202 | if !found || next != 45 {
203 | t.Errorf("Found next clear bit as %d, it should have been 45", next)
204 | }
205 |
206 | v = New(1000)
207 | for i := uint(0); i < 128; i++ {
208 | v.Set(i)
209 | }
210 | v.Clear(73)
211 | v.Clear(99)
212 | next, found = v.NextClear(10)
213 | if !found || next != 73 {
214 | t.Errorf("Found next clear bit as %d, it should have been 73", next)
215 | }
216 |
217 | next, found = v.NextClear(72)
218 | if !found || next != 73 {
219 | t.Errorf("Found next clear bit as %d, it should have been 73", next)
220 | }
221 | next, found = v.NextClear(73)
222 | if !found || next != 73 {
223 | t.Errorf("Found next clear bit as %d, it should have been 73", next)
224 | }
225 | next, found = v.NextClear(74)
226 | if !found || next != 99 {
227 | t.Errorf("Found next clear bit as %d, it should have been 73", next)
228 | }
229 |
230 | v = New(128)
231 | next, found = v.NextClear(0)
232 | if !found || next != 0 {
233 | t.Errorf("Found next clear bit as %d, it should have been 0", next)
234 | }
235 |
236 | for i := uint(0); i < 128; i++ {
237 | v.Set(i)
238 | }
239 | _, found = v.NextClear(0)
240 | if found {
241 | t.Errorf("There are not clear bits")
242 | }
243 |
244 | b := new(BitSet)
245 | c, d := b.NextClear(1)
246 | if c != 0 || d {
247 | t.Error("Unexpected values")
248 | return
249 | }
250 |
251 | v = New(100)
252 | for i := uint(0); i != 100; i++ {
253 | v.Set(i)
254 | }
255 | next, found = v.NextClear(0)
256 | if found || next != 0 {
257 | t.Errorf("Found next clear bit as %d, it should have return (0, false)", next)
258 | }
259 | }
260 |
261 | func TestIterate(t *testing.T) {
262 | v := New(10000)
263 | v.Set(0)
264 | v.Set(1)
265 | v.Set(2)
266 | data := make([]uint, 3)
267 | c := 0
268 | for i, e := v.NextSet(0); e; i, e = v.NextSet(i + 1) {
269 | data[c] = i
270 | c++
271 | }
272 | if data[0] != 0 {
273 | t.Errorf("bug 0")
274 | }
275 | if data[1] != 1 {
276 | t.Errorf("bug 1")
277 | }
278 | if data[2] != 2 {
279 | t.Errorf("bug 2")
280 | }
281 | v.Set(10)
282 | v.Set(2000)
283 | data = make([]uint, 5)
284 | c = 0
285 | for i, e := v.NextSet(0); e; i, e = v.NextSet(i + 1) {
286 | data[c] = i
287 | c++
288 | }
289 | if data[0] != 0 {
290 | t.Errorf("bug 0")
291 | }
292 | if data[1] != 1 {
293 | t.Errorf("bug 1")
294 | }
295 | if data[2] != 2 {
296 | t.Errorf("bug 2")
297 | }
298 | if data[3] != 10 {
299 | t.Errorf("bug 3")
300 | }
301 | if data[4] != 2000 {
302 | t.Errorf("bug 4")
303 | }
304 | }
305 |
306 | func TestNextSet(t *testing.T) {
307 | testCases := []struct {
308 | name string
309 | //
310 | set []uint
311 | del []uint
312 | //
313 | startIdx uint
314 | wantIdx uint
315 | wantOk bool
316 | }{
317 | {
318 | name: "null",
319 | set: []uint{},
320 | startIdx: 0,
321 | wantIdx: 0,
322 | wantOk: false,
323 | },
324 | {
325 | name: "zero",
326 | set: []uint{0},
327 | startIdx: 0,
328 | wantIdx: 0,
329 | wantOk: true,
330 | },
331 | {
332 | name: "1,5",
333 | set: []uint{1, 5},
334 | startIdx: 0,
335 | wantIdx: 1,
336 | wantOk: true,
337 | },
338 | {
339 | name: "many",
340 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
341 | startIdx: 100,
342 | wantIdx: 130,
343 | wantOk: true,
344 | },
345 | {
346 | name: "many-2",
347 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
348 | del: []uint{130, 190, 300, 420},
349 | startIdx: 100,
350 | wantIdx: 250,
351 | wantOk: true,
352 | },
353 | {
354 | name: "last",
355 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
356 | startIdx: 511,
357 | wantIdx: 511,
358 | wantOk: true,
359 | },
360 | {
361 | name: "last-2",
362 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
363 | del: []uint{511},
364 | startIdx: 511,
365 | wantIdx: 0,
366 | wantOk: false,
367 | },
368 | }
369 |
370 | for _, tc := range testCases {
371 | var b BitSet
372 | for _, u := range tc.set {
373 | b.Set(u)
374 | }
375 |
376 | for _, u := range tc.del {
377 | b.Clear(u) // without compact
378 | }
379 |
380 | idx, ok := b.NextSet(tc.startIdx)
381 |
382 | if ok != tc.wantOk {
383 | t.Errorf("NextSet, %s: got ok: %v, want: %v", tc.name, ok, tc.wantOk)
384 | }
385 | if idx != tc.wantIdx {
386 | t.Errorf("NextSet, %s: got next idx: %d, want: %d", tc.name, idx, tc.wantIdx)
387 | }
388 | }
389 | }
390 |
391 | func TestNextSetMany(t *testing.T) {
392 | testCases := []struct {
393 | name string
394 | //
395 | set []uint
396 | del []uint
397 | //
398 | buf []uint
399 | wantData []uint
400 | //
401 | startIdx uint
402 | wantIdx uint
403 | }{
404 | {
405 | name: "null",
406 | set: []uint{},
407 | del: []uint{},
408 | buf: make([]uint, 0, 512),
409 | wantData: []uint{},
410 | startIdx: 0,
411 | wantIdx: 0,
412 | },
413 | {
414 | name: "zero",
415 | set: []uint{0},
416 | del: []uint{},
417 | buf: make([]uint, 0, 512),
418 | wantData: []uint{0}, // bit #0 is set
419 | startIdx: 0,
420 | wantIdx: 0,
421 | },
422 | {
423 | name: "1,5",
424 | set: []uint{1, 5},
425 | del: []uint{},
426 | buf: make([]uint, 0, 512),
427 | wantData: []uint{1, 5},
428 | startIdx: 0,
429 | wantIdx: 5,
430 | },
431 | {
432 | name: "many",
433 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
434 | del: []uint{},
435 | buf: make([]uint, 0, 512),
436 | wantData: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
437 | startIdx: 0,
438 | wantIdx: 511,
439 | },
440 | {
441 | name: "start idx",
442 | set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511},
443 | del: []uint{},
444 | buf: make([]uint, 0, 512),
445 | wantData: []uint{250, 300, 380, 420, 480, 511},
446 | startIdx: 195,
447 | wantIdx: 511,
448 | },
449 | {
450 | name: "zero buffer",
451 | set: []uint{1, 2, 3, 4, 511},
452 | del: []uint{},
453 | buf: make([]uint, 0), // buffer
454 | wantData: []uint{},
455 | startIdx: 0,
456 | wantIdx: 0,
457 | },
458 | {
459 | name: "buffer too short, first word",
460 | set: []uint{1, 2, 3, 4, 5, 6, 7, 8, 9},
461 | del: []uint{},
462 | buf: make([]uint, 0, 5), // buffer
463 | wantData: []uint{1, 2, 3, 4, 5},
464 | startIdx: 0,
465 | wantIdx: 5,
466 | },
467 | {
468 | name: "buffer too short",
469 | set: []uint{65, 66, 67, 68, 69, 70},
470 | del: []uint{},
471 | buf: make([]uint, 0, 5), // buffer
472 | wantData: []uint{65, 66, 67, 68, 69},
473 | startIdx: 0,
474 | wantIdx: 69,
475 | },
476 | {
477 | name: "special, last return",
478 | set: []uint{1},
479 | del: []uint{1}, // delete without compact
480 | buf: make([]uint, 0, 5), // buffer
481 | wantData: []uint{},
482 | startIdx: 0,
483 | wantIdx: 0,
484 | },
485 | }
486 |
487 | for _, tc := range testCases {
488 | var b BitSet
489 | for _, u := range tc.set {
490 | b.Set(u)
491 | }
492 |
493 | for _, u := range tc.del {
494 | b.Clear(u) // without compact
495 | }
496 |
497 | idx, buf := b.NextSetMany(tc.startIdx, tc.buf)
498 |
499 | if idx != tc.wantIdx {
500 | t.Errorf("NextSetMany, %s: got next idx: %d, want: %d", tc.name, idx, tc.wantIdx)
501 | }
502 |
503 | if !reflect.DeepEqual(buf, tc.wantData) {
504 | t.Errorf("NextSetMany, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v",
505 | tc.name, buf, tc.wantData)
506 | }
507 | }
508 | }
509 |
510 | func TestAppendTo(t *testing.T) {
511 | testCases := []struct {
512 | name string
513 | set []uint
514 | buf []uint
515 | }{
516 | {
517 | name: "null",
518 | set: nil,
519 | buf: nil,
520 | },
521 | {
522 | name: "one",
523 | set: []uint{42},
524 | buf: make([]uint, 0, 5),
525 | },
526 | {
527 | name: "many",
528 | set: []uint{1, 42, 55, 258, 7211, 54666},
529 | buf: make([]uint, 0),
530 | },
531 | }
532 |
533 | for _, tc := range testCases {
534 | var b BitSet
535 | for _, u := range tc.set {
536 | b.Set(u)
537 | }
538 |
539 | tc.buf = b.AppendTo(tc.buf)
540 |
541 | if !reflect.DeepEqual(tc.buf, tc.set) {
542 | t.Errorf("AppendTo, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v",
543 | tc.name, tc.buf, tc.set)
544 | }
545 | }
546 | }
547 |
548 | func TestAsSlice(t *testing.T) {
549 | testCases := []struct {
550 | name string
551 | set []uint
552 | buf []uint
553 | }{
554 | {
555 | name: "null",
556 | set: nil,
557 | buf: nil,
558 | },
559 | {
560 | name: "one",
561 | set: []uint{42},
562 | buf: make([]uint, 1),
563 | },
564 | {
565 | name: "many",
566 | set: []uint{1, 42, 55, 258, 7211, 54666},
567 | buf: make([]uint, 6),
568 | },
569 | }
570 |
571 | for _, tc := range testCases {
572 | var b BitSet
573 | for _, u := range tc.set {
574 | b.Set(u)
575 | }
576 |
577 | tc.buf = b.AsSlice(tc.buf)
578 |
579 | if !reflect.DeepEqual(tc.buf, tc.set) {
580 | t.Errorf("AsSlice, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v",
581 | tc.name, tc.buf, tc.set)
582 | }
583 | }
584 | }
585 |
586 | func TestPanicAppendTo(t *testing.T) {
587 | defer func() {
588 | if r := recover(); r != nil {
589 | t.Error("AppendTo with empty buf should not have caused a panic")
590 | }
591 | }()
592 | v := New(1000)
593 | v.Set(1000)
594 | _ = v.AppendTo(nil)
595 | }
596 |
597 | func TestPanicAsSlice(t *testing.T) {
598 | defer func() {
599 | if r := recover(); r == nil {
600 | t.Error("AsSlice with buf too small should have caused a panic")
601 | }
602 | }()
603 | v := New(1000)
604 | v.Set(1000)
605 | _ = v.AsSlice(nil)
606 | }
607 |
608 | func TestSetTo(t *testing.T) {
609 | v := New(1000)
610 | v.SetTo(100, true)
611 | if !v.Test(100) {
612 | t.Errorf("Bit %d is clear, and it shouldn't be.", 100)
613 | }
614 | v.SetTo(100, false)
615 | if v.Test(100) {
616 | t.Errorf("Bit %d is set, and it shouldn't be.", 100)
617 | }
618 | }
619 |
620 | func TestChain(t *testing.T) {
621 | if !New(1000).Set(100).Set(99).Clear(99).Test(100) {
622 | t.Errorf("Bit %d is clear, and it shouldn't be.", 100)
623 | }
624 | }
625 |
626 | func TestOutOfBoundsLong(t *testing.T) {
627 | v := New(64)
628 | defer func() {
629 | if r := recover(); r != nil {
630 | t.Error("Long distance out of index error should not have caused a panic")
631 | }
632 | }()
633 | v.Set(1000)
634 | }
635 |
636 | func TestOutOfBoundsClose(t *testing.T) {
637 | v := New(65)
638 | defer func() {
639 | if r := recover(); r != nil {
640 | t.Error("Local out of index error should not have caused a panic")
641 | }
642 | }()
643 | v.Set(66)
644 | }
645 |
646 | func TestCount(t *testing.T) {
647 | tot := uint(64*4 + 11) // just some multi unit64 number
648 | v := New(tot)
649 | checkLast := true
650 | for i := uint(0); i < tot; i++ {
651 | sz := uint(v.Count())
652 | if sz != i {
653 | t.Errorf("Count reported as %d, but it should be %d", sz, i)
654 | checkLast = false
655 | break
656 | }
657 | v.Set(i)
658 | }
659 | if checkLast {
660 | sz := uint(v.Count())
661 | if sz != tot {
662 | t.Errorf("After all bits set, size reported as %d, but it should be %d", sz, tot)
663 | }
664 | }
665 | }
666 |
667 | // test setting every 3rd bit, just in case something odd is happening
668 | func TestCount2(t *testing.T) {
669 | tot := uint(64*4 + 11) // just some multi unit64 number
670 | v := New(tot)
671 | for i := uint(0); i < tot; i += 3 {
672 | sz := uint(v.Count())
673 | if sz != i/3 {
674 | t.Errorf("Count reported as %d, but it should be %d", sz, i)
675 | break
676 | }
677 | v.Set(i)
678 | }
679 | }
680 |
681 | // nil tests
682 | func TestNullTest(t *testing.T) {
683 | var v *BitSet
684 | defer func() {
685 | if r := recover(); r == nil {
686 | t.Error("Checking bit of null reference should have caused a panic")
687 | }
688 | }()
689 | v.Test(66)
690 | }
691 |
692 | func TestNullSet(t *testing.T) {
693 | var v *BitSet
694 | defer func() {
695 | if r := recover(); r == nil {
696 | t.Error("Setting bit of null reference should have caused a panic")
697 | }
698 | }()
699 | v.Set(66)
700 | }
701 |
702 | func TestNullClear(t *testing.T) {
703 | var v *BitSet
704 | defer func() {
705 | if r := recover(); r == nil {
706 | t.Error("Clearning bit of null reference should have caused a panic")
707 | }
708 | }()
709 | v.Clear(66)
710 | }
711 |
712 | func TestNullCount(t *testing.T) {
713 | var v *BitSet
714 | defer func() {
715 | if r := recover(); r != nil {
716 | t.Error("Counting null reference should not have caused a panic")
717 | }
718 | }()
719 | cnt := v.Count()
720 | if cnt != 0 {
721 | t.Errorf("Count reported as %d, but it should be 0", cnt)
722 | }
723 | }
724 |
725 | func TestMustNew(t *testing.T) {
726 | testCases := []struct {
727 | length uint
728 | nwords int
729 | }{
730 | {
731 | length: 0,
732 | nwords: 0,
733 | },
734 | {
735 | length: 1,
736 | nwords: 1,
737 | },
738 | {
739 | length: 64,
740 | nwords: 1,
741 | },
742 | {
743 | length: 65,
744 | nwords: 2,
745 | },
746 | {
747 | length: 512,
748 | nwords: 8,
749 | },
750 | {
751 | length: 513,
752 | nwords: 9,
753 | },
754 | }
755 |
756 | for _, tc := range testCases {
757 | b := MustNew(tc.length)
758 | if len(b.set) != tc.nwords {
759 | t.Errorf("length = %d, len(b.set) got: %d, want: %d", tc.length, len(b.set), tc.nwords)
760 | }
761 | }
762 | }
763 |
764 | func TestPanicMustNew(t *testing.T) {
765 | defer func() {
766 | if r := recover(); r == nil {
767 | t.Error("length too big should have caused a panic")
768 | }
769 | }()
770 | MustNew(Cap())
771 | }
772 |
773 | func TestPanicDifferenceBNil(t *testing.T) {
774 | var b *BitSet
775 | compare := New(10)
776 | defer func() {
777 | if r := recover(); r == nil {
778 | t.Error("Nil First should should have caused a panic")
779 | }
780 | }()
781 | b.Difference(compare)
782 | }
783 |
784 | func TestPanicDifferenceCompareNil(t *testing.T) {
785 | var compare *BitSet
786 | b := New(10)
787 | defer func() {
788 | if r := recover(); r == nil {
789 | t.Error("Nil Second should should have caused a panic")
790 | }
791 | }()
792 | b.Difference(compare)
793 | }
794 |
795 | func TestPanicUnionBNil(t *testing.T) {
796 | var b *BitSet
797 | compare := New(10)
798 | defer func() {
799 | if r := recover(); r == nil {
800 | t.Error("Nil First should should have caused a panic")
801 | }
802 | }()
803 | b.Union(compare)
804 | }
805 |
806 | func TestPanicUnionCompareNil(t *testing.T) {
807 | var compare *BitSet
808 | b := New(10)
809 | defer func() {
810 | if r := recover(); r == nil {
811 | t.Error("Nil Second should should have caused a panic")
812 | }
813 | }()
814 | b.Union(compare)
815 | }
816 |
817 | func TestPanicIntersectionBNil(t *testing.T) {
818 | var b *BitSet
819 | compare := New(10)
820 | defer func() {
821 | if r := recover(); r == nil {
822 | t.Error("Nil First should should have caused a panic")
823 | }
824 | }()
825 | b.Intersection(compare)
826 | }
827 |
828 | func TestPanicIntersectionCompareNil(t *testing.T) {
829 | var compare *BitSet
830 | b := New(10)
831 | defer func() {
832 | if r := recover(); r == nil {
833 | t.Error("Nil Second should should have caused a panic")
834 | }
835 | }()
836 | b.Intersection(compare)
837 | }
838 |
839 | func TestPanicSymmetricDifferenceBNil(t *testing.T) {
840 | var b *BitSet
841 | compare := New(10)
842 | defer func() {
843 | if r := recover(); r == nil {
844 | t.Error("Nil First should should have caused a panic")
845 | }
846 | }()
847 | b.SymmetricDifference(compare)
848 | }
849 |
850 | func TestPanicSymmetricDifferenceCompareNil(t *testing.T) {
851 | var compare *BitSet
852 | b := New(10)
853 | defer func() {
854 | if r := recover(); r == nil {
855 | t.Error("Nil Second should should have caused a panic")
856 | }
857 | }()
858 | b.SymmetricDifference(compare)
859 | }
860 |
861 | func TestPanicComplementBNil(t *testing.T) {
862 | var b *BitSet
863 | defer func() {
864 | if r := recover(); r == nil {
865 | t.Error("Nil should should have caused a panic")
866 | }
867 | }()
868 | b.Complement()
869 | }
870 |
871 | func TestPanicAnytBNil(t *testing.T) {
872 | var b *BitSet
873 | defer func() {
874 | if r := recover(); r == nil {
875 | t.Error("Nil should should have caused a panic")
876 | }
877 | }()
878 | b.Any()
879 | }
880 |
881 | func TestPanicNonetBNil(t *testing.T) {
882 | var b *BitSet
883 | defer func() {
884 | if r := recover(); r == nil {
885 | t.Error("Nil should should have caused a panic")
886 | }
887 | }()
888 | b.None()
889 | }
890 |
891 | func TestPanicAlltBNil(t *testing.T) {
892 | var b *BitSet
893 | defer func() {
894 | if r := recover(); r == nil {
895 | t.Error("Nil should should have caused a panic")
896 | }
897 | }()
898 | b.All()
899 | }
900 |
901 | func TestAll(t *testing.T) {
902 | v := New(0)
903 | if !v.All() {
904 | t.Error("Empty sets should return true on All()")
905 | }
906 | v = New(2)
907 | v.SetTo(0, true)
908 | v.SetTo(1, true)
909 | if !v.All() {
910 | t.Error("Non-empty sets with all bits set should return true on All()")
911 | }
912 | v = New(2)
913 | if v.All() {
914 | t.Error("Non-empty sets with no bits set should return false on All()")
915 | }
916 | v = New(2)
917 | v.SetTo(0, true)
918 | if v.All() {
919 | t.Error("Non-empty sets with some bits set should return false on All()")
920 | }
921 | }
922 |
923 | func TestShrink(t *testing.T) {
924 | bs := New(10)
925 | bs.Set(0)
926 | bs.Shrink(63)
927 | if !bs.Test(0) {
928 | t.Error("0 should be set")
929 | return
930 | }
931 | b := New(0)
932 |
933 | b.Set(0)
934 | b.Set(1)
935 | b.Set(2)
936 | b.Set(3)
937 | b.Set(64)
938 | b.Compact()
939 | if !b.Test(0) {
940 | t.Error("0 should be set")
941 | return
942 | }
943 | if !b.Test(1) {
944 | t.Error("1 should be set")
945 | return
946 | }
947 | if !b.Test(2) {
948 | t.Error("2 should be set")
949 | return
950 | }
951 | if !b.Test(3) {
952 | t.Error("3 should be set")
953 | return
954 | }
955 | if !b.Test(64) {
956 | t.Error("64 should be set")
957 | return
958 | }
959 |
960 | b.Shrink(2)
961 | if !b.Test(0) {
962 | t.Error("0 should be set")
963 | return
964 | }
965 | if !b.Test(1) {
966 | t.Error("1 should be set")
967 | return
968 | }
969 | if !b.Test(2) {
970 | t.Error("2 should be set")
971 | return
972 | }
973 | if b.Test(3) {
974 | t.Error("3 should not be set")
975 | return
976 | }
977 | if b.Test(64) {
978 | t.Error("64 should not be set")
979 | return
980 | }
981 |
982 | b.Set(24)
983 | b.Shrink(100)
984 | if !b.Test(24) {
985 | t.Error("24 should be set")
986 | return
987 | }
988 |
989 | b.Set(127)
990 | b.Set(128)
991 | b.Set(129)
992 | b.Compact()
993 | if !b.Test(127) {
994 | t.Error("127 should be set")
995 | return
996 | }
997 | if !b.Test(128) {
998 | t.Error("128 should be set")
999 | return
1000 | }
1001 | if !b.Test(129) {
1002 | t.Error("129 be set")
1003 | return
1004 | }
1005 |
1006 | b.Shrink(128)
1007 | if !b.Test(127) {
1008 | t.Error("127 should be set")
1009 | return
1010 | }
1011 | if !b.Test(128) {
1012 | t.Error("128 should be set")
1013 | return
1014 | }
1015 | if b.Test(129) {
1016 | t.Error("129 should not be set")
1017 | return
1018 | }
1019 |
1020 | b.Set(129)
1021 | b.Shrink(129)
1022 | if !b.Test(129) {
1023 | t.Error("129 should be set")
1024 | return
1025 | }
1026 |
1027 | b.Set(1000)
1028 | b.Set(2000)
1029 | b.Set(3000)
1030 | b.Shrink(3000)
1031 | if len(b.set) != 3000/64+1 {
1032 | t.Error("Wrong length of BitSet.set")
1033 | return
1034 | }
1035 | if !b.Test(3000) {
1036 | t.Error("3000 should be set")
1037 | return
1038 | }
1039 |
1040 | b.Shrink(2000)
1041 | if len(b.set) != 2000/64+1 {
1042 | t.Error("Wrong length of BitSet.set")
1043 | return
1044 | }
1045 | if b.Test(3000) {
1046 | t.Error("3000 should not be set")
1047 | return
1048 | }
1049 | if !b.Test(2000) {
1050 | t.Error("2000 should be set")
1051 | return
1052 | }
1053 | if !b.Test(1000) {
1054 | t.Error("1000 should be set")
1055 | return
1056 | }
1057 | if !b.Test(24) {
1058 | t.Error("24 should be set")
1059 | return
1060 | }
1061 |
1062 | b = New(110)
1063 | b.Set(80)
1064 | b.Shrink(70)
1065 | for _, word := range b.set {
1066 | if word != 0 {
1067 | t.Error("word should be 0", word)
1068 | }
1069 | }
1070 | }
1071 |
1072 | func TestInsertAtWithSet(t *testing.T) {
1073 | b := New(0)
1074 | b.Set(0)
1075 | b.Set(1)
1076 | b.Set(63)
1077 | b.Set(64)
1078 | b.Set(65)
1079 |
1080 | b.InsertAt(3)
1081 | if !b.Test(0) {
1082 | t.Error("0 should be set")
1083 | return
1084 | }
1085 | if !b.Test(1) {
1086 | t.Error("1 should be set")
1087 | return
1088 | }
1089 | if b.Test(3) {
1090 | t.Error("3 should not be set")
1091 | return
1092 | }
1093 | if !b.Test(64) {
1094 | t.Error("64 should be set")
1095 | return
1096 | }
1097 | if !b.Test(65) {
1098 | t.Error("65 should be set")
1099 | return
1100 | }
1101 | if !b.Test(66) {
1102 | t.Error("66 should be set")
1103 | return
1104 | }
1105 | }
1106 |
1107 | func TestInsertAt(t *testing.T) {
1108 | type testCase struct {
1109 | input []string
1110 | insertIdx uint
1111 | expected []string
1112 | }
1113 |
1114 | testCases := []testCase{
1115 | {
1116 | input: []string{
1117 | "1111111111111111111111111111111111111111111111111111111111111111",
1118 | },
1119 | insertIdx: uint(62),
1120 | expected: []string{
1121 | "1011111111111111111111111111111111111111111111111111111111111111",
1122 | "0000000000000000000000000000000000000000000000000000000000000001",
1123 | },
1124 | },
1125 | {
1126 | input: []string{
1127 | "1111111111111111111111111111111111111111111111111111111111111111",
1128 | },
1129 | insertIdx: uint(63),
1130 | expected: []string{
1131 | "0111111111111111111111111111111111111111111111111111111111111111",
1132 | "0000000000000000000000000000000000000000000000000000000000000001",
1133 | },
1134 | },
1135 | {
1136 | input: []string{
1137 | "1111111111111111111111111111111111111111111111111111111111111111",
1138 | },
1139 | insertIdx: uint(0),
1140 | expected: []string{
1141 | "1111111111111111111111111111111111111111111111111111111111111110",
1142 | "0000000000000000000000000000000000000000000000000000000000000001",
1143 | },
1144 | },
1145 | {
1146 | input: []string{
1147 | "1111111111111111111111111111111111111111111111111111111111111111",
1148 | "1111111111111111111111111111111111111111111111111111111111111111",
1149 | "1111111111111111111111111111111111111111111111111111111111111111",
1150 | },
1151 | insertIdx: uint(70),
1152 | expected: []string{
1153 | "1111111111111111111111111111111111111111111111111111111111111111",
1154 | "1111111111111111111111111111111111111111111111111111111110111111",
1155 | "1111111111111111111111111111111111111111111111111111111111111111",
1156 | "0000000000000000000000000000000000000000000000000000000000000001",
1157 | },
1158 | },
1159 | {
1160 | input: []string{
1161 | "1111111111111111111111111111111111111111111111111111111111111111",
1162 | "1111111111111111111111111111111111111111111111111111111111111111",
1163 | "1111111111111111111111111111111111111111111111111111111111110000",
1164 | },
1165 | insertIdx: uint(70),
1166 | expected: []string{
1167 | "1111111111111111111111111111111111111111111111111111111111111111",
1168 | "1111111111111111111111111111111111111111111111111111111110111111",
1169 | "1111111111111111111111111111111111111111111111111111111111100001",
1170 | "0000000000000000000000000000000000000000000000000000000000000001",
1171 | },
1172 | },
1173 | {
1174 | input: []string{
1175 | "1111111111111111111111111111111111111111111111111111111111110000",
1176 | },
1177 | insertIdx: uint(10),
1178 | expected: []string{
1179 | "1111111111111111111111111111111111111111111111111111101111110000",
1180 | "0000000000000000000000000000000000000000000000000000000000000001",
1181 | },
1182 | },
1183 | }
1184 |
1185 | for _, tc := range testCases {
1186 | var input []uint64
1187 | for _, inputElement := range tc.input {
1188 | parsed, _ := strconv.ParseUint(inputElement, 2, 64)
1189 | input = append(input, parsed)
1190 | }
1191 |
1192 | var expected []uint64
1193 | for _, expectedElement := range tc.expected {
1194 | parsed, _ := strconv.ParseUint(expectedElement, 2, 64)
1195 | expected = append(expected, parsed)
1196 | }
1197 |
1198 | b := From(input)
1199 | b.InsertAt(tc.insertIdx)
1200 | if len(b.set) != len(expected) {
1201 | t.Error("Length of sets should be equal")
1202 | return
1203 | }
1204 | for i := range b.set {
1205 | if b.set[i] != expected[i] {
1206 | t.Error("Unexpected results found in set")
1207 | return
1208 | }
1209 | }
1210 | }
1211 | }
1212 |
1213 | func TestNone(t *testing.T) {
1214 | v := New(0)
1215 | if !v.None() {
1216 | t.Error("Empty sets should return true on None()")
1217 | }
1218 | v = New(2)
1219 | v.SetTo(0, true)
1220 | v.SetTo(1, true)
1221 | if v.None() {
1222 | t.Error("Non-empty sets with all bits set should return false on None()")
1223 | }
1224 | v = New(2)
1225 | if !v.None() {
1226 | t.Error("Non-empty sets with no bits set should return true on None()")
1227 | }
1228 | v = New(2)
1229 | v.SetTo(0, true)
1230 | if v.None() {
1231 | t.Error("Non-empty sets with some bits set should return false on None()")
1232 | }
1233 | v = new(BitSet)
1234 | if !v.None() {
1235 | t.Error("Empty sets should return true on None()")
1236 | }
1237 | }
1238 |
1239 | func TestEqual(t *testing.T) {
1240 | a := New(100)
1241 | b := New(99)
1242 | c := New(100)
1243 | if a.Equal(b) {
1244 | t.Error("Sets of different sizes should be not be equal")
1245 | }
1246 | if !a.Equal(c) {
1247 | t.Error("Two empty sets of the same size should be equal")
1248 | }
1249 | a.Set(99)
1250 | c.Set(0)
1251 | if a.Equal(c) {
1252 | t.Error("Two sets with differences should not be equal")
1253 | }
1254 | c.Set(99)
1255 | a.Set(0)
1256 | if !a.Equal(c) {
1257 | t.Error("Two sets with the same bits set should be equal")
1258 | }
1259 | if a.Equal(nil) {
1260 | t.Error("The sets should be different")
1261 | }
1262 | a = New(0)
1263 | b = New(0)
1264 | if !a.Equal(b) {
1265 | t.Error("Two empty set should be equal")
1266 | }
1267 | var x *BitSet
1268 | var y *BitSet
1269 | z := New(0)
1270 | if !x.Equal(y) {
1271 | t.Error("Two nil bitsets should be equal")
1272 | }
1273 | if x.Equal(z) {
1274 | t.Error("Nil receiver bitset should not be equal to non-nil bitset")
1275 | }
1276 | }
1277 |
1278 | func TestUnion(t *testing.T) {
1279 | a := New(100)
1280 | b := New(200)
1281 | for i := uint(1); i < 100; i += 2 {
1282 | a.Set(i)
1283 | b.Set(i - 1)
1284 | }
1285 | for i := uint(100); i < 200; i++ {
1286 | b.Set(i)
1287 | }
1288 | if a.UnionCardinality(b) != 200 {
1289 | t.Errorf("Union should have 200 bits set, but had %d", a.UnionCardinality(b))
1290 | }
1291 | if a.UnionCardinality(b) != b.UnionCardinality(a) {
1292 | t.Errorf("Union should be symmetric")
1293 | }
1294 |
1295 | c := a.Union(b)
1296 | d := b.Union(a)
1297 | if c.Count() != 200 {
1298 | t.Errorf("Union should have 200 bits set, but had %d", c.Count())
1299 | }
1300 | if !c.Equal(d) {
1301 | t.Errorf("Union should be symmetric")
1302 | }
1303 | }
1304 |
1305 | func TestEmptyUnionCardinality(t *testing.T) {
1306 | a := New(0)
1307 | b := New(0)
1308 | if a.UnionCardinality(b) != 0 {
1309 | t.Error("UnionCardinality should be zero")
1310 | }
1311 | }
1312 |
1313 | func TestInPlaceUnion(t *testing.T) {
1314 | a := New(100)
1315 | b := New(200)
1316 | for i := uint(1); i < 100; i += 2 {
1317 | a.Set(i)
1318 | b.Set(i - 1)
1319 | }
1320 | for i := uint(100); i < 200; i++ {
1321 | b.Set(i)
1322 | }
1323 | c := a.Clone()
1324 | c.InPlaceUnion(b)
1325 | d := b.Clone()
1326 | d.InPlaceUnion(a)
1327 | if c.Count() != 200 {
1328 | t.Errorf("Union should have 200 bits set, but had %d", c.Count())
1329 | }
1330 | if d.Count() != 200 {
1331 | t.Errorf("Union should have 200 bits set, but had %d", d.Count())
1332 | }
1333 | if !c.Equal(d) {
1334 | t.Errorf("Union should be symmetric")
1335 | }
1336 | }
1337 |
1338 | func TestIntersection(t *testing.T) {
1339 | a := New(100)
1340 | b := New(200)
1341 | for i := uint(1); i < 100; i += 2 {
1342 | a.Set(i)
1343 | b.Set(i - 1).Set(i)
1344 | }
1345 | for i := uint(100); i < 200; i++ {
1346 | b.Set(i)
1347 | }
1348 | if a.IntersectionCardinality(b) != 50 {
1349 | t.Errorf("Intersection should have 50 bits set, but had %d", a.IntersectionCardinality(b))
1350 | }
1351 | if a.IntersectionCardinality(b) != b.IntersectionCardinality(a) {
1352 | t.Errorf("Intersection should be symmetric")
1353 | }
1354 | c := a.Intersection(b)
1355 | d := b.Intersection(a)
1356 | if c.Count() != 50 {
1357 | t.Errorf("Intersection should have 50 bits set, but had %d", c.Count())
1358 | }
1359 | if !c.Equal(d) {
1360 | t.Errorf("Intersection should be symmetric")
1361 | }
1362 | }
1363 |
1364 | func TestEmptyIntersectionCardinality(t *testing.T) {
1365 | a := New(0)
1366 | b := New(0)
1367 | if a.IntersectionCardinality(b) != 0 {
1368 | t.Error("IntersectionCardinality should be zero")
1369 | }
1370 | }
1371 |
1372 | func TestInplaceIntersection(t *testing.T) {
1373 | a := New(100)
1374 | b := New(200)
1375 | for i := uint(1); i < 100; i += 2 {
1376 | a.Set(i)
1377 | b.Set(i - 1).Set(i)
1378 | }
1379 | for i := uint(100); i < 200; i++ {
1380 | b.Set(i)
1381 | }
1382 | c := a.Clone()
1383 | c.InPlaceIntersection(b)
1384 | d := b.Clone()
1385 | d.InPlaceIntersection(a)
1386 | if c.Count() != 50 {
1387 | t.Errorf("Intersection should have 50 bits set, but had %d", c.Count())
1388 | }
1389 | if d.Count() != 50 {
1390 | t.Errorf("Intersection should have 50 bits set, but had %d", d.Count())
1391 | }
1392 | if !c.Equal(d) {
1393 | t.Errorf("Intersection should be symmetric")
1394 | }
1395 | }
1396 |
1397 | func TestDifference(t *testing.T) {
1398 | a := New(100)
1399 | b := New(200)
1400 | for i := uint(1); i < 100; i += 2 {
1401 | a.Set(i)
1402 | b.Set(i - 1)
1403 | }
1404 | for i := uint(100); i < 200; i++ {
1405 | b.Set(i)
1406 | }
1407 | if a.DifferenceCardinality(b) != 50 {
1408 | t.Errorf("a-b Difference should have 50 bits set, but had %d", a.DifferenceCardinality(b))
1409 | }
1410 | if b.DifferenceCardinality(a) != 150 {
1411 | t.Errorf("b-a Difference should have 150 bits set, but had %d", b.DifferenceCardinality(a))
1412 | }
1413 |
1414 | c := a.Difference(b)
1415 | d := b.Difference(a)
1416 | if c.Count() != 50 {
1417 | t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count())
1418 | }
1419 | if d.Count() != 150 {
1420 | t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count())
1421 | }
1422 | if c.Equal(d) {
1423 | t.Errorf("Difference, here, should not be symmetric")
1424 | }
1425 | }
1426 |
1427 | func TestEmptyDifferenceCardinality(t *testing.T) {
1428 | a := New(0)
1429 | b := New(0)
1430 | if a.DifferenceCardinality(b) != 0 {
1431 | t.Error("DifferenceCardinality should be zero")
1432 | }
1433 | }
1434 |
1435 | func TestInPlaceDifference(t *testing.T) {
1436 | a := New(100)
1437 | b := New(200)
1438 | for i := uint(1); i < 100; i += 2 {
1439 | a.Set(i)
1440 | b.Set(i - 1)
1441 | }
1442 | for i := uint(100); i < 200; i++ {
1443 | b.Set(i)
1444 | }
1445 | c := a.Clone()
1446 | c.InPlaceDifference(b)
1447 | d := b.Clone()
1448 | d.InPlaceDifference(a)
1449 | if c.Count() != 50 {
1450 | t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count())
1451 | }
1452 | if d.Count() != 150 {
1453 | t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count())
1454 | }
1455 | if c.Equal(d) {
1456 | t.Errorf("Difference, here, should not be symmetric")
1457 | }
1458 | }
1459 |
1460 | func TestSymmetricDifference(t *testing.T) {
1461 | a := New(100)
1462 | b := New(200)
1463 | for i := uint(1); i < 100; i += 2 {
1464 | a.Set(i) // 01010101010 ... 0000000
1465 | b.Set(i - 1).Set(i) // 11111111111111111000000
1466 | }
1467 | for i := uint(100); i < 200; i++ {
1468 | b.Set(i)
1469 | }
1470 | if a.SymmetricDifferenceCardinality(b) != 150 {
1471 | t.Errorf("a^b Difference should have 150 bits set, but had %d", a.SymmetricDifferenceCardinality(b))
1472 | }
1473 | if b.SymmetricDifferenceCardinality(a) != 150 {
1474 | t.Errorf("b^a Difference should have 150 bits set, but had %d", b.SymmetricDifferenceCardinality(a))
1475 | }
1476 |
1477 | c := a.SymmetricDifference(b)
1478 | d := b.SymmetricDifference(a)
1479 | if c.Count() != 150 {
1480 | t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count())
1481 | }
1482 | if d.Count() != 150 {
1483 | t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count())
1484 | }
1485 | if !c.Equal(d) {
1486 | t.Errorf("SymmetricDifference should be symmetric")
1487 | }
1488 | }
1489 |
1490 | func TestEmptySymmetricDifferenceCardinality(t *testing.T) {
1491 | a := New(0)
1492 | b := New(0)
1493 | if a.SymmetricDifferenceCardinality(b) != 0 {
1494 | t.Error("SymmetricDifferenceCardinality should be zero")
1495 | }
1496 | }
1497 |
1498 | func TestInPlaceSymmetricDifference(t *testing.T) {
1499 | a := New(100)
1500 | b := New(200)
1501 | for i := uint(1); i < 100; i += 2 {
1502 | a.Set(i) // 01010101010 ... 0000000
1503 | b.Set(i - 1).Set(i) // 11111111111111111000000
1504 | }
1505 | for i := uint(100); i < 200; i++ {
1506 | b.Set(i)
1507 | }
1508 | c := a.Clone()
1509 | c.InPlaceSymmetricDifference(b)
1510 | d := b.Clone()
1511 | d.InPlaceSymmetricDifference(a)
1512 | if c.Count() != 150 {
1513 | t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count())
1514 | }
1515 | if d.Count() != 150 {
1516 | t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count())
1517 | }
1518 | if !c.Equal(d) {
1519 | t.Errorf("SymmetricDifference should be symmetric")
1520 | }
1521 | }
1522 |
1523 | func TestComplement(t *testing.T) {
1524 | a := New(50)
1525 | b := a.Complement()
1526 | if b.Count() != 50 {
1527 | t.Errorf("Complement failed, size should be 50, but was %d", b.Count())
1528 | }
1529 | a = New(50)
1530 | a.Set(10).Set(20).Set(42)
1531 | b = a.Complement()
1532 | if b.Count() != 47 {
1533 | t.Errorf("Complement failed, size should be 47, but was %d", b.Count())
1534 | }
1535 | }
1536 |
1537 | func TestIsSuperSet(t *testing.T) {
1538 | test := func(name string, lenS, lenSS int, overrideS, overrideSS map[int]bool, want, wantStrict bool) {
1539 | t.Run(name, func(t *testing.T) {
1540 | s := New(uint(lenS))
1541 | ss := New(uint(lenSS))
1542 |
1543 | l := lenS
1544 | if lenSS < lenS {
1545 | l = lenSS
1546 | }
1547 |
1548 | r := rand.New(rand.NewSource(42))
1549 | for i := 0; i < l; i++ {
1550 | bit := r.Intn(2) == 1
1551 | s.SetTo(uint(i), bit)
1552 | ss.SetTo(uint(i), bit)
1553 | }
1554 |
1555 | for i, v := range overrideS {
1556 | s.SetTo(uint(i), v)
1557 | }
1558 | for i, v := range overrideSS {
1559 | ss.SetTo(uint(i), v)
1560 | }
1561 |
1562 | if got := ss.IsSuperSet(s); got != want {
1563 | t.Errorf("IsSuperSet() = %v, want %v", got, want)
1564 | }
1565 | if got := ss.IsStrictSuperSet(s); got != wantStrict {
1566 | t.Errorf("IsStrictSuperSet() = %v, want %v", got, wantStrict)
1567 | }
1568 | })
1569 | }
1570 |
1571 | test("empty", 0, 0, nil, nil, true, false)
1572 | test("empty vs non-empty", 0, 100, nil, nil, true, false)
1573 | test("non-empty vs empty", 100, 0, nil, nil, true, false)
1574 | test("equal", 100, 100, nil, nil, true, false)
1575 |
1576 | test("set is shorter, subset", 100, 200, map[int]bool{50: true}, map[int]bool{50: false}, false, false)
1577 | test("set is shorter, equal", 100, 200, nil, nil, true, false)
1578 | test("set is shorter, superset", 100, 200, map[int]bool{50: false}, map[int]bool{50: true}, true, true)
1579 | test("set is shorter, neither", 100, 200, map[int]bool{50: true}, map[int]bool{50: false, 150: true}, false, false)
1580 |
1581 | test("set is longer, subset", 200, 100, map[int]bool{50: true}, map[int]bool{50: false}, false, false)
1582 | test("set is longer, equal", 200, 100, nil, nil, true, false)
1583 | test("set is longer, superset", 200, 100, nil, map[int]bool{150: true}, true, true)
1584 | test("set is longer, neither", 200, 100, map[int]bool{50: false, 150: true}, map[int]bool{50: true}, false, false)
1585 | }
1586 |
1587 | func TestDumpAsBits(t *testing.T) {
1588 | a := New(10).Set(10)
1589 | astr := "0000000000000000000000000000000000000000000000000000010000000000."
1590 | if a.DumpAsBits() != astr {
1591 | t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", astr, a.DumpAsBits())
1592 | }
1593 | var b BitSet // zero value (b.set == nil)
1594 | bstr := "."
1595 | if b.DumpAsBits() != bstr {
1596 | t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", bstr, b.DumpAsBits())
1597 | }
1598 | }
1599 |
1600 | func TestMarshalUnmarshalBinary(t *testing.T) {
1601 | a := New(1010).Set(10).Set(1001)
1602 | b := new(BitSet)
1603 |
1604 | copyBinary(t, a, b)
1605 |
1606 | // BitSets must be equal after marshalling and unmarshalling
1607 | if !a.Equal(b) {
1608 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1609 | return
1610 | }
1611 |
1612 | aSetBit := uint(128)
1613 | a = New(256).Set(aSetBit)
1614 | aExpectedMarshaledSize := 8 /* length: uint64 */ + 4*8 /* set : [4]uint64 */
1615 | aMarshaled, err := a.MarshalBinary()
1616 |
1617 | if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() {
1618 | t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes")
1619 | return
1620 | }
1621 |
1622 | shiftAmount := uint(72)
1623 | // https://github.com/bits-and-blooms/bitset/issues/114
1624 | for i := uint(0); i < shiftAmount; i++ {
1625 | a.DeleteAt(0)
1626 | }
1627 |
1628 | aExpectedMarshaledSize = 8 /* length: uint64 */ + 3*8 /* set : [3]uint64 */
1629 | aMarshaled, err = a.MarshalBinary()
1630 | if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() {
1631 | t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes")
1632 | return
1633 | }
1634 |
1635 | copyBinary(t, a, b)
1636 |
1637 | if b.Len() != 256-shiftAmount || !b.Test(aSetBit-shiftAmount) {
1638 | t.Error("Shifted bitset is not copied correctly")
1639 | }
1640 | }
1641 |
1642 | func TestMarshalUnmarshalBinaryByLittleEndian(t *testing.T) {
1643 | LittleEndian()
1644 | defer func() {
1645 | // Revert when done.
1646 | binaryOrder = binary.BigEndian
1647 | }()
1648 | a := New(1010).Set(10).Set(1001)
1649 | b := new(BitSet)
1650 |
1651 | copyBinary(t, a, b)
1652 |
1653 | // BitSets must be equal after marshalling and unmarshalling
1654 | if !a.Equal(b) {
1655 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1656 | return
1657 | }
1658 | }
1659 |
1660 | func copyBinary(t *testing.T, from encoding.BinaryMarshaler, to encoding.BinaryUnmarshaler) {
1661 | data, err := from.MarshalBinary()
1662 | if err != nil {
1663 | t.Error(err.Error())
1664 | return
1665 | }
1666 |
1667 | err = to.UnmarshalBinary(data)
1668 | if err != nil {
1669 | t.Error(err.Error())
1670 | return
1671 | }
1672 | }
1673 |
1674 | func TestMarshalUnmarshalJSON(t *testing.T) {
1675 | t.Run("value", func(t *testing.T) {
1676 | a := BitSet{}
1677 | a.Set(10).Set(1001)
1678 | data, err := json.Marshal(a)
1679 | if err != nil {
1680 | t.Error(err.Error())
1681 | return
1682 | }
1683 |
1684 | b := new(BitSet)
1685 | err = json.Unmarshal(data, b)
1686 | if err != nil {
1687 | t.Error(err.Error())
1688 | return
1689 | }
1690 |
1691 | // Bitsets must be equal after marshalling and unmarshalling
1692 | if !a.Equal(b) {
1693 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1694 | return
1695 | }
1696 | })
1697 | t.Run("pointer", func(t *testing.T) {
1698 | a := New(1010).Set(10).Set(1001)
1699 | data, err := json.Marshal(a)
1700 | if err != nil {
1701 | t.Error(err.Error())
1702 | return
1703 | }
1704 |
1705 | b := new(BitSet)
1706 | err = json.Unmarshal(data, b)
1707 | if err != nil {
1708 | t.Error(err.Error())
1709 | return
1710 | }
1711 |
1712 | // Bitsets must be equal after marshalling and unmarshalling
1713 | if !a.Equal(b) {
1714 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1715 | return
1716 | }
1717 | })
1718 | }
1719 |
1720 | func TestMarshalUnmarshalJSONWithTrailingData(t *testing.T) {
1721 | a := New(1010).Set(10).Set(1001)
1722 | data, err := json.Marshal(a)
1723 | if err != nil {
1724 | t.Error(err.Error())
1725 | return
1726 | }
1727 |
1728 | // appending some noise
1729 | data = data[:len(data)-3] // remove "
1730 | data = append(data, []byte(`AAAAAAAAAA"`)...)
1731 |
1732 | b := new(BitSet)
1733 | err = json.Unmarshal(data, b)
1734 | if err != nil {
1735 | t.Error(err.Error())
1736 | return
1737 | }
1738 |
1739 | // Bitsets must be equal after marshalling and unmarshalling
1740 | // Do not over-reading when unmarshalling
1741 | if !a.Equal(b) {
1742 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1743 | return
1744 | }
1745 | }
1746 |
1747 | func TestMarshalUnmarshalJSONByStdEncoding(t *testing.T) {
1748 | Base64StdEncoding()
1749 | a := New(1010).Set(10).Set(1001)
1750 | data, err := json.Marshal(a)
1751 | if err != nil {
1752 | t.Error(err.Error())
1753 | return
1754 | }
1755 |
1756 | b := new(BitSet)
1757 | err = json.Unmarshal(data, b)
1758 | if err != nil {
1759 | t.Error(err.Error())
1760 | return
1761 | }
1762 |
1763 | // Bitsets must be equal after marshalling and unmarshalling
1764 | if !a.Equal(b) {
1765 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits())
1766 | return
1767 | }
1768 | }
1769 |
1770 | func TestSafeSet(t *testing.T) {
1771 | b := new(BitSet)
1772 | c := b.safeSet()
1773 | outType := fmt.Sprintf("%T", c)
1774 | expType := "[]uint64"
1775 | if outType != expType {
1776 | t.Error("Expecting type: ", expType, ", gotf:", outType)
1777 | return
1778 | }
1779 | if len(c) != 0 {
1780 | t.Error("The slice should be empty")
1781 | return
1782 | }
1783 | }
1784 |
1785 | func TestSetBitsetFrom(t *testing.T) {
1786 | u := []uint64{2, 3, 5, 7, 11}
1787 | b := new(BitSet)
1788 | b.SetBitsetFrom(u)
1789 | outType := fmt.Sprintf("%T", b)
1790 | expType := "*bitset.BitSet"
1791 | if outType != expType {
1792 | t.Error("Expecting type: ", expType, ", gotf:", outType)
1793 | return
1794 | }
1795 | }
1796 |
1797 | func TestIssue116(t *testing.T) {
1798 | a := []uint64{2, 3, 5, 7, 11}
1799 | b := []uint64{2, 3, 5, 7, 11, 0, 1}
1800 | bitset1 := FromWithLength(320, a)
1801 | bitset2 := FromWithLength(320, b)
1802 | if !bitset1.Equal(bitset2) || !bitset2.Equal(bitset1) {
1803 | t.Error("Bitsets should be equal irrespective of the underlying capacity")
1804 | }
1805 | }
1806 |
1807 | func TestFrom(t *testing.T) {
1808 | u := []uint64{2, 3, 5, 7, 11}
1809 | b := From(u)
1810 | outType := fmt.Sprintf("%T", b)
1811 | expType := "*bitset.BitSet"
1812 | if outType != expType {
1813 | t.Error("Expecting type: ", expType, ", gotf:", outType)
1814 | return
1815 | }
1816 | }
1817 |
1818 | func TestWords(t *testing.T) {
1819 | b := new(BitSet)
1820 | c := b.Words()
1821 | outType := fmt.Sprintf("%T", c)
1822 | expType := "[]uint64"
1823 | if outType != expType {
1824 | t.Error("Expecting type: ", expType, ", gotf:", outType)
1825 | return
1826 | }
1827 | if len(c) != 0 {
1828 | t.Error("The slice should be empty")
1829 | return
1830 | }
1831 | }
1832 |
1833 | // Bytes is deprecated
1834 | func TestBytes(t *testing.T) {
1835 | b := new(BitSet)
1836 | c := b.Bytes()
1837 | outType := fmt.Sprintf("%T", c)
1838 | expType := "[]uint64"
1839 | if outType != expType {
1840 | t.Error("Expecting type: ", expType, ", gotf:", outType)
1841 | return
1842 | }
1843 | if len(c) != 0 {
1844 | t.Error("The slice should be empty")
1845 | return
1846 | }
1847 | }
1848 |
1849 | func TestCap(t *testing.T) {
1850 | c := Cap()
1851 | if c <= 0 {
1852 | t.Error("The uint capacity should be >= 0")
1853 | return
1854 | }
1855 | }
1856 |
1857 | func TestWordsNeededLong(t *testing.T) {
1858 | i := Cap()
1859 | out := wordsNeeded(i)
1860 | if out <= 0 {
1861 | t.Error("Unexpected value: ", out)
1862 | return
1863 | }
1864 | }
1865 |
1866 | func TestTestTooLong(t *testing.T) {
1867 | b := new(BitSet)
1868 | if b.Test(1) {
1869 | t.Error("Unexpected value: true")
1870 | return
1871 | }
1872 | }
1873 |
1874 | func TestClearTooLong(t *testing.T) {
1875 | b := new(BitSet)
1876 | c := b.Clear(1)
1877 | if b != c {
1878 | t.Error("Unexpected value")
1879 | return
1880 | }
1881 | }
1882 |
1883 | func TestClearAll(t *testing.T) {
1884 | u := []uint64{2, 3, 5, 7, 11}
1885 | b := From(u)
1886 | c := b.ClearAll()
1887 | if c.length != 320 {
1888 | t.Error("Unexpected length: ", b.length)
1889 | return
1890 | }
1891 | if c.Test(0) || c.Test(1) || c.Test(2) || c.Test(3) || c.Test(4) || c.Test(5) {
1892 | t.Error("All bits should be unset")
1893 | return
1894 | }
1895 | }
1896 |
1897 | func TestRankSelect(t *testing.T) {
1898 | u := []uint{2, 3, 5, 7, 11, 700, 1500}
1899 | b := BitSet{}
1900 | for _, v := range u {
1901 | b.Set(v)
1902 | }
1903 |
1904 | if b.Rank(5) != 3 {
1905 | t.Error("Unexpected rank")
1906 | return
1907 | }
1908 | if b.Rank(6) != 3 {
1909 | t.Error("Unexpected rank")
1910 | return
1911 | }
1912 | if b.Rank(1500) != 7 {
1913 | t.Error("Unexpected rank")
1914 | return
1915 | }
1916 | if b.Select(0) != 2 {
1917 | t.Error("Unexpected select")
1918 | return
1919 | }
1920 | if b.Select(1) != 3 {
1921 | t.Error("Unexpected select")
1922 | return
1923 | }
1924 | if b.Select(2) != 5 {
1925 | t.Error("Unexpected select")
1926 | return
1927 | }
1928 |
1929 | if b.Select(5) != 700 {
1930 | t.Error("Unexpected select")
1931 | return
1932 | }
1933 | }
1934 |
1935 | func TestFlip(t *testing.T) {
1936 | b := new(BitSet)
1937 | c := b.Flip(11)
1938 | if c.length != 12 {
1939 | t.Error("Unexpected value: ", c.length)
1940 | return
1941 | }
1942 | d := c.Flip(7)
1943 | if d.length != 12 {
1944 | t.Error("Unexpected value: ", d.length)
1945 | return
1946 | }
1947 | }
1948 |
1949 | func TestFlipRange(t *testing.T) {
1950 | b := new(BitSet)
1951 | b.Set(1).Set(3).Set(5).Set(7).Set(9).Set(11).Set(13).Set(15)
1952 | c := b.FlipRange(4, 25)
1953 | if c.length != 25 {
1954 | t.Error("Unexpected value: ", c.length)
1955 | return
1956 | }
1957 | d := c.FlipRange(8, 24)
1958 | if d.length != 25 {
1959 | t.Error("Unexpected value: ", d.length)
1960 | return
1961 | }
1962 | //
1963 | for i := uint(0); i < 256; i++ {
1964 | for j := uint(0); j <= i; j++ {
1965 | bits := New(i)
1966 | bits.FlipRange(0, j)
1967 | c := bits.Count()
1968 | if c != j {
1969 | t.Error("Unexpected value: ", c, " expected: ", j)
1970 | return
1971 | }
1972 | }
1973 | }
1974 | }
1975 |
1976 | func TestCopy(t *testing.T) {
1977 | a := New(10)
1978 | if a.Copy(nil) != 0 {
1979 | t.Error("No values should be copied")
1980 | return
1981 | }
1982 | a = New(10)
1983 | b := New(20)
1984 | if a.Copy(b) != 10 {
1985 | t.Error("Unexpected value")
1986 | return
1987 | }
1988 | }
1989 |
1990 | func TestCopyUnaligned(t *testing.T) {
1991 | a := New(16)
1992 | a.FlipRange(0, 16)
1993 | b := New(1)
1994 | a.Copy(b)
1995 | if b.Count() > b.Len() {
1996 | t.Errorf("targets copied set count (%d) should never be larger than target's length (%d)", b.Count(), b.Len())
1997 | }
1998 | if !b.Test(0) {
1999 | t.Errorf("first bit should still be set in copy: %+v", b)
2000 | }
2001 |
2002 | // Test a more complex scenario with a mix of bits set in the unaligned space to verify no bits are lost.
2003 | a = New(32)
2004 | a.Set(0).Set(3).Set(4).Set(16).Set(17).Set(29).Set(31)
2005 | b = New(19)
2006 | a.Copy(b)
2007 |
2008 | const expectedCount = 5
2009 | if b.Count() != expectedCount {
2010 | t.Errorf("targets copied set count: %d, want %d", b.Count(), expectedCount)
2011 | }
2012 |
2013 | if !(b.Test(0) && b.Test(3) && b.Test(4) && b.Test(16) && b.Test(17)) {
2014 | t.Errorf("expected set bits are not set: %+v", b)
2015 | }
2016 | }
2017 |
2018 | func TestCopyFull(t *testing.T) {
2019 | a := New(10)
2020 | b := &BitSet{}
2021 | a.CopyFull(b)
2022 | if b.length != a.length || len(b.set) != len(a.set) {
2023 | t.Error("Expected full length copy")
2024 | return
2025 | }
2026 | for i, v := range a.set {
2027 | if v != b.set[i] {
2028 | t.Error("Unexpected value")
2029 | return
2030 | }
2031 | }
2032 | }
2033 |
2034 | func TestNextSetError(t *testing.T) {
2035 | b := new(BitSet)
2036 | c, d := b.NextSet(1)
2037 | if c != 0 || d {
2038 | t.Error("Unexpected values")
2039 | return
2040 | }
2041 | }
2042 |
2043 | func TestDeleteWithBitStrings(t *testing.T) {
2044 | type testCase struct {
2045 | input []string
2046 | deleteIdx uint
2047 | expected []string
2048 | }
2049 |
2050 | testCases := []testCase{
2051 | {
2052 | input: []string{
2053 | "1110000000000000000000000000000000000000000000000000000000000001",
2054 | },
2055 | deleteIdx: uint(63),
2056 | expected: []string{
2057 | "0110000000000000000000000000000000000000000000000000000000000001",
2058 | },
2059 | },
2060 | {
2061 | input: []string{
2062 | "1000000000000000000000000000000000000000000000000000000000010101",
2063 | },
2064 | deleteIdx: uint(0),
2065 | expected: []string{
2066 | "0100000000000000000000000000000000000000000000000000000000001010",
2067 | },
2068 | },
2069 | {
2070 | input: []string{
2071 | "0000000000000000000000000000000000000000000000000000000000111000",
2072 | },
2073 | deleteIdx: uint(4),
2074 | expected: []string{
2075 | "0000000000000000000000000000000000000000000000000000000000011000",
2076 | },
2077 | },
2078 | {
2079 | input: []string{
2080 | "1000000000000000000000000000000000000000000000000000000000000001",
2081 | "1010000000000000000000000000000000000000000000000000000000000001",
2082 | },
2083 | deleteIdx: uint(63),
2084 | expected: []string{
2085 | "1000000000000000000000000000000000000000000000000000000000000001",
2086 | "0101000000000000000000000000000000000000000000000000000000000000",
2087 | },
2088 | },
2089 | {
2090 | input: []string{
2091 | "1000000000000000000000000000000000000000000000000000000000000000",
2092 | "1000000000000000000000000000000000000000000000000000000000000001",
2093 | "1000000000000000000000000000000000000000000000000000000000000001",
2094 | },
2095 | deleteIdx: uint(64),
2096 | expected: []string{
2097 | "1000000000000000000000000000000000000000000000000000000000000000",
2098 | "1100000000000000000000000000000000000000000000000000000000000000",
2099 | "0100000000000000000000000000000000000000000000000000000000000000",
2100 | },
2101 | },
2102 | {
2103 | input: []string{
2104 | "0000000000000000000000000000000000000000000000000000000000000001",
2105 | "0000000000000000000000000000000000000000000000000000000000000001",
2106 | "0000000000000000000000000000000000000000000000000000000000000001",
2107 | "0000000000000000000000000000000000000000000000000000000000000001",
2108 | "0000000000000000000000000000000000000000000000000000000000000001",
2109 | },
2110 | deleteIdx: uint(256),
2111 | expected: []string{
2112 | "0000000000000000000000000000000000000000000000000000000000000001",
2113 | "0000000000000000000000000000000000000000000000000000000000000001",
2114 | "0000000000000000000000000000000000000000000000000000000000000001",
2115 | "0000000000000000000000000000000000000000000000000000000000000001",
2116 | "0000000000000000000000000000000000000000000000000000000000000000",
2117 | },
2118 | },
2119 | }
2120 |
2121 | for _, tc := range testCases {
2122 | var input []uint64
2123 | for _, inputElement := range tc.input {
2124 | parsed, _ := strconv.ParseUint(inputElement, 2, 64)
2125 | input = append(input, parsed)
2126 | }
2127 |
2128 | var expected []uint64
2129 | for _, expectedElement := range tc.expected {
2130 | parsed, _ := strconv.ParseUint(expectedElement, 2, 64)
2131 | expected = append(expected, parsed)
2132 | }
2133 |
2134 | b := From(input)
2135 | b.DeleteAt(tc.deleteIdx)
2136 | if len(b.set) != len(expected) {
2137 | t.Errorf("Length of sets expected to be %d, but was %d", len(expected), len(b.set))
2138 | return
2139 | }
2140 | for i := range b.set {
2141 | if b.set[i] != expected[i] {
2142 | t.Errorf("Unexpected output\nExpected: %b\nGot: %b", expected[i], b.set[i])
2143 | return
2144 | }
2145 | }
2146 | }
2147 | }
2148 |
2149 | func TestDeleteWithBitSetInstance(t *testing.T) {
2150 | length := uint(256)
2151 | bitSet := New(length)
2152 |
2153 | // the indexes that get set in the bit set
2154 | indexesToSet := []uint{0, 1, 126, 127, 128, 129, 170, 171, 200, 201, 202, 203, 255}
2155 |
2156 | // the position we delete from the bitset
2157 | deleteAt := uint(127)
2158 |
2159 | // the indexes that we expect to be set after the delete
2160 | expectedToBeSet := []uint{0, 1, 126, 127, 128, 169, 170, 199, 200, 201, 202, 254}
2161 |
2162 | expected := make(map[uint]struct{})
2163 | for _, index := range expectedToBeSet {
2164 | expected[index] = struct{}{}
2165 | }
2166 |
2167 | for _, index := range indexesToSet {
2168 | bitSet.Set(index)
2169 | }
2170 |
2171 | bitSet.DeleteAt(deleteAt)
2172 |
2173 | for i := uint(0); i < length; i++ {
2174 | if _, ok := expected[i]; ok {
2175 | if !bitSet.Test(i) {
2176 | t.Errorf("Expected index %d to be set, but wasn't", i)
2177 | }
2178 | } else {
2179 | if bitSet.Test(i) {
2180 | t.Errorf("Expected index %d to not be set, but was", i)
2181 | }
2182 | }
2183 | }
2184 | }
2185 |
2186 | func TestWriteTo(t *testing.T) {
2187 | const length = 9585
2188 | const oneEvery = 97
2189 | addBuf := []byte(`12345678`)
2190 | bs := New(length)
2191 | // Add some bits
2192 | for i := uint(0); i < length; i += oneEvery {
2193 | bs = bs.Set(i)
2194 | }
2195 |
2196 | var buf bytes.Buffer
2197 | n, err := bs.WriteTo(&buf)
2198 | if err != nil {
2199 | t.Fatal(err)
2200 | }
2201 | wantSz := buf.Len() // Size of the serialized data in bytes.
2202 | if n != int64(wantSz) {
2203 | t.Errorf("want write size to be %d, got %d", wantSz, n)
2204 | }
2205 | buf.Write(addBuf) // Add additional data on stream.
2206 |
2207 | // Generate test input for regression tests:
2208 | if false {
2209 | gzout := bytes.NewBuffer(nil)
2210 | gz, err := gzip.NewWriterLevel(gzout, 9)
2211 | if err != nil {
2212 | t.Fatal(err)
2213 | }
2214 | gz.Write(buf.Bytes())
2215 | gz.Close()
2216 | t.Log("Encoded:", base64.StdEncoding.EncodeToString(gzout.Bytes()))
2217 | }
2218 |
2219 | // Read back.
2220 | bs = New(length)
2221 | n, err = bs.ReadFrom(&buf)
2222 | if err != nil {
2223 | t.Fatal(err)
2224 | }
2225 | if n != int64(wantSz) {
2226 | t.Errorf("want read size to be %d, got %d", wantSz, n)
2227 | }
2228 | // Check bits
2229 | for i := uint(0); i < length; i += oneEvery {
2230 | if !bs.Test(i) {
2231 | t.Errorf("bit %d was not set", i)
2232 | }
2233 | }
2234 |
2235 | more, err := io.ReadAll(&buf)
2236 | if err != nil {
2237 | t.Fatal(err)
2238 | }
2239 | if !bytes.Equal(more, addBuf) {
2240 | t.Fatalf("extra mismatch. got %v, want %v", more, addBuf)
2241 | }
2242 | }
2243 |
2244 | type inCompleteRetBufReader struct {
2245 | returnEvery int64
2246 | reader io.Reader
2247 | offset int64
2248 | }
2249 |
2250 | func (ir *inCompleteRetBufReader) Read(b []byte) (n int, err error) {
2251 | if ir.returnEvery > 0 {
2252 | maxRead := ir.returnEvery - (ir.offset % ir.returnEvery)
2253 | if len(b) > int(maxRead) {
2254 | b = b[:maxRead]
2255 | }
2256 | }
2257 | n, err = ir.reader.Read(b)
2258 | ir.offset += int64(n)
2259 | return
2260 | }
2261 |
2262 | func TestReadFrom(t *testing.T) {
2263 | addBuf := []byte(`12345678`) // Bytes after stream
2264 | tests := []struct {
2265 | length uint
2266 | oneEvery uint
2267 | input string // base64+gzipped
2268 | wantErr error
2269 | returnEvery int64
2270 | }{
2271 | {
2272 | length: 9585,
2273 | oneEvery: 97,
2274 | input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwMSACVgYGBg4sIgLMDAwKGARd2BgYGjAFB41noDx6IAJajw64IAajw4UoMajg4ZR4/EaP5pQh1g+MDQyNjE1M7cABAAA//9W5OoOwAQAAA==",
2275 | returnEvery: 127,
2276 | },
2277 | {
2278 | length: 1337,
2279 | oneEvery: 42,
2280 | input: "H4sIAAAAAAAC/2IAA1ZLBgYWEIPRAUQKgJkMcCZYisEBzkSSYkSTYqCxAYZGxiamZuYWgAAAAP//D0wyWbgAAAA=",
2281 | },
2282 | {
2283 | length: 1337, // Truncated input.
2284 | oneEvery: 42,
2285 | input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwARmAQIAAP//vR3xdRkAAAA=",
2286 | wantErr: io.ErrUnexpectedEOF,
2287 | },
2288 | {
2289 | length: 1337, // Empty input.
2290 | oneEvery: 42,
2291 | input: "H4sIAAAAAAAC/wEAAP//AAAAAAAAAAA=",
2292 | wantErr: io.ErrUnexpectedEOF,
2293 | },
2294 | }
2295 |
2296 | for i, test := range tests {
2297 | t.Run(fmt.Sprint(i), func(t *testing.T) {
2298 | fatalErr := func(err error) {
2299 | t.Helper()
2300 | if err != nil {
2301 | t.Fatal(err)
2302 | }
2303 | }
2304 |
2305 | var buf bytes.Buffer
2306 | b, err := base64.StdEncoding.DecodeString(test.input)
2307 | fatalErr(err)
2308 | gz, err := gzip.NewReader(bytes.NewBuffer(b))
2309 | fatalErr(err)
2310 | _, err = io.Copy(&buf, gz)
2311 | fatalErr(err)
2312 | fatalErr(gz.Close())
2313 |
2314 | bs := New(test.length)
2315 | _, err = bs.ReadFrom(&inCompleteRetBufReader{returnEvery: test.returnEvery, reader: &buf})
2316 | if err != nil {
2317 | if errors.Is(err, test.wantErr) {
2318 | // Correct, nothing more we can test.
2319 | return
2320 | }
2321 | t.Fatalf("did not get expected error %v, got %v", test.wantErr, err)
2322 | } else {
2323 | if test.wantErr != nil {
2324 | t.Fatalf("did not get expected error %v", test.wantErr)
2325 | }
2326 | }
2327 | fatalErr(err)
2328 |
2329 | // Test if correct bits are set.
2330 | for i := uint(0); i < test.length; i++ {
2331 | want := i%test.oneEvery == 0
2332 | got := bs.Test(i)
2333 | if want != got {
2334 | t.Errorf("bit %d was %v, should be %v", i, got, want)
2335 | }
2336 | }
2337 |
2338 | more, err := io.ReadAll(&buf)
2339 | fatalErr(err)
2340 |
2341 | if !bytes.Equal(more, addBuf) {
2342 | t.Errorf("extra mismatch. got %v, want %v", more, addBuf)
2343 | }
2344 | })
2345 | }
2346 | }
2347 |
2348 | func TestSetAll(t *testing.T) {
2349 | test := func(name string, bs *BitSet, want uint) {
2350 | t.Run(name, func(t *testing.T) {
2351 | bs.SetAll()
2352 | if bs.Count() != want {
2353 | t.Errorf("expected %d bits to be set, got %d", want, bs.Count())
2354 | }
2355 | })
2356 | }
2357 |
2358 | test("nil", nil, 0)
2359 | for _, length := range []uint{0, 1, 10, 63, 64, 65, 100, 640} {
2360 | test(fmt.Sprintf("length %d", length), New(length), length)
2361 | }
2362 | }
2363 |
2364 | func TestShiftLeft(t *testing.T) {
2365 | data := []uint{5, 28, 45, 72, 89, 560}
2366 |
2367 | test := func(name string, bits uint) {
2368 | t.Run(name, func(t *testing.T) {
2369 | b := New(200)
2370 | for _, i := range data {
2371 | b.Set(i)
2372 | }
2373 |
2374 | b.ShiftLeft(bits)
2375 |
2376 | if int(b.Count()) != len(data) {
2377 | t.Error("bad bits count")
2378 | }
2379 |
2380 | for _, i := range data {
2381 | if !b.Test(i + bits) {
2382 | t.Errorf("bit %v is not set", i+bits)
2383 | }
2384 | }
2385 | })
2386 | }
2387 |
2388 | test("zero", 0)
2389 | test("no page change", 19)
2390 | test("shift to full page", 38)
2391 | test("full page shift", 64)
2392 | test("no page split", 80)
2393 | test("with page split", 114)
2394 | test("with extension", 242)
2395 | test("with extra word", 16)
2396 | }
2397 |
2398 | func TestShiftRight(t *testing.T) {
2399 | data := []uint{5, 28, 45, 72, 89}
2400 |
2401 | test := func(name string, bits uint) {
2402 | t.Run(name, func(t *testing.T) {
2403 | b := New(200)
2404 | for _, i := range data {
2405 | b.Set(i)
2406 | }
2407 |
2408 | b.ShiftRight(bits)
2409 |
2410 | count := 0
2411 | for _, i := range data {
2412 | if i >= bits {
2413 | count++
2414 |
2415 | if !b.Test(i - bits) {
2416 | t.Errorf("bit %v is not set", i-bits)
2417 | }
2418 | }
2419 | }
2420 |
2421 | if int(b.Count()) != count {
2422 | t.Errorf("bad bits count: expected %d, got %d", count, b.Count())
2423 | }
2424 | })
2425 | }
2426 |
2427 | test("zero", 0)
2428 | test("no page change", 3)
2429 | test("no page split", 20)
2430 | test("with page split", 40)
2431 | test("full page shift", 64)
2432 | test("with extension", 70)
2433 | test("full shift", 89)
2434 | test("remove all", 242)
2435 | }
2436 |
2437 | func TestShiftRightFull(t *testing.T) {
2438 | testCases := []struct{
2439 | data []uint
2440 | shiftDistance uint
2441 | }{
2442 | {
2443 | []uint{20}, 20,
2444 | },
2445 | {
2446 | []uint{0, 20, 40, 1260, 1280}, 1,
2447 | },
2448 | {
2449 | []uint{0, 20, 40, 1260, 1280}, 1281,
2450 | },
2451 | {
2452 | []uint{961}, 64,
2453 | },
2454 | }
2455 |
2456 | test := func(data []uint, shiftDistance uint) {
2457 | b := New(0)
2458 | for i := range data {
2459 | b.Set(data[i])
2460 | }
2461 | b.ShiftRight(shiftDistance)
2462 | for i := range data {
2463 | shiftedBit := int(data[i])-int(shiftDistance)
2464 | if shiftedBit >= 0 {
2465 | if !b.Test(uint(shiftedBit)) {
2466 | t.Errorf("bit %d should be set after ShiftRight(%d) if bit %d was set prior", data[i]-shiftDistance, shiftDistance, data[i])
2467 | }
2468 | }
2469 | }
2470 | }
2471 |
2472 | for i := range testCases {
2473 | test(testCases[i].data, testCases[i].shiftDistance)
2474 | }
2475 | }
2476 |
2477 | func TestWord(t *testing.T) {
2478 | data := []uint64{0x0bfd85fc01af96dd, 0x3fe212a7eae11414, 0x7aa412221245dee1, 0x557092c1711306d5}
2479 | testCases := map[string]struct {
2480 | index uint
2481 | expected uint64
2482 | }{
2483 | "first word": {
2484 | index: 0,
2485 | expected: 0x0bfd85fc01af96dd,
2486 | },
2487 | "third word": {
2488 | index: 128,
2489 | expected: 0x7aa412221245dee1,
2490 | },
2491 | "off the edge": {
2492 | index: 256,
2493 | expected: 0,
2494 | },
2495 | "way off the edge": {
2496 | index: 63235235,
2497 | expected: 0,
2498 | },
2499 | "split between two words": {
2500 | index: 96,
2501 | expected: 0x1245dee13fe212a7,
2502 | },
2503 | "partly off edge": {
2504 | index: 254,
2505 | expected: 1,
2506 | },
2507 | "skip one nibble": {
2508 | index: 4,
2509 | expected: 0x40bfd85fc01af96d,
2510 | },
2511 | "slightly offset results": {
2512 | index: 65,
2513 | expected: 0x9ff10953f5708a0a,
2514 | },
2515 | }
2516 |
2517 | for name, testCase := range testCases {
2518 | t.Run(name, func(t *testing.T) {
2519 | bitSet := From(data)
2520 | output := bitSet.GetWord64AtBit(testCase.index)
2521 |
2522 | if output != testCase.expected {
2523 | t.Errorf("Word should have returned %d for input %d, but returned %d", testCase.expected, testCase.index, output)
2524 | }
2525 | })
2526 | }
2527 | }
2528 |
2529 | func TestPreviousSet(t *testing.T) {
2530 | v := New(128)
2531 | v.Set(0)
2532 | v.Set(2)
2533 | v.Set(4)
2534 | v.Set(120)
2535 | for _, tt := range []struct {
2536 | index uint
2537 | want uint
2538 | wantFound bool
2539 | }{
2540 | {0, 0, true},
2541 | {1, 0, true},
2542 | {2, 2, true},
2543 | {3, 2, true},
2544 | {4, 4, true},
2545 | {5, 4, true},
2546 | {100, 4, true},
2547 | {120, 120, true},
2548 | {121, 120, true},
2549 | {1024, 0, false},
2550 | } {
2551 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2552 | got, found := v.PreviousSet(tt.index)
2553 | if got != tt.want || found != tt.wantFound {
2554 | t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2555 | }
2556 | })
2557 | }
2558 | v.ClearAll()
2559 | for _, tt := range []struct {
2560 | index uint
2561 | want uint
2562 | wantFound bool
2563 | }{
2564 | {0, 0, false},
2565 | {120, 0, false},
2566 | {1024, 0, false},
2567 | } {
2568 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2569 | got, found := v.PreviousSet(tt.index)
2570 | if got != tt.want || found != tt.wantFound {
2571 | t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2572 | }
2573 | })
2574 | }
2575 | }
2576 |
2577 | func TestPreviousClear(t *testing.T) {
2578 | v := New(128)
2579 | v.Set(0)
2580 | v.Set(2)
2581 | v.Set(4)
2582 | v.Set(120)
2583 | for _, tt := range []struct {
2584 | index uint
2585 | want uint
2586 | wantFound bool
2587 | }{
2588 | {0, 0, false},
2589 | {1, 1, true},
2590 | {2, 1, true},
2591 | {3, 3, true},
2592 | {4, 3, true},
2593 | {5, 5, true},
2594 | {100, 100, true},
2595 | {120, 119, true},
2596 | {121, 121, true},
2597 | {1024, 0, false},
2598 | } {
2599 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2600 | got, found := v.PreviousClear(tt.index)
2601 | if got != tt.want || found != tt.wantFound {
2602 | t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2603 | }
2604 | })
2605 | }
2606 | v.SetAll()
2607 | for _, tt := range []struct {
2608 | index uint
2609 | want uint
2610 | wantFound bool
2611 | }{
2612 | {0, 0, false},
2613 | {120, 0, false},
2614 | {1024, 0, false},
2615 | } {
2616 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
2617 | got, found := v.PreviousClear(tt.index)
2618 | if got != tt.want || found != tt.wantFound {
2619 | t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound)
2620 | }
2621 | })
2622 | }
2623 | }
2624 |
2625 | func TestBitSetOnesBetween(t *testing.T) {
2626 | testCases := []struct {
2627 | name string
2628 | input *BitSet
2629 | from uint
2630 | to uint
2631 | expected uint
2632 | }{
2633 | {"empty range", New(64).Set(0).Set(1), 5, 5, 0},
2634 | {"invalid range", New(64).Set(0).Set(1), 5, 3, 0},
2635 | {"single word", New(64).Set(1).Set(2).Set(3), 1, 3, 2},
2636 | {"single word full", New(64).Set(0).Set(1).Set(2).Set(3), 0, 4, 4},
2637 | {"cross word boundary", New(128).Set(63).Set(64).Set(65), 63, 66, 3},
2638 | {"multiple words", New(256).Set(0).Set(63).Set(64).Set(127).Set(128), 0, 129, 5},
2639 | {"large gap", New(256).Set(0).Set(100).Set(200), 0, 201, 3},
2640 | }
2641 |
2642 | for _, tc := range testCases {
2643 | t.Run(tc.name, func(t *testing.T) {
2644 | got := tc.input.OnesBetween(tc.from, tc.to)
2645 | if got != tc.expected {
2646 | t.Errorf("OnesBetween(%d, %d) = %d, want %d",
2647 | tc.from, tc.to, got, tc.expected)
2648 | }
2649 | })
2650 | }
2651 |
2652 | // Property-based testing
2653 | const numTests = 1e5
2654 | seed := time.Now().UnixNano()
2655 | rng := rand.New(rand.NewSource(seed))
2656 | t.Logf("Seed: %d", seed)
2657 |
2658 | for i := 0; i < numTests; i++ {
2659 | size := uint(rng.Intn(1024) + 64)
2660 | bs := New(size)
2661 |
2662 | // Set random bits
2663 | for j := 0; j < int(size/4); j++ {
2664 | bs.Set(uint(rng.Intn(int(size))))
2665 | }
2666 |
2667 | // Generate random range
2668 | from := uint(rng.Intn(int(size)))
2669 | to := from + uint(rng.Intn(int(size-from)))
2670 |
2671 | // Compare with naive implementation
2672 | got := bs.OnesBetween(from, to)
2673 | want := uint(0)
2674 | for j := from; j < to; j++ {
2675 | if bs.Test(j) {
2676 | want++
2677 | }
2678 | }
2679 |
2680 | if got != want {
2681 | t.Errorf("Case %d: OnesBetween(%d, %d) = %d, want %d",
2682 | i, from, to, got, want)
2683 | }
2684 | }
2685 | }
2686 |
2687 | func BenchmarkBitSetOnesBetween(b *testing.B) {
2688 | sizes := []int{64, 256, 1024, 4096, 16384}
2689 | densities := []float64{0.1, 0.5, 0.9} // Different bit densities to test
2690 | rng := rand.New(rand.NewSource(42))
2691 |
2692 | for _, size := range sizes {
2693 | for _, density := range densities {
2694 | // Create bitset with given density
2695 | bs := New(uint(size))
2696 | for i := 0; i < int(float64(size)*density); i++ {
2697 | bs.Set(uint(rng.Intn(size)))
2698 | }
2699 |
2700 | // Generate random ranges
2701 | ranges := make([][2]uint, 1000)
2702 | for i := range ranges {
2703 | from := uint(rng.Intn(size))
2704 | to := from + uint(rng.Intn(size-int(from)))
2705 | ranges[i] = [2]uint{from, to}
2706 | }
2707 |
2708 | name := fmt.Sprintf("size=%d/density=%.1f", size, density)
2709 | b.Run(name, func(b *testing.B) {
2710 | for i := 0; i < b.N; i++ {
2711 | r := ranges[i%len(ranges)]
2712 | _ = bs.OnesBetween(r[0], r[1])
2713 | }
2714 | })
2715 | }
2716 | }
2717 | }
2718 |
2719 | func generatePextTestCases(n int) [][2]uint64 {
2720 | cases := make([][2]uint64, n)
2721 | for i := range cases {
2722 | cases[i][0] = rand.Uint64()
2723 | cases[i][1] = rand.Uint64()
2724 | }
2725 | return cases
2726 | }
2727 |
2728 | func BenchmarkPEXT(b *testing.B) {
2729 | // Generate test cases
2730 | testCases := generatePextTestCases(1000)
2731 |
2732 | b.ResetTimer()
2733 |
2734 | var r uint64
2735 | for i := 0; i < b.N; i++ {
2736 | tc := testCases[i%len(testCases)]
2737 | r = pext(tc[0], tc[1])
2738 | }
2739 | _ = r // prevent optimization
2740 | }
2741 |
2742 | func BenchmarkPDEP(b *testing.B) {
2743 | // Generate test cases
2744 | testCases := generatePextTestCases(1000)
2745 |
2746 | b.ResetTimer()
2747 |
2748 | var r uint64
2749 | for i := 0; i < b.N; i++ {
2750 | tc := testCases[i%len(testCases)]
2751 | r = pdep(tc[0], tc[1])
2752 | }
2753 | _ = r // prevent optimization
2754 | }
2755 |
2756 | func TestPext(t *testing.T) {
2757 | const numTests = 1e6
2758 | seed := time.Now().UnixNano()
2759 | rng := rand.New(rand.NewSource(seed))
2760 | t.Logf("Seed: %d", seed)
2761 |
2762 | for i := 0; i < numTests; i++ {
2763 | w := rng.Uint64()
2764 | m := rng.Uint64()
2765 | result := pext(w, m)
2766 | popCount := bits.OnesCount64(m)
2767 |
2768 | // Test invariants
2769 | if popCount > 0 && result >= (uint64(1)< bits.OnesCount64(w&m) {
2775 | t.Fatalf("Case %d: result has more 1s than masked input: result=%x, input&mask=%x",
2776 | i, result, w&m)
2777 | }
2778 |
2779 | // Test that extracted bits preserve relative ordering:
2780 | // For each bit position that's set in the mask (m):
2781 | // 1. Extract a bit from result (resultCopy&1)
2782 | // 2. Get corresponding input bit from w (w>>j&1)
2783 | // 3. XOR them - if different, bits weren't preserved correctly
2784 | resultCopy := result
2785 | for j := 0; j < 64; j++ {
2786 | // Check if mask bit is set at position j
2787 | if m&(uint64(1)<>j&1 gets bit j from original input
2790 | // XOR (^) checks if they match
2791 | if (resultCopy&1)^(w>>j&1) != 0 {
2792 | t.Fatalf("Case %d: bit ordering violation at position %d", i, j)
2793 | }
2794 | // Shift to examine next bit in packed result
2795 | resultCopy >>= 1
2796 | }
2797 | }
2798 | }
2799 | }
2800 |
2801 | func TestPdep(t *testing.T) {
2802 | const numTests = 1e6
2803 | seed := time.Now().UnixNano()
2804 | rng := rand.New(rand.NewSource(seed))
2805 | t.Logf("Seed: %d", seed)
2806 |
2807 | for i := 0; i < numTests; i++ {
2808 | w := rng.Uint64() // value to deposit
2809 | m := rng.Uint64() // mask
2810 | result := pdep(w, m)
2811 | popCount := bits.OnesCount64(m)
2812 |
2813 | // Test invariants
2814 | if result&^m != 0 {
2815 | t.Fatalf("Case %d: result %x has bits set outside of mask %x",
2816 | i, result, m)
2817 | }
2818 |
2819 | if bits.OnesCount64(result) > bits.OnesCount64(w) {
2820 | t.Fatalf("Case %d: result has more 1s than input: result=%x, input=%x",
2821 | i, result, w)
2822 | }
2823 |
2824 | // Verify by using PEXT to extract bits back
2825 | // The composition of PEXT(PDEP(x,m),m) should equal x masked to popcount bits
2826 | extracted := pext(result, m)
2827 | maskBits := (uint64(1) << popCount) - 1
2828 | if (extracted & maskBits) != (w & maskBits) {
2829 | t.Fatalf("Case %d: PEXT(PDEP(w,m),m) != w: got=%x, want=%x (w=%x, m=%x)",
2830 | i, extracted&maskBits, w&maskBits, w, m)
2831 | }
2832 | }
2833 | }
2834 |
2835 | func TestBitSetExtract(t *testing.T) {
2836 | // Property-based tests
2837 | const numTests = 1e4
2838 | seed := time.Now().UnixNano()
2839 | rng := rand.New(rand.NewSource(seed))
2840 | t.Logf("Seed: %d", seed)
2841 |
2842 | for i := 0; i < numTests; i++ {
2843 | // Create random bitsets
2844 | size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits
2845 | src := New(size)
2846 | mask := New(size)
2847 | dst := New(size)
2848 |
2849 | // Set random bits
2850 | for j := 0; j < int(size/4); j++ {
2851 | src.Set(uint(rng.Intn(int(size))))
2852 | mask.Set(uint(rng.Intn(int(size))))
2853 | }
2854 |
2855 | // Extract bits
2856 | src.ExtractTo(mask, dst)
2857 |
2858 | // Test invariants
2859 | if dst.Count() > src.IntersectionCardinality(mask) {
2860 | t.Errorf("Case %d: result has more 1s than masked input", i)
2861 | }
2862 |
2863 | // Test bits are properly extracted and packed
2864 | pos := uint(0)
2865 | for j := uint(0); j < size; j++ {
2866 | if mask.Test(j) {
2867 | if src.Test(j) != dst.Test(pos) {
2868 | t.Errorf("Case %d: bit ordering violation at source position %d", i, j)
2869 | }
2870 | pos++
2871 | }
2872 | }
2873 | }
2874 |
2875 | // Keep existing test cases
2876 | testCases := []struct {
2877 | name string
2878 | src *BitSet // source bits
2879 | mask *BitSet // mask bits
2880 | expected *BitSet // expected extracted bits
2881 | }{
2882 | {
2883 | name: "single bit",
2884 | src: New(8).Set(1), // 0b01
2885 | mask: New(8).Set(1), // 0b01
2886 | expected: New(8).Set(0), // 0b1
2887 | },
2888 | {
2889 | name: "two sequential bits",
2890 | src: New(8).Set(0).Set(1), // 0b11
2891 | mask: New(8).Set(0).Set(1), // 0b11
2892 | expected: New(8).Set(0).Set(1), // 0b11
2893 | },
2894 | {
2895 | name: "sparse bits",
2896 | src: New(16).Set(0).Set(10), // 0b10000000001
2897 | mask: New(16).Set(0).Set(5).Set(10), // 0b10000100001
2898 | expected: New(8).Set(0).Set(2), // 0b101
2899 | },
2900 | {
2901 | name: "masked off bits",
2902 | src: New(8).Set(0).Set(1).Set(2).Set(3), // 0b1111
2903 | mask: New(8).Set(0).Set(2), // 0b0101
2904 | expected: New(8).Set(0).Set(1), // 0b11
2905 | },
2906 | {
2907 | name: "cross word boundary",
2908 | src: New(128).Set(63).Set(64).Set(65),
2909 | mask: New(128).Set(63).Set(64).Set(65),
2910 | expected: New(8).Set(0).Set(1).Set(2),
2911 | },
2912 | {
2913 | name: "large gap",
2914 | src: New(256).Set(0).Set(100).Set(200),
2915 | mask: New(256).Set(0).Set(100).Set(200),
2916 | expected: New(8).Set(0).Set(1).Set(2),
2917 | },
2918 | {
2919 | name: "extracting zeros",
2920 | src: New(8), // 0b00
2921 | mask: New(8).Set(0).Set(1).Set(2), // 0b111
2922 | expected: New(8), // 0b000
2923 | },
2924 | }
2925 |
2926 | for _, tc := range testCases {
2927 | t.Run(tc.name, func(t *testing.T) {
2928 | dst := New(tc.expected.Len())
2929 | tc.src.ExtractTo(tc.mask, dst)
2930 | if !dst.Equal(tc.expected) {
2931 | t.Errorf("got %v, expected %v", dst, tc.expected)
2932 | }
2933 |
2934 | // Verify inverse relationship within the mask bits
2935 | deposited := New(tc.src.Len())
2936 | dst.DepositTo(tc.mask, deposited)
2937 |
2938 | // Only bits selected by the mask should match between source and deposited
2939 | maskedSource := tc.src.Intersection(tc.mask)
2940 | maskedDeposited := deposited.Intersection(tc.mask)
2941 |
2942 | if !maskedSource.Equal(maskedDeposited) {
2943 | t.Error("DepositTo(ExtractTo(x,m),m) doesn't preserve masked source bits")
2944 | }
2945 | })
2946 | }
2947 | }
2948 |
2949 | func TestBitSetDeposit(t *testing.T) {
2950 | // Property-based tests
2951 | const numTests = 1e4
2952 | seed := time.Now().UnixNano()
2953 | rng := rand.New(rand.NewSource(seed))
2954 | t.Logf("Seed: %d", seed)
2955 |
2956 | for i := 0; i < numTests; i++ {
2957 | // Create random bitsets
2958 | size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits
2959 | src := New(size)
2960 | mask := New(size)
2961 | dst := New(size)
2962 |
2963 | // Set random bits
2964 | for j := 0; j < int(size/4); j++ {
2965 | src.Set(uint(rng.Intn(int(mask.Count() + 1))))
2966 | mask.Set(uint(rng.Intn(int(size))))
2967 | }
2968 |
2969 | // Deposit bits
2970 | src.DepositTo(mask, dst)
2971 |
2972 | // Test invariants
2973 | if dst.Count() > src.Count() {
2974 | t.Errorf("Case %d: result has more 1s than input", i)
2975 | }
2976 |
2977 | if (dst.Bytes()[0] &^ mask.Bytes()[0]) != 0 {
2978 | t.Errorf("Case %d: result has bits set outside of mask", i)
2979 | }
2980 |
2981 | // Extract bits back and verify
2982 | extracted := New(size)
2983 | dst.ExtractTo(mask, extracted)
2984 | maskBits := New(size)
2985 | for j := uint(0); j < mask.Count(); j++ {
2986 | maskBits.Set(j)
2987 | }
2988 | srcMasked := src.Clone()
2989 | srcMasked.InPlaceIntersection(maskBits)
2990 | if !extracted.Equal(srcMasked) {
2991 | t.Errorf("Case %d: ExtractTo(DepositTo(x,m),m) != x", i)
2992 | }
2993 | }
2994 |
2995 | // Keep existing test cases
2996 | testCases := []struct {
2997 | name string
2998 | src *BitSet // source bits (packed in low positions)
2999 | mask *BitSet // mask bits (positions to deposit into)
3000 | dst *BitSet // destination bits (initially set)
3001 | expected *BitSet // expected result
3002 | }{
3003 | {
3004 | name: "sparse bits",
3005 | src: New(8).Set(0), // 0b01
3006 | mask: New(8).Set(0).Set(5), // 0b100001
3007 | expected: New(8).Set(0), // 0b000001
3008 | },
3009 | {
3010 | name: "masked off bits",
3011 | src: New(8).Set(0).Set(1), // 0b11
3012 | mask: New(8).Set(0).Set(2), // 0b101
3013 | expected: New(8).Set(0).Set(2), // 0b101
3014 | },
3015 | {
3016 | name: "cross word boundary",
3017 | src: New(8).Set(0).Set(1), // 0b11
3018 | mask: New(128).Set(63).Set(64), // bits across word boundary
3019 | expected: New(128).Set(63).Set(64), // bits deposited across boundary
3020 | },
3021 | {
3022 | name: "large gaps",
3023 | src: New(8).Set(0).Set(1), // 0b11
3024 | mask: New(128).Set(0).Set(100), // widely spaced bits
3025 | expected: New(128).Set(0).Set(100), // deposited into sparse positions
3026 | },
3027 | {
3028 | name: "depositing zeros",
3029 | src: New(8), // 0b00
3030 | mask: New(8).Set(0).Set(1).Set(2), // 0b111
3031 | expected: New(8), // 0b000
3032 | },
3033 | {
3034 | name: "preserve unmasked bits",
3035 | src: New(8), // empty source
3036 | mask: New(8), // empty mask
3037 | dst: New(8).Set(1).Set(2).Set(3), // dst has some bits set
3038 | expected: New(8).Set(1).Set(2).Set(3), // should remain unchanged
3039 | },
3040 | {
3041 | name: "preserve bits outside mask within word",
3042 | src: New(8).Set(0), // source has bit 0 set
3043 | mask: New(8).Set(1), // only depositing into bit 1
3044 | dst: New(8).Set(0).Set(2).Set(3), // dst has bits 0,2,3 set
3045 | expected: New(8).Set(0).Set(1).Set(2).Set(3), // bits 0,2,3 should remain, bit 1 should be set
3046 | },
3047 | }
3048 |
3049 | for _, tc := range testCases {
3050 | t.Run(tc.name, func(t *testing.T) {
3051 | var dst *BitSet
3052 | if tc.dst == nil {
3053 | dst = New(tc.expected.Len())
3054 | } else {
3055 | dst = tc.dst.Clone()
3056 | }
3057 | tc.src.DepositTo(tc.mask, dst)
3058 | if !dst.Equal(tc.expected) {
3059 | t.Errorf("got %v, expected %v", dst, tc.expected)
3060 | }
3061 |
3062 | // Verify inverse relationship for set bits up to mask cardinality
3063 | extracted := New(tc.src.Len())
3064 | dst.ExtractTo(tc.mask, extracted)
3065 |
3066 | if !extracted.Equal(tc.src) {
3067 | t.Error("ExtractTo(DepositTo(x,m),m) doesn't preserve source bits that were selected by mask")
3068 | }
3069 | })
3070 | }
3071 | }
3072 |
3073 | func BenchmarkBitSetExtractDeposit(b *testing.B) {
3074 | sizes := []int{64, 256, 1024, 4096, 16384, 2 << 15}
3075 | rng := rand.New(rand.NewSource(42)) // fixed seed for reproducibility
3076 |
3077 | for _, size := range sizes {
3078 | // Create source with random bits
3079 | src := New(uint(size))
3080 | for i := 0; i < size/4; i++ { // Set ~25% of bits
3081 | src.Set(uint(rng.Intn(size)))
3082 | }
3083 |
3084 | // Create mask with random bits
3085 | mask := New(uint(size))
3086 | for i := 0; i < size/4; i++ {
3087 | mask.Set(uint(rng.Intn(size)))
3088 | }
3089 |
3090 | b.Run(fmt.Sprintf("size=%d/fn=ExtractTo", size), func(b *testing.B) {
3091 | dst := New(uint(size))
3092 | b.ReportAllocs()
3093 | b.ResetTimer()
3094 | for i := 0; i < b.N; i++ {
3095 | src.ExtractTo(mask, dst)
3096 | dst.ClearAll()
3097 | }
3098 | })
3099 |
3100 | b.Run(fmt.Sprintf("size=%d/fn=DepositTo", size), func(b *testing.B) {
3101 | dst := New(uint(size))
3102 | b.ReportAllocs()
3103 | b.ResetTimer()
3104 | for i := 0; i < b.N; i++ {
3105 | src.DepositTo(mask, dst)
3106 | dst.ClearAll()
3107 | }
3108 | })
3109 | }
3110 | }
3111 |
--------------------------------------------------------------------------------