├── .github ├── FUNDING.yml ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── scorecard.yml │ └── test.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── azure-pipelines.yml ├── bitset.go ├── bitset_benchmark_test.go ├── bitset_iter.go ├── bitset_iter_test.go ├── bitset_test.go ├── cmd └── pextgen │ └── main.go ├── go.mod ├── go.sum ├── pext.gen.go ├── popcnt.go ├── popcnt_test.go └── select.go /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | groups: 13 | github-actions: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 36 | with: 37 | persist-credentials: false 38 | 39 | - name: "Run analysis" 40 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 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@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 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 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | permissions: 5 | contents: read 6 | jobs: 7 | test: 8 | strategy: 9 | matrix: 10 | go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x,1.20.x,1.21.x,1.22.x,1.23.x,1.24.x] 11 | os: [ubuntu-latest, macos-latest, windows-latest] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - name: Install Go 15 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 #v5.3.0 16 | with: 17 | go-version: ${{ matrix.go-version }} 18 | - name: Checkout code 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.0 20 | - name: Test 21 | run: go test ./... 22 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | branches: 6 | except: 7 | - release 8 | 9 | branches: 10 | only: 11 | - master 12 | - travis 13 | 14 | go: 15 | - "1.11.x" 16 | - tip 17 | 18 | matrix: 19 | allow_failures: 20 | - go: tip 21 | 22 | before_install: 23 | - if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi; 24 | - if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi; 25 | - go get github.com/mattn/goveralls 26 | 27 | before_script: 28 | - make deps 29 | 30 | script: 31 | - make qa 32 | 33 | after_failure: 34 | - cat ./target/test/report.xml 35 | 36 | after_success: 37 | - if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi; 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitset 2 | 3 | *Go language library to map between non-negative integers and boolean values* 4 | 5 | [![Test](https://github.com/bits-and-blooms/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset) 7 | [![PkgGoDev](https://pkg.go.dev/badge/github.com/bits-and-blooms/bitset?tab=doc)](https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc) 8 | 9 | 10 | This library is part of the [awesome go collection](https://github.com/avelino/awesome-go). It is used in production by several important systems: 11 | 12 | * [beego](https://github.com/beego/beego) 13 | * [CubeFS](https://github.com/cubefs/cubefs) 14 | * [Amazon EKS Distro](https://github.com/aws/eks-distro) 15 | * [sourcegraph](https://github.com/sourcegraph/sourcegraph-public-snapshot) 16 | * [torrent](https://github.com/anacrolix/torrent) 17 | 18 | 19 | ## Description 20 | 21 | Package bitset implements bitsets, a mapping between non-negative integers and boolean values. 22 | It should be more efficient than map[uint] bool. 23 | 24 | It provides methods for setting, clearing, flipping, and testing individual integers. 25 | 26 | But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits. 27 | 28 | BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk automatically, but `Shrink` and `Compact` methods are available. On creation, a hint can be given for the number of bits that will be used. 29 | 30 | Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining. 31 | 32 | ### Example use: 33 | 34 | ```go 35 | package main 36 | 37 | import ( 38 | "fmt" 39 | "math/rand" 40 | 41 | "github.com/bits-and-blooms/bitset" 42 | ) 43 | 44 | func main() { 45 | fmt.Printf("Hello from BitSet!\n") 46 | var b bitset.BitSet 47 | // play some Go Fish 48 | for i := 0; i < 100; i++ { 49 | card1 := uint(rand.Intn(52)) 50 | card2 := uint(rand.Intn(52)) 51 | b.Set(card1) 52 | if b.Test(card2) { 53 | fmt.Println("Go Fish!") 54 | } 55 | b.Clear(card1) 56 | } 57 | 58 | // Chaining 59 | b.Set(10).Set(11) 60 | 61 | for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) { 62 | fmt.Println("The following bit is set:", i) 63 | } 64 | if b.Intersection(bitset.New(100).Set(10)).Count() == 1 { 65 | fmt.Println("Intersection works.") 66 | } else { 67 | fmt.Println("Intersection doesn't work???") 68 | } 69 | } 70 | ``` 71 | 72 | If you have Go 1.23 or better, you can iterate over the set bits like so: 73 | 74 | ```go 75 | for i := range b.EachSet() {} 76 | ``` 77 | 78 | 79 | 80 | Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc 81 | 82 | ## Serialization 83 | 84 | 85 | You may serialize a bitset safely and portably to a stream 86 | of bytes as follows: 87 | ```Go 88 | const length = 9585 89 | const oneEvery = 97 90 | bs := bitset.New(length) 91 | // Add some bits 92 | for i := uint(0); i < length; i += oneEvery { 93 | bs = bs.Set(i) 94 | } 95 | 96 | var buf bytes.Buffer 97 | n, err := bs.WriteTo(&buf) 98 | if err != nil { 99 | // failure 100 | } 101 | // Here n == buf.Len() 102 | ``` 103 | You can later deserialize the result as follows: 104 | 105 | ```Go 106 | // Read back from buf 107 | bs = bitset.New() 108 | n, err = bs.ReadFrom(&buf) 109 | if err != nil { 110 | // error 111 | } 112 | // n is the number of bytes read 113 | ``` 114 | 115 | The `ReadFrom` function attempts to read the data into the existing 116 | BitSet instance, to minimize memory allocations. 117 | 118 | 119 | *Performance tip*: 120 | When reading and writing to a file or a network connection, you may get better performance by 121 | wrapping your streams with `bufio` instances. 122 | 123 | E.g., 124 | ```Go 125 | f, err := os.Create("myfile") 126 | w := bufio.NewWriter(f) 127 | ``` 128 | ```Go 129 | f, err := os.Open("myfile") 130 | r := bufio.NewReader(f) 131 | ``` 132 | 133 | ## Memory Usage 134 | 135 | The memory usage of a bitset using `N` bits is at least `N/8` bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](https://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring). 136 | 137 | The `roaring` library allows you to go back and forth between compressed Roaring bitmaps and the conventional bitset instances: 138 | ```Go 139 | mybitset := roaringbitmap.ToBitSet() 140 | newroaringbitmap := roaring.FromBitSet(mybitset) 141 | ``` 142 | 143 | 144 | ### Goroutine safety 145 | 146 | In general, it's not safe to access the same BitSet using different goroutines--they are unsynchronized for performance. 147 | 148 | Should you want to access a BitSet from more than one goroutine, you should provide synchronization. Typically this is done by using channels to pass the *BitSet around (in Go style; so there is only ever one owner), or by using `sync.Mutex` to serialize operations on BitSets. 149 | 150 | ## Installation 151 | 152 | ```bash 153 | go get github.com/bits-and-blooms/bitset 154 | ``` 155 | 156 | ## Contributing 157 | 158 | If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)") 159 | 160 | ## Running all tests 161 | 162 | Before committing the code, please check if it passes tests, has adequate coverage, etc. 163 | ```bash 164 | go test 165 | go test -cover 166 | ``` 167 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | cnt += popcntMaskSlice(b.set[:l], compare.set[:l]) 909 | cnt += popcntSlice(b.set[l:]) 910 | return uint(cnt) 911 | } 912 | 913 | // InPlaceDifference computes the difference of base set and other set 914 | // This is the BitSet equivalent of &^ (and not) 915 | func (b *BitSet) InPlaceDifference(compare *BitSet) { 916 | panicIfNull(b) 917 | panicIfNull(compare) 918 | l := compare.wordCount() 919 | if l > b.wordCount() { 920 | l = b.wordCount() 921 | } 922 | if l <= 0 { 923 | return 924 | } 925 | // bounds check elimination 926 | data, cmpData := b.set, compare.set 927 | _ = data[l-1] 928 | _ = cmpData[l-1] 929 | for i := 0; i < l; i++ { 930 | data[i] &^= cmpData[i] 931 | } 932 | } 933 | 934 | // Convenience function: return two bitsets ordered by 935 | // increasing length. Note: neither can be nil 936 | func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) { 937 | if a.length <= b.length { 938 | ap, bp = a, b 939 | } else { 940 | ap, bp = b, a 941 | } 942 | return 943 | } 944 | 945 | // Intersection of base set and other set 946 | // This is the BitSet equivalent of & (and) 947 | // In case of allocation failure, the function will return an empty BitSet. 948 | func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) { 949 | panicIfNull(b) 950 | panicIfNull(compare) 951 | b, compare = sortByLength(b, compare) 952 | result = New(b.length) 953 | for i, word := range b.set { 954 | result.set[i] = word & compare.set[i] 955 | } 956 | return 957 | } 958 | 959 | // IntersectionCardinality computes the cardinality of the intersection 960 | func (b *BitSet) IntersectionCardinality(compare *BitSet) uint { 961 | panicIfNull(b) 962 | panicIfNull(compare) 963 | b, compare = sortByLength(b, compare) 964 | cnt := popcntAndSlice(b.set, compare.set) 965 | return uint(cnt) 966 | } 967 | 968 | // InPlaceIntersection destructively computes the intersection of 969 | // base set and the compare set. 970 | // This is the BitSet equivalent of & (and) 971 | func (b *BitSet) InPlaceIntersection(compare *BitSet) { 972 | panicIfNull(b) 973 | panicIfNull(compare) 974 | l := compare.wordCount() 975 | if l > b.wordCount() { 976 | l = b.wordCount() 977 | } 978 | if l > 0 { 979 | // bounds check elimination 980 | data, cmpData := b.set, compare.set 981 | _ = data[l-1] 982 | _ = cmpData[l-1] 983 | 984 | for i := 0; i < l; i++ { 985 | data[i] &= cmpData[i] 986 | } 987 | } 988 | if l >= 0 { 989 | for i := l; i < len(b.set); i++ { 990 | b.set[i] = 0 991 | } 992 | } 993 | if compare.length > 0 { 994 | if compare.length-1 >= b.length { 995 | b.extendSet(compare.length - 1) 996 | } 997 | } 998 | } 999 | 1000 | // Union of base set and other set 1001 | // This is the BitSet equivalent of | (or) 1002 | func (b *BitSet) Union(compare *BitSet) (result *BitSet) { 1003 | panicIfNull(b) 1004 | panicIfNull(compare) 1005 | b, compare = sortByLength(b, compare) 1006 | result = compare.Clone() 1007 | for i, word := range b.set { 1008 | result.set[i] = word | compare.set[i] 1009 | } 1010 | return 1011 | } 1012 | 1013 | // UnionCardinality computes the cardinality of the uniton of the base set 1014 | // and the compare set. 1015 | func (b *BitSet) UnionCardinality(compare *BitSet) uint { 1016 | panicIfNull(b) 1017 | panicIfNull(compare) 1018 | b, compare = sortByLength(b, compare) 1019 | cnt := popcntOrSlice(b.set, compare.set) 1020 | if len(compare.set) > len(b.set) { 1021 | cnt += popcntSlice(compare.set[len(b.set):]) 1022 | } 1023 | return uint(cnt) 1024 | } 1025 | 1026 | // InPlaceUnion creates the destructive union of base set and compare set. 1027 | // This is the BitSet equivalent of | (or). 1028 | func (b *BitSet) InPlaceUnion(compare *BitSet) { 1029 | panicIfNull(b) 1030 | panicIfNull(compare) 1031 | l := compare.wordCount() 1032 | if l > b.wordCount() { 1033 | l = b.wordCount() 1034 | } 1035 | if compare.length > 0 && compare.length-1 >= b.length { 1036 | b.extendSet(compare.length - 1) 1037 | } 1038 | if l > 0 { 1039 | // bounds check elimination 1040 | data, cmpData := b.set, compare.set 1041 | _ = data[l-1] 1042 | _ = cmpData[l-1] 1043 | 1044 | for i := 0; i < l; i++ { 1045 | data[i] |= cmpData[i] 1046 | } 1047 | } 1048 | if len(compare.set) > l { 1049 | for i := l; i < len(compare.set); i++ { 1050 | b.set[i] = compare.set[i] 1051 | } 1052 | } 1053 | } 1054 | 1055 | // SymmetricDifference of base set and other set 1056 | // This is the BitSet equivalent of ^ (xor) 1057 | func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) { 1058 | panicIfNull(b) 1059 | panicIfNull(compare) 1060 | b, compare = sortByLength(b, compare) 1061 | // compare is bigger, so clone it 1062 | result = compare.Clone() 1063 | for i, word := range b.set { 1064 | result.set[i] = word ^ compare.set[i] 1065 | } 1066 | return 1067 | } 1068 | 1069 | // SymmetricDifferenceCardinality computes the cardinality of the symmetric difference 1070 | func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint { 1071 | panicIfNull(b) 1072 | panicIfNull(compare) 1073 | b, compare = sortByLength(b, compare) 1074 | cnt := popcntXorSlice(b.set, compare.set) 1075 | if len(compare.set) > len(b.set) { 1076 | cnt += popcntSlice(compare.set[len(b.set):]) 1077 | } 1078 | return uint(cnt) 1079 | } 1080 | 1081 | // InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set 1082 | // This is the BitSet equivalent of ^ (xor) 1083 | func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) { 1084 | panicIfNull(b) 1085 | panicIfNull(compare) 1086 | l := compare.wordCount() 1087 | if l > b.wordCount() { 1088 | l = b.wordCount() 1089 | } 1090 | if compare.length > 0 && compare.length-1 >= b.length { 1091 | b.extendSet(compare.length - 1) 1092 | } 1093 | if l > 0 { 1094 | // bounds check elimination 1095 | data, cmpData := b.set, compare.set 1096 | _ = data[l-1] 1097 | _ = cmpData[l-1] 1098 | for i := 0; i < l; i++ { 1099 | data[i] ^= cmpData[i] 1100 | } 1101 | } 1102 | if len(compare.set) > l { 1103 | for i := l; i < len(compare.set); i++ { 1104 | b.set[i] = compare.set[i] 1105 | } 1106 | } 1107 | } 1108 | 1109 | // Is the length an exact multiple of word sizes? 1110 | func (b *BitSet) isLenExactMultiple() bool { 1111 | return wordsIndex(b.length) == 0 1112 | } 1113 | 1114 | // Clean last word by setting unused bits to 0 1115 | func (b *BitSet) cleanLastWord() { 1116 | if !b.isLenExactMultiple() { 1117 | b.set[len(b.set)-1] &= allBits >> (wordSize - wordsIndex(b.length)) 1118 | } 1119 | } 1120 | 1121 | // Complement computes the (local) complement of a bitset (up to length bits) 1122 | // In case of allocation failure, the function will return an empty BitSet. 1123 | func (b *BitSet) Complement() (result *BitSet) { 1124 | panicIfNull(b) 1125 | result = New(b.length) 1126 | for i, word := range b.set { 1127 | result.set[i] = ^word 1128 | } 1129 | result.cleanLastWord() 1130 | return 1131 | } 1132 | 1133 | // All returns true if all bits are set, false otherwise. Returns true for 1134 | // empty sets. 1135 | func (b *BitSet) All() bool { 1136 | panicIfNull(b) 1137 | return b.Count() == b.length 1138 | } 1139 | 1140 | // None returns true if no bit is set, false otherwise. Returns true for 1141 | // empty sets. 1142 | func (b *BitSet) None() bool { 1143 | panicIfNull(b) 1144 | if b != nil && b.set != nil { 1145 | for _, word := range b.set { 1146 | if word > 0 { 1147 | return false 1148 | } 1149 | } 1150 | } 1151 | return true 1152 | } 1153 | 1154 | // Any returns true if any bit is set, false otherwise 1155 | func (b *BitSet) Any() bool { 1156 | panicIfNull(b) 1157 | return !b.None() 1158 | } 1159 | 1160 | // IsSuperSet returns true if this is a superset of the other set 1161 | func (b *BitSet) IsSuperSet(other *BitSet) bool { 1162 | l := other.wordCount() 1163 | if b.wordCount() < l { 1164 | l = b.wordCount() 1165 | } 1166 | for i, word := range other.set[:l] { 1167 | if b.set[i]&word != word { 1168 | return false 1169 | } 1170 | } 1171 | return popcntSlice(other.set[l:]) == 0 1172 | } 1173 | 1174 | // IsStrictSuperSet returns true if this is a strict superset of the other set 1175 | func (b *BitSet) IsStrictSuperSet(other *BitSet) bool { 1176 | return b.Count() > other.Count() && b.IsSuperSet(other) 1177 | } 1178 | 1179 | // DumpAsBits dumps a bit set as a string of bits. Following the usual convention in Go, 1180 | // the least significant bits are printed last (index 0 is at the end of the string). 1181 | // This is useful for debugging and testing. It is not suitable for serialization. 1182 | func (b *BitSet) DumpAsBits() string { 1183 | if b.set == nil { 1184 | return "." 1185 | } 1186 | buffer := bytes.NewBufferString("") 1187 | i := len(b.set) - 1 1188 | for ; i >= 0; i-- { 1189 | fmt.Fprintf(buffer, "%064b.", b.set[i]) 1190 | } 1191 | return buffer.String() 1192 | } 1193 | 1194 | // BinaryStorageSize returns the binary storage requirements (see WriteTo) in bytes. 1195 | func (b *BitSet) BinaryStorageSize() int { 1196 | return wordBytes + wordBytes*b.wordCount() 1197 | } 1198 | 1199 | func readUint64Array(reader io.Reader, data []uint64) error { 1200 | length := len(data) 1201 | bufferSize := 128 1202 | buffer := make([]byte, bufferSize*wordBytes) 1203 | for i := 0; i < length; i += bufferSize { 1204 | end := i + bufferSize 1205 | if end > length { 1206 | end = length 1207 | buffer = buffer[:wordBytes*(end-i)] 1208 | } 1209 | chunk := data[i:end] 1210 | if _, err := io.ReadFull(reader, buffer); err != nil { 1211 | return err 1212 | } 1213 | for i := range chunk { 1214 | chunk[i] = uint64(binaryOrder.Uint64(buffer[8*i:])) 1215 | } 1216 | } 1217 | return nil 1218 | } 1219 | 1220 | func writeUint64Array(writer io.Writer, data []uint64) error { 1221 | bufferSize := 128 1222 | buffer := make([]byte, bufferSize*wordBytes) 1223 | for i := 0; i < len(data); i += bufferSize { 1224 | end := i + bufferSize 1225 | if end > len(data) { 1226 | end = len(data) 1227 | buffer = buffer[:wordBytes*(end-i)] 1228 | } 1229 | chunk := data[i:end] 1230 | for i, x := range chunk { 1231 | binaryOrder.PutUint64(buffer[8*i:], x) 1232 | } 1233 | _, err := writer.Write(buffer) 1234 | if err != nil { 1235 | return err 1236 | } 1237 | } 1238 | return nil 1239 | } 1240 | 1241 | // WriteTo writes a BitSet to a stream. The format is: 1242 | // 1. uint64 length 1243 | // 2. []uint64 set 1244 | // The length is the number of bits in the BitSet. 1245 | // 1246 | // The set is a slice of uint64s containing between length and length + 63 bits. 1247 | // It is interpreted as a big-endian array of uint64s by default (see BinaryOrder()) 1248 | // meaning that the first 8 bits are stored at byte index 7, the next 8 bits are stored 1249 | // at byte index 6... the bits 64 to 71 are stored at byte index 8, etc. 1250 | // If you change the binary order, you need to do so for both reading and writing. 1251 | // We recommend using the default binary order. 1252 | // 1253 | // Upon success, the number of bytes written is returned. 1254 | // 1255 | // Performance: if this function is used to write to a disk or network 1256 | // connection, it might be beneficial to wrap the stream in a bufio.Writer. 1257 | // E.g., 1258 | // 1259 | // f, err := os.Create("myfile") 1260 | // w := bufio.NewWriter(f) 1261 | func (b *BitSet) WriteTo(stream io.Writer) (int64, error) { 1262 | length := uint64(b.length) 1263 | // Write length 1264 | err := binary.Write(stream, binaryOrder, &length) 1265 | if err != nil { 1266 | // Upon failure, we do not guarantee that we 1267 | // return the number of bytes written. 1268 | return int64(0), err 1269 | } 1270 | err = writeUint64Array(stream, b.set[:b.wordCount()]) 1271 | if err != nil { 1272 | // Upon failure, we do not guarantee that we 1273 | // return the number of bytes written. 1274 | return int64(wordBytes), err 1275 | } 1276 | return int64(b.BinaryStorageSize()), nil 1277 | } 1278 | 1279 | // ReadFrom reads a BitSet from a stream written using WriteTo 1280 | // The format is: 1281 | // 1. uint64 length 1282 | // 2. []uint64 set 1283 | // See WriteTo for details. 1284 | // Upon success, the number of bytes read is returned. 1285 | // If the current BitSet is not large enough to hold the data, 1286 | // it is extended. In case of error, the BitSet is either 1287 | // left unchanged or made empty if the error occurs too late 1288 | // to preserve the content. 1289 | // 1290 | // Performance: if this function is used to read from a disk or network 1291 | // connection, it might be beneficial to wrap the stream in a bufio.Reader. 1292 | // E.g., 1293 | // 1294 | // f, err := os.Open("myfile") 1295 | // r := bufio.NewReader(f) 1296 | func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) { 1297 | var length uint64 1298 | err := binary.Read(stream, binaryOrder, &length) 1299 | if err != nil { 1300 | if err == io.EOF { 1301 | err = io.ErrUnexpectedEOF 1302 | } 1303 | return 0, err 1304 | } 1305 | newlength := uint(length) 1306 | 1307 | if uint64(newlength) != length { 1308 | return 0, errors.New("unmarshalling error: type mismatch") 1309 | } 1310 | nWords := wordsNeeded(uint(newlength)) 1311 | if cap(b.set) >= nWords { 1312 | b.set = b.set[:nWords] 1313 | } else { 1314 | b.set = make([]uint64, nWords) 1315 | } 1316 | 1317 | b.length = newlength 1318 | 1319 | err = readUint64Array(stream, b.set) 1320 | if err != nil { 1321 | if err == io.EOF { 1322 | err = io.ErrUnexpectedEOF 1323 | } 1324 | // We do not want to leave the BitSet partially filled as 1325 | // it is error prone. 1326 | b.set = b.set[:0] 1327 | b.length = 0 1328 | return 0, err 1329 | } 1330 | 1331 | return int64(b.BinaryStorageSize()), nil 1332 | } 1333 | 1334 | // MarshalBinary encodes a BitSet into a binary form and returns the result. 1335 | // Please see WriteTo for details. 1336 | func (b *BitSet) MarshalBinary() ([]byte, error) { 1337 | var buf bytes.Buffer 1338 | _, err := b.WriteTo(&buf) 1339 | if err != nil { 1340 | return []byte{}, err 1341 | } 1342 | 1343 | return buf.Bytes(), err 1344 | } 1345 | 1346 | // UnmarshalBinary decodes the binary form generated by MarshalBinary. 1347 | // Please see WriteTo for details. 1348 | func (b *BitSet) UnmarshalBinary(data []byte) error { 1349 | buf := bytes.NewReader(data) 1350 | _, err := b.ReadFrom(buf) 1351 | return err 1352 | } 1353 | 1354 | // MarshalJSON marshals a BitSet as a JSON structure 1355 | func (b BitSet) MarshalJSON() ([]byte, error) { 1356 | buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize())) 1357 | _, err := b.WriteTo(buffer) 1358 | if err != nil { 1359 | return nil, err 1360 | } 1361 | 1362 | // URLEncode all bytes 1363 | return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes())) 1364 | } 1365 | 1366 | // UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON 1367 | func (b *BitSet) UnmarshalJSON(data []byte) error { 1368 | // Unmarshal as string 1369 | var s string 1370 | err := json.Unmarshal(data, &s) 1371 | if err != nil { 1372 | return err 1373 | } 1374 | 1375 | // URLDecode string 1376 | buf, err := base64Encoding.DecodeString(s) 1377 | if err != nil { 1378 | return err 1379 | } 1380 | 1381 | _, err = b.ReadFrom(bytes.NewReader(buf)) 1382 | return err 1383 | } 1384 | 1385 | // Rank returns the number of set bits up to and including the index 1386 | // that are set in the bitset. 1387 | // See https://en.wikipedia.org/wiki/Ranking#Ranking_in_statistics 1388 | func (b *BitSet) Rank(index uint) (rank uint) { 1389 | index++ // Rank is up to and including 1390 | 1391 | // needed more than once 1392 | length := len(b.set) 1393 | 1394 | // TODO: built-in min requires go1.21 or later 1395 | // idx := min(int(index>>6), len(b.set)) 1396 | idx := int(index >> 6) 1397 | if idx > length { 1398 | idx = length 1399 | } 1400 | 1401 | // sum up the popcounts until idx ... 1402 | // TODO: cannot range over idx (...): requires go1.22 or later 1403 | // for j := range idx { 1404 | for j := 0; j < idx; j++ { 1405 | if w := b.set[j]; w != 0 { 1406 | rank += uint(bits.OnesCount64(w)) 1407 | } 1408 | } 1409 | 1410 | // ... plus partial word at idx, 1411 | // make Rank inlineable and faster in the end 1412 | // don't test index&63 != 0, just add, less branching 1413 | if idx < length { 1414 | rank += uint(bits.OnesCount64(b.set[idx] << (64 - index&63))) 1415 | } 1416 | 1417 | return 1418 | } 1419 | 1420 | // Select returns the index of the jth set bit, where j is the argument. 1421 | // The caller is responsible to ensure that 0 <= j < Count(): when j is 1422 | // out of range, the function returns the length of the bitset (b.length). 1423 | // 1424 | // Note that this function differs in convention from the Rank function which 1425 | // returns 1 when ranking the smallest value. We follow the conventional 1426 | // textbook definition of Select and Rank. 1427 | func (b *BitSet) Select(index uint) uint { 1428 | leftover := index 1429 | for idx, word := range b.set { 1430 | w := uint(bits.OnesCount64(word)) 1431 | if w > leftover { 1432 | return uint(idx)*64 + select64(word, leftover) 1433 | } 1434 | leftover -= w 1435 | } 1436 | return b.length 1437 | } 1438 | 1439 | // top detects the top bit set 1440 | func (b *BitSet) top() (uint, bool) { 1441 | for idx := len(b.set) - 1; idx >= 0; idx-- { 1442 | if word := b.set[idx]; word != 0 { 1443 | return uint(idx<= b.length { 1481 | b.length = top + bits + 1 1482 | } 1483 | 1484 | pad, idx := top%wordSize, top>>log2WordSize 1485 | shift, pages := bits%wordSize, bits>>log2WordSize 1486 | if bits%wordSize == 0 { // happy case: just add pages 1487 | copy(dst[pages:nsize], b.set) 1488 | } else { 1489 | if pad+shift >= wordSize { 1490 | dst[idx+pages+1] = b.set[idx] >> (wordSize - shift) 1491 | } 1492 | 1493 | for i := int(idx); i >= 0; i-- { 1494 | if i > 0 { 1495 | dst[i+int(pages)] = (b.set[i] << shift) | (b.set[i-1] >> (wordSize - shift)) 1496 | } else { 1497 | dst[i+int(pages)] = b.set[i] << shift 1498 | } 1499 | } 1500 | } 1501 | 1502 | // zeroing extra pages 1503 | for i := 0; i < int(pages); i++ { 1504 | dst[i] = 0 1505 | } 1506 | 1507 | b.set = dst 1508 | } 1509 | 1510 | // ShiftRight shifts the bitset like >> operation would do. 1511 | func (b *BitSet) ShiftRight(bits uint) { 1512 | panicIfNull(b) 1513 | 1514 | if bits == 0 { 1515 | return 1516 | } 1517 | 1518 | top, ok := b.top() 1519 | if !ok { 1520 | return 1521 | } 1522 | 1523 | if bits >= top { 1524 | b.set = make([]uint64, wordsNeeded(b.length)) 1525 | return 1526 | } 1527 | 1528 | pad, idx := top%wordSize, top>>log2WordSize 1529 | shift, pages := bits%wordSize, bits>>log2WordSize 1530 | if bits%wordSize == 0 { // happy case: just clear pages 1531 | b.set = b.set[pages:] 1532 | b.length -= pages * wordSize 1533 | } else { 1534 | for i := 0; i <= int(idx-pages); i++ { 1535 | if i < int(idx-pages) { 1536 | b.set[i] = (b.set[i+int(pages)] >> shift) | (b.set[i+int(pages)+1] << (wordSize - shift)) 1537 | } else { 1538 | b.set[i] = b.set[i+int(pages)] >> shift 1539 | } 1540 | } 1541 | 1542 | if pad < shift { 1543 | b.set[int(idx-pages)] = 0 1544 | } 1545 | } 1546 | 1547 | for i := int(idx-pages) + 1; i <= int(idx); i++ { 1548 | b.set[i] = 0 1549 | } 1550 | } 1551 | 1552 | // OnesBetween returns the number of set bits in the range [from, to). 1553 | // The range is inclusive of 'from' and exclusive of 'to'. 1554 | // Returns 0 if from >= to. 1555 | func (b *BitSet) OnesBetween(from, to uint) uint { 1556 | panicIfNull(b) 1557 | 1558 | if from >= to { 1559 | return 0 1560 | } 1561 | 1562 | // Calculate indices and masks for the starting and ending words 1563 | startWord := from >> log2WordSize // Divide by wordSize 1564 | endWord := to >> log2WordSize 1565 | startOffset := from & wordMask // Mod wordSize 1566 | endOffset := to & wordMask 1567 | 1568 | // Case 1: Bits lie within a single word 1569 | if startWord == endWord { 1570 | // Create mask for bits between from and to 1571 | mask := uint64((1<= startOffset 1580 | count = uint(bits.OnesCount64(b.set[startWord] & startMask)) 1581 | 1582 | // 2b: Count all bits in complete words between start and end 1583 | if endWord > startWord+1 { 1584 | count += uint(popcntSlice(b.set[startWord+1 : endWord])) 1585 | } 1586 | 1587 | // 2c: Count bits in last word (from start of word to endOffset) 1588 | if endOffset > 0 { 1589 | endMask := uint64(1<> log2WordSize 1644 | bitOffset := outPos & wordMask 1645 | 1646 | // Write extracted bits, handling word boundary crossing 1647 | dst.set[wordIdx] |= extracted << bitOffset 1648 | if bitOffset+bitsExtracted > wordSize { 1649 | dst.set[wordIdx+1] = extracted >> (wordSize - bitOffset) 1650 | } 1651 | 1652 | outPos += bitsExtracted 1653 | } 1654 | } 1655 | 1656 | // Deposit creates a new BitSet and deposits bits according to a mask. 1657 | // See DepositTo for details. 1658 | func (b *BitSet) Deposit(mask *BitSet) *BitSet { 1659 | dst := New(mask.length) 1660 | b.DepositTo(mask, dst) 1661 | return dst 1662 | } 1663 | 1664 | // DepositTo spreads bits from a compacted form in the BitSet into positions 1665 | // specified by mask in dst. This is the inverse operation of Extract. 1666 | // 1667 | // For example, if mask has bits set at positions 1,4,5, then DepositTo will 1668 | // take consecutive bits 0,1,2 from the source BitSet and place them into 1669 | // positions 1,4,5 in the destination BitSet. 1670 | func (b *BitSet) DepositTo(mask *BitSet, dst *BitSet) { 1671 | panicIfNull(b) 1672 | panicIfNull(mask) 1673 | panicIfNull(dst) 1674 | 1675 | if len(dst.set) == 0 || len(mask.set) == 0 || len(b.set) == 0 { 1676 | return 1677 | } 1678 | 1679 | inPos := uint(0) 1680 | length := len(mask.set) 1681 | if len(dst.set) < length { 1682 | length = len(dst.set) 1683 | } 1684 | 1685 | // Process each word 1686 | for i := 0; i < length; i++ { 1687 | if mask.set[i] == 0 { 1688 | continue // Skip words with no bits to deposit 1689 | } 1690 | 1691 | // Calculate source word index 1692 | wordIdx := inPos >> log2WordSize 1693 | if wordIdx >= uint(len(b.set)) { 1694 | break // No more source bits available 1695 | } 1696 | 1697 | // Get source bits, handling word boundary crossing 1698 | sourceBits := b.set[wordIdx] 1699 | bitOffset := inPos & wordMask 1700 | if wordIdx+1 < uint(len(b.set)) && bitOffset != 0 { 1701 | // Combine bits from current and next word 1702 | sourceBits = (sourceBits >> bitOffset) | 1703 | (b.set[wordIdx+1] << (wordSize - bitOffset)) 1704 | } else { 1705 | sourceBits >>= bitOffset 1706 | } 1707 | 1708 | // Deposit bits according to mask 1709 | dst.set[i] = (dst.set[i] &^ mask.set[i]) | pdep(sourceBits, mask.set[i]) 1710 | inPos += uint(bits.OnesCount64(mask.set[i])) 1711 | } 1712 | } 1713 | 1714 | //go:generate go run cmd/pextgen/main.go -pkg=bitset 1715 | 1716 | func pext(w, m uint64) (result uint64) { 1717 | var outPos uint 1718 | 1719 | // Process byte by byte 1720 | for i := 0; i < 8; i++ { 1721 | shift := i << 3 // i * 8 using bit shift 1722 | b := uint8(w >> shift) 1723 | mask := uint8(m >> shift) 1724 | 1725 | extracted := pextLUT[b][mask] 1726 | bits := popLUT[mask] 1727 | 1728 | result |= uint64(extracted) << outPos 1729 | outPos += uint(bits) 1730 | } 1731 | 1732 | return result 1733 | } 1734 | 1735 | func pdep(w, m uint64) (result uint64) { 1736 | var inPos uint 1737 | 1738 | // Process byte by byte 1739 | for i := 0; i < 8; i++ { 1740 | shift := i << 3 // i * 8 using bit shift 1741 | mask := uint8(m >> shift) 1742 | bits := popLUT[mask] 1743 | 1744 | // Get the bits we'll deposit from the source 1745 | b := uint8(w >> inPos) 1746 | 1747 | // Deposit them according to the mask for this byte 1748 | deposited := pdepLUT[b][mask] 1749 | 1750 | // Add to result 1751 | result |= uint64(deposited) << shift 1752 | inPos += uint(bits) 1753 | } 1754 | 1755 | return result 1756 | } 1757 | -------------------------------------------------------------------------------- /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_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<= 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 | -------------------------------------------------------------------------------- /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 TestInPlaceUnion(t *testing.T) { 1306 | a := New(100) 1307 | b := New(200) 1308 | for i := uint(1); i < 100; i += 2 { 1309 | a.Set(i) 1310 | b.Set(i - 1) 1311 | } 1312 | for i := uint(100); i < 200; i++ { 1313 | b.Set(i) 1314 | } 1315 | c := a.Clone() 1316 | c.InPlaceUnion(b) 1317 | d := b.Clone() 1318 | d.InPlaceUnion(a) 1319 | if c.Count() != 200 { 1320 | t.Errorf("Union should have 200 bits set, but had %d", c.Count()) 1321 | } 1322 | if d.Count() != 200 { 1323 | t.Errorf("Union should have 200 bits set, but had %d", d.Count()) 1324 | } 1325 | if !c.Equal(d) { 1326 | t.Errorf("Union should be symmetric") 1327 | } 1328 | } 1329 | 1330 | func TestIntersection(t *testing.T) { 1331 | a := New(100) 1332 | b := New(200) 1333 | for i := uint(1); i < 100; i += 2 { 1334 | a.Set(i) 1335 | b.Set(i - 1).Set(i) 1336 | } 1337 | for i := uint(100); i < 200; i++ { 1338 | b.Set(i) 1339 | } 1340 | if a.IntersectionCardinality(b) != 50 { 1341 | t.Errorf("Intersection should have 50 bits set, but had %d", a.IntersectionCardinality(b)) 1342 | } 1343 | if a.IntersectionCardinality(b) != b.IntersectionCardinality(a) { 1344 | t.Errorf("Intersection should be symmetric") 1345 | } 1346 | c := a.Intersection(b) 1347 | d := b.Intersection(a) 1348 | if c.Count() != 50 { 1349 | t.Errorf("Intersection should have 50 bits set, but had %d", c.Count()) 1350 | } 1351 | if !c.Equal(d) { 1352 | t.Errorf("Intersection should be symmetric") 1353 | } 1354 | } 1355 | 1356 | func TestInplaceIntersection(t *testing.T) { 1357 | a := New(100) 1358 | b := New(200) 1359 | for i := uint(1); i < 100; i += 2 { 1360 | a.Set(i) 1361 | b.Set(i - 1).Set(i) 1362 | } 1363 | for i := uint(100); i < 200; i++ { 1364 | b.Set(i) 1365 | } 1366 | c := a.Clone() 1367 | c.InPlaceIntersection(b) 1368 | d := b.Clone() 1369 | d.InPlaceIntersection(a) 1370 | if c.Count() != 50 { 1371 | t.Errorf("Intersection should have 50 bits set, but had %d", c.Count()) 1372 | } 1373 | if d.Count() != 50 { 1374 | t.Errorf("Intersection should have 50 bits set, but had %d", d.Count()) 1375 | } 1376 | if !c.Equal(d) { 1377 | t.Errorf("Intersection should be symmetric") 1378 | } 1379 | } 1380 | 1381 | func TestDifference(t *testing.T) { 1382 | a := New(100) 1383 | b := New(200) 1384 | for i := uint(1); i < 100; i += 2 { 1385 | a.Set(i) 1386 | b.Set(i - 1) 1387 | } 1388 | for i := uint(100); i < 200; i++ { 1389 | b.Set(i) 1390 | } 1391 | if a.DifferenceCardinality(b) != 50 { 1392 | t.Errorf("a-b Difference should have 50 bits set, but had %d", a.DifferenceCardinality(b)) 1393 | } 1394 | if b.DifferenceCardinality(a) != 150 { 1395 | t.Errorf("b-a Difference should have 150 bits set, but had %d", b.DifferenceCardinality(a)) 1396 | } 1397 | 1398 | c := a.Difference(b) 1399 | d := b.Difference(a) 1400 | if c.Count() != 50 { 1401 | t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count()) 1402 | } 1403 | if d.Count() != 150 { 1404 | t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count()) 1405 | } 1406 | if c.Equal(d) { 1407 | t.Errorf("Difference, here, should not be symmetric") 1408 | } 1409 | } 1410 | 1411 | func TestInPlaceDifference(t *testing.T) { 1412 | a := New(100) 1413 | b := New(200) 1414 | for i := uint(1); i < 100; i += 2 { 1415 | a.Set(i) 1416 | b.Set(i - 1) 1417 | } 1418 | for i := uint(100); i < 200; i++ { 1419 | b.Set(i) 1420 | } 1421 | c := a.Clone() 1422 | c.InPlaceDifference(b) 1423 | d := b.Clone() 1424 | d.InPlaceDifference(a) 1425 | if c.Count() != 50 { 1426 | t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count()) 1427 | } 1428 | if d.Count() != 150 { 1429 | t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count()) 1430 | } 1431 | if c.Equal(d) { 1432 | t.Errorf("Difference, here, should not be symmetric") 1433 | } 1434 | } 1435 | 1436 | func TestSymmetricDifference(t *testing.T) { 1437 | a := New(100) 1438 | b := New(200) 1439 | for i := uint(1); i < 100; i += 2 { 1440 | a.Set(i) // 01010101010 ... 0000000 1441 | b.Set(i - 1).Set(i) // 11111111111111111000000 1442 | } 1443 | for i := uint(100); i < 200; i++ { 1444 | b.Set(i) 1445 | } 1446 | if a.SymmetricDifferenceCardinality(b) != 150 { 1447 | t.Errorf("a^b Difference should have 150 bits set, but had %d", a.SymmetricDifferenceCardinality(b)) 1448 | } 1449 | if b.SymmetricDifferenceCardinality(a) != 150 { 1450 | t.Errorf("b^a Difference should have 150 bits set, but had %d", b.SymmetricDifferenceCardinality(a)) 1451 | } 1452 | 1453 | c := a.SymmetricDifference(b) 1454 | d := b.SymmetricDifference(a) 1455 | if c.Count() != 150 { 1456 | t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count()) 1457 | } 1458 | if d.Count() != 150 { 1459 | t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count()) 1460 | } 1461 | if !c.Equal(d) { 1462 | t.Errorf("SymmetricDifference should be symmetric") 1463 | } 1464 | } 1465 | 1466 | func TestInPlaceSymmetricDifference(t *testing.T) { 1467 | a := New(100) 1468 | b := New(200) 1469 | for i := uint(1); i < 100; i += 2 { 1470 | a.Set(i) // 01010101010 ... 0000000 1471 | b.Set(i - 1).Set(i) // 11111111111111111000000 1472 | } 1473 | for i := uint(100); i < 200; i++ { 1474 | b.Set(i) 1475 | } 1476 | c := a.Clone() 1477 | c.InPlaceSymmetricDifference(b) 1478 | d := b.Clone() 1479 | d.InPlaceSymmetricDifference(a) 1480 | if c.Count() != 150 { 1481 | t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count()) 1482 | } 1483 | if d.Count() != 150 { 1484 | t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count()) 1485 | } 1486 | if !c.Equal(d) { 1487 | t.Errorf("SymmetricDifference should be symmetric") 1488 | } 1489 | } 1490 | 1491 | func TestComplement(t *testing.T) { 1492 | a := New(50) 1493 | b := a.Complement() 1494 | if b.Count() != 50 { 1495 | t.Errorf("Complement failed, size should be 50, but was %d", b.Count()) 1496 | } 1497 | a = New(50) 1498 | a.Set(10).Set(20).Set(42) 1499 | b = a.Complement() 1500 | if b.Count() != 47 { 1501 | t.Errorf("Complement failed, size should be 47, but was %d", b.Count()) 1502 | } 1503 | } 1504 | 1505 | func TestIsSuperSet(t *testing.T) { 1506 | test := func(name string, lenS, lenSS int, overrideS, overrideSS map[int]bool, want, wantStrict bool) { 1507 | t.Run(name, func(t *testing.T) { 1508 | s := New(uint(lenS)) 1509 | ss := New(uint(lenSS)) 1510 | 1511 | l := lenS 1512 | if lenSS < lenS { 1513 | l = lenSS 1514 | } 1515 | 1516 | r := rand.New(rand.NewSource(42)) 1517 | for i := 0; i < l; i++ { 1518 | bit := r.Intn(2) == 1 1519 | s.SetTo(uint(i), bit) 1520 | ss.SetTo(uint(i), bit) 1521 | } 1522 | 1523 | for i, v := range overrideS { 1524 | s.SetTo(uint(i), v) 1525 | } 1526 | for i, v := range overrideSS { 1527 | ss.SetTo(uint(i), v) 1528 | } 1529 | 1530 | if got := ss.IsSuperSet(s); got != want { 1531 | t.Errorf("IsSuperSet() = %v, want %v", got, want) 1532 | } 1533 | if got := ss.IsStrictSuperSet(s); got != wantStrict { 1534 | t.Errorf("IsStrictSuperSet() = %v, want %v", got, wantStrict) 1535 | } 1536 | }) 1537 | } 1538 | 1539 | test("empty", 0, 0, nil, nil, true, false) 1540 | test("empty vs non-empty", 0, 100, nil, nil, true, false) 1541 | test("non-empty vs empty", 100, 0, nil, nil, true, false) 1542 | test("equal", 100, 100, nil, nil, true, false) 1543 | 1544 | test("set is shorter, subset", 100, 200, map[int]bool{50: true}, map[int]bool{50: false}, false, false) 1545 | test("set is shorter, equal", 100, 200, nil, nil, true, false) 1546 | test("set is shorter, superset", 100, 200, map[int]bool{50: false}, map[int]bool{50: true}, true, true) 1547 | test("set is shorter, neither", 100, 200, map[int]bool{50: true}, map[int]bool{50: false, 150: true}, false, false) 1548 | 1549 | test("set is longer, subset", 200, 100, map[int]bool{50: true}, map[int]bool{50: false}, false, false) 1550 | test("set is longer, equal", 200, 100, nil, nil, true, false) 1551 | test("set is longer, superset", 200, 100, nil, map[int]bool{150: true}, true, true) 1552 | test("set is longer, neither", 200, 100, map[int]bool{50: false, 150: true}, map[int]bool{50: true}, false, false) 1553 | } 1554 | 1555 | func TestDumpAsBits(t *testing.T) { 1556 | a := New(10).Set(10) 1557 | astr := "0000000000000000000000000000000000000000000000000000010000000000." 1558 | if a.DumpAsBits() != astr { 1559 | t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", astr, a.DumpAsBits()) 1560 | } 1561 | var b BitSet // zero value (b.set == nil) 1562 | bstr := "." 1563 | if b.DumpAsBits() != bstr { 1564 | t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", bstr, b.DumpAsBits()) 1565 | } 1566 | } 1567 | 1568 | func TestMarshalUnmarshalBinary(t *testing.T) { 1569 | a := New(1010).Set(10).Set(1001) 1570 | b := new(BitSet) 1571 | 1572 | copyBinary(t, a, b) 1573 | 1574 | // BitSets must be equal after marshalling and unmarshalling 1575 | if !a.Equal(b) { 1576 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1577 | return 1578 | } 1579 | 1580 | aSetBit := uint(128) 1581 | a = New(256).Set(aSetBit) 1582 | aExpectedMarshaledSize := 8 /* length: uint64 */ + 4*8 /* set : [4]uint64 */ 1583 | aMarshaled, err := a.MarshalBinary() 1584 | 1585 | if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() { 1586 | t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes") 1587 | return 1588 | } 1589 | 1590 | shiftAmount := uint(72) 1591 | // https://github.com/bits-and-blooms/bitset/issues/114 1592 | for i := uint(0); i < shiftAmount; i++ { 1593 | a.DeleteAt(0) 1594 | } 1595 | 1596 | aExpectedMarshaledSize = 8 /* length: uint64 */ + 3*8 /* set : [3]uint64 */ 1597 | aMarshaled, err = a.MarshalBinary() 1598 | if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() { 1599 | t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes") 1600 | return 1601 | } 1602 | 1603 | copyBinary(t, a, b) 1604 | 1605 | if b.Len() != 256-shiftAmount || !b.Test(aSetBit-shiftAmount) { 1606 | t.Error("Shifted bitset is not copied correctly") 1607 | } 1608 | } 1609 | 1610 | func TestMarshalUnmarshalBinaryByLittleEndian(t *testing.T) { 1611 | LittleEndian() 1612 | defer func() { 1613 | // Revert when done. 1614 | binaryOrder = binary.BigEndian 1615 | }() 1616 | a := New(1010).Set(10).Set(1001) 1617 | b := new(BitSet) 1618 | 1619 | copyBinary(t, a, b) 1620 | 1621 | // BitSets must be equal after marshalling and unmarshalling 1622 | if !a.Equal(b) { 1623 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1624 | return 1625 | } 1626 | } 1627 | 1628 | func copyBinary(t *testing.T, from encoding.BinaryMarshaler, to encoding.BinaryUnmarshaler) { 1629 | data, err := from.MarshalBinary() 1630 | if err != nil { 1631 | t.Error(err.Error()) 1632 | return 1633 | } 1634 | 1635 | err = to.UnmarshalBinary(data) 1636 | if err != nil { 1637 | t.Error(err.Error()) 1638 | return 1639 | } 1640 | } 1641 | 1642 | func TestMarshalUnmarshalJSON(t *testing.T) { 1643 | t.Run("value", func(t *testing.T) { 1644 | a := BitSet{} 1645 | a.Set(10).Set(1001) 1646 | data, err := json.Marshal(a) 1647 | if err != nil { 1648 | t.Error(err.Error()) 1649 | return 1650 | } 1651 | 1652 | b := new(BitSet) 1653 | err = json.Unmarshal(data, b) 1654 | if err != nil { 1655 | t.Error(err.Error()) 1656 | return 1657 | } 1658 | 1659 | // Bitsets must be equal after marshalling and unmarshalling 1660 | if !a.Equal(b) { 1661 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1662 | return 1663 | } 1664 | }) 1665 | t.Run("pointer", func(t *testing.T) { 1666 | a := New(1010).Set(10).Set(1001) 1667 | data, err := json.Marshal(a) 1668 | if err != nil { 1669 | t.Error(err.Error()) 1670 | return 1671 | } 1672 | 1673 | b := new(BitSet) 1674 | err = json.Unmarshal(data, b) 1675 | if err != nil { 1676 | t.Error(err.Error()) 1677 | return 1678 | } 1679 | 1680 | // Bitsets must be equal after marshalling and unmarshalling 1681 | if !a.Equal(b) { 1682 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1683 | return 1684 | } 1685 | }) 1686 | } 1687 | 1688 | func TestMarshalUnmarshalJSONWithTrailingData(t *testing.T) { 1689 | a := New(1010).Set(10).Set(1001) 1690 | data, err := json.Marshal(a) 1691 | if err != nil { 1692 | t.Error(err.Error()) 1693 | return 1694 | } 1695 | 1696 | // appending some noise 1697 | data = data[:len(data)-3] // remove " 1698 | data = append(data, []byte(`AAAAAAAAAA"`)...) 1699 | 1700 | b := new(BitSet) 1701 | err = json.Unmarshal(data, b) 1702 | if err != nil { 1703 | t.Error(err.Error()) 1704 | return 1705 | } 1706 | 1707 | // Bitsets must be equal after marshalling and unmarshalling 1708 | // Do not over-reading when unmarshalling 1709 | if !a.Equal(b) { 1710 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1711 | return 1712 | } 1713 | } 1714 | 1715 | func TestMarshalUnmarshalJSONByStdEncoding(t *testing.T) { 1716 | Base64StdEncoding() 1717 | a := New(1010).Set(10).Set(1001) 1718 | data, err := json.Marshal(a) 1719 | if err != nil { 1720 | t.Error(err.Error()) 1721 | return 1722 | } 1723 | 1724 | b := new(BitSet) 1725 | err = json.Unmarshal(data, b) 1726 | if err != nil { 1727 | t.Error(err.Error()) 1728 | return 1729 | } 1730 | 1731 | // Bitsets must be equal after marshalling and unmarshalling 1732 | if !a.Equal(b) { 1733 | t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) 1734 | return 1735 | } 1736 | } 1737 | 1738 | func TestSafeSet(t *testing.T) { 1739 | b := new(BitSet) 1740 | c := b.safeSet() 1741 | outType := fmt.Sprintf("%T", c) 1742 | expType := "[]uint64" 1743 | if outType != expType { 1744 | t.Error("Expecting type: ", expType, ", gotf:", outType) 1745 | return 1746 | } 1747 | if len(c) != 0 { 1748 | t.Error("The slice should be empty") 1749 | return 1750 | } 1751 | } 1752 | 1753 | func TestSetBitsetFrom(t *testing.T) { 1754 | u := []uint64{2, 3, 5, 7, 11} 1755 | b := new(BitSet) 1756 | b.SetBitsetFrom(u) 1757 | outType := fmt.Sprintf("%T", b) 1758 | expType := "*bitset.BitSet" 1759 | if outType != expType { 1760 | t.Error("Expecting type: ", expType, ", gotf:", outType) 1761 | return 1762 | } 1763 | } 1764 | 1765 | func TestIssue116(t *testing.T) { 1766 | a := []uint64{2, 3, 5, 7, 11} 1767 | b := []uint64{2, 3, 5, 7, 11, 0, 1} 1768 | bitset1 := FromWithLength(320, a) 1769 | bitset2 := FromWithLength(320, b) 1770 | if !bitset1.Equal(bitset2) || !bitset2.Equal(bitset1) { 1771 | t.Error("Bitsets should be equal irrespective of the underlying capacity") 1772 | } 1773 | } 1774 | 1775 | func TestFrom(t *testing.T) { 1776 | u := []uint64{2, 3, 5, 7, 11} 1777 | b := From(u) 1778 | outType := fmt.Sprintf("%T", b) 1779 | expType := "*bitset.BitSet" 1780 | if outType != expType { 1781 | t.Error("Expecting type: ", expType, ", gotf:", outType) 1782 | return 1783 | } 1784 | } 1785 | 1786 | func TestWords(t *testing.T) { 1787 | b := new(BitSet) 1788 | c := b.Words() 1789 | outType := fmt.Sprintf("%T", c) 1790 | expType := "[]uint64" 1791 | if outType != expType { 1792 | t.Error("Expecting type: ", expType, ", gotf:", outType) 1793 | return 1794 | } 1795 | if len(c) != 0 { 1796 | t.Error("The slice should be empty") 1797 | return 1798 | } 1799 | } 1800 | 1801 | // Bytes is deprecated 1802 | func TestBytes(t *testing.T) { 1803 | b := new(BitSet) 1804 | c := b.Bytes() 1805 | outType := fmt.Sprintf("%T", c) 1806 | expType := "[]uint64" 1807 | if outType != expType { 1808 | t.Error("Expecting type: ", expType, ", gotf:", outType) 1809 | return 1810 | } 1811 | if len(c) != 0 { 1812 | t.Error("The slice should be empty") 1813 | return 1814 | } 1815 | } 1816 | 1817 | func TestCap(t *testing.T) { 1818 | c := Cap() 1819 | if c <= 0 { 1820 | t.Error("The uint capacity should be >= 0") 1821 | return 1822 | } 1823 | } 1824 | 1825 | func TestWordsNeededLong(t *testing.T) { 1826 | i := Cap() 1827 | out := wordsNeeded(i) 1828 | if out <= 0 { 1829 | t.Error("Unexpected value: ", out) 1830 | return 1831 | } 1832 | } 1833 | 1834 | func TestTestTooLong(t *testing.T) { 1835 | b := new(BitSet) 1836 | if b.Test(1) { 1837 | t.Error("Unexpected value: true") 1838 | return 1839 | } 1840 | } 1841 | 1842 | func TestClearTooLong(t *testing.T) { 1843 | b := new(BitSet) 1844 | c := b.Clear(1) 1845 | if b != c { 1846 | t.Error("Unexpected value") 1847 | return 1848 | } 1849 | } 1850 | 1851 | func TestClearAll(t *testing.T) { 1852 | u := []uint64{2, 3, 5, 7, 11} 1853 | b := From(u) 1854 | c := b.ClearAll() 1855 | if c.length != 320 { 1856 | t.Error("Unexpected length: ", b.length) 1857 | return 1858 | } 1859 | if c.Test(0) || c.Test(1) || c.Test(2) || c.Test(3) || c.Test(4) || c.Test(5) { 1860 | t.Error("All bits should be unset") 1861 | return 1862 | } 1863 | } 1864 | 1865 | func TestRankSelect(t *testing.T) { 1866 | u := []uint{2, 3, 5, 7, 11, 700, 1500} 1867 | b := BitSet{} 1868 | for _, v := range u { 1869 | b.Set(v) 1870 | } 1871 | 1872 | if b.Rank(5) != 3 { 1873 | t.Error("Unexpected rank") 1874 | return 1875 | } 1876 | if b.Rank(6) != 3 { 1877 | t.Error("Unexpected rank") 1878 | return 1879 | } 1880 | if b.Rank(1500) != 7 { 1881 | t.Error("Unexpected rank") 1882 | return 1883 | } 1884 | if b.Select(0) != 2 { 1885 | t.Error("Unexpected select") 1886 | return 1887 | } 1888 | if b.Select(1) != 3 { 1889 | t.Error("Unexpected select") 1890 | return 1891 | } 1892 | if b.Select(2) != 5 { 1893 | t.Error("Unexpected select") 1894 | return 1895 | } 1896 | 1897 | if b.Select(5) != 700 { 1898 | t.Error("Unexpected select") 1899 | return 1900 | } 1901 | } 1902 | 1903 | func TestFlip(t *testing.T) { 1904 | b := new(BitSet) 1905 | c := b.Flip(11) 1906 | if c.length != 12 { 1907 | t.Error("Unexpected value: ", c.length) 1908 | return 1909 | } 1910 | d := c.Flip(7) 1911 | if d.length != 12 { 1912 | t.Error("Unexpected value: ", d.length) 1913 | return 1914 | } 1915 | } 1916 | 1917 | func TestFlipRange(t *testing.T) { 1918 | b := new(BitSet) 1919 | b.Set(1).Set(3).Set(5).Set(7).Set(9).Set(11).Set(13).Set(15) 1920 | c := b.FlipRange(4, 25) 1921 | if c.length != 25 { 1922 | t.Error("Unexpected value: ", c.length) 1923 | return 1924 | } 1925 | d := c.FlipRange(8, 24) 1926 | if d.length != 25 { 1927 | t.Error("Unexpected value: ", d.length) 1928 | return 1929 | } 1930 | // 1931 | for i := uint(0); i < 256; i++ { 1932 | for j := uint(0); j <= i; j++ { 1933 | bits := New(i) 1934 | bits.FlipRange(0, j) 1935 | c := bits.Count() 1936 | if c != j { 1937 | t.Error("Unexpected value: ", c, " expected: ", j) 1938 | return 1939 | } 1940 | } 1941 | } 1942 | } 1943 | 1944 | func TestCopy(t *testing.T) { 1945 | a := New(10) 1946 | if a.Copy(nil) != 0 { 1947 | t.Error("No values should be copied") 1948 | return 1949 | } 1950 | a = New(10) 1951 | b := New(20) 1952 | if a.Copy(b) != 10 { 1953 | t.Error("Unexpected value") 1954 | return 1955 | } 1956 | } 1957 | 1958 | func TestCopyUnaligned(t *testing.T) { 1959 | a := New(16) 1960 | a.FlipRange(0, 16) 1961 | b := New(1) 1962 | a.Copy(b) 1963 | if b.Count() > b.Len() { 1964 | t.Errorf("targets copied set count (%d) should never be larger than target's length (%d)", b.Count(), b.Len()) 1965 | } 1966 | if !b.Test(0) { 1967 | t.Errorf("first bit should still be set in copy: %+v", b) 1968 | } 1969 | 1970 | // Test a more complex scenario with a mix of bits set in the unaligned space to verify no bits are lost. 1971 | a = New(32) 1972 | a.Set(0).Set(3).Set(4).Set(16).Set(17).Set(29).Set(31) 1973 | b = New(19) 1974 | a.Copy(b) 1975 | 1976 | const expectedCount = 5 1977 | if b.Count() != expectedCount { 1978 | t.Errorf("targets copied set count: %d, want %d", b.Count(), expectedCount) 1979 | } 1980 | 1981 | if !(b.Test(0) && b.Test(3) && b.Test(4) && b.Test(16) && b.Test(17)) { 1982 | t.Errorf("expected set bits are not set: %+v", b) 1983 | } 1984 | } 1985 | 1986 | func TestCopyFull(t *testing.T) { 1987 | a := New(10) 1988 | b := &BitSet{} 1989 | a.CopyFull(b) 1990 | if b.length != a.length || len(b.set) != len(a.set) { 1991 | t.Error("Expected full length copy") 1992 | return 1993 | } 1994 | for i, v := range a.set { 1995 | if v != b.set[i] { 1996 | t.Error("Unexpected value") 1997 | return 1998 | } 1999 | } 2000 | } 2001 | 2002 | func TestNextSetError(t *testing.T) { 2003 | b := new(BitSet) 2004 | c, d := b.NextSet(1) 2005 | if c != 0 || d { 2006 | t.Error("Unexpected values") 2007 | return 2008 | } 2009 | } 2010 | 2011 | func TestDeleteWithBitStrings(t *testing.T) { 2012 | type testCase struct { 2013 | input []string 2014 | deleteIdx uint 2015 | expected []string 2016 | } 2017 | 2018 | testCases := []testCase{ 2019 | { 2020 | input: []string{ 2021 | "1110000000000000000000000000000000000000000000000000000000000001", 2022 | }, 2023 | deleteIdx: uint(63), 2024 | expected: []string{ 2025 | "0110000000000000000000000000000000000000000000000000000000000001", 2026 | }, 2027 | }, 2028 | { 2029 | input: []string{ 2030 | "1000000000000000000000000000000000000000000000000000000000010101", 2031 | }, 2032 | deleteIdx: uint(0), 2033 | expected: []string{ 2034 | "0100000000000000000000000000000000000000000000000000000000001010", 2035 | }, 2036 | }, 2037 | { 2038 | input: []string{ 2039 | "0000000000000000000000000000000000000000000000000000000000111000", 2040 | }, 2041 | deleteIdx: uint(4), 2042 | expected: []string{ 2043 | "0000000000000000000000000000000000000000000000000000000000011000", 2044 | }, 2045 | }, 2046 | { 2047 | input: []string{ 2048 | "1000000000000000000000000000000000000000000000000000000000000001", 2049 | "1010000000000000000000000000000000000000000000000000000000000001", 2050 | }, 2051 | deleteIdx: uint(63), 2052 | expected: []string{ 2053 | "1000000000000000000000000000000000000000000000000000000000000001", 2054 | "0101000000000000000000000000000000000000000000000000000000000000", 2055 | }, 2056 | }, 2057 | { 2058 | input: []string{ 2059 | "1000000000000000000000000000000000000000000000000000000000000000", 2060 | "1000000000000000000000000000000000000000000000000000000000000001", 2061 | "1000000000000000000000000000000000000000000000000000000000000001", 2062 | }, 2063 | deleteIdx: uint(64), 2064 | expected: []string{ 2065 | "1000000000000000000000000000000000000000000000000000000000000000", 2066 | "1100000000000000000000000000000000000000000000000000000000000000", 2067 | "0100000000000000000000000000000000000000000000000000000000000000", 2068 | }, 2069 | }, 2070 | { 2071 | input: []string{ 2072 | "0000000000000000000000000000000000000000000000000000000000000001", 2073 | "0000000000000000000000000000000000000000000000000000000000000001", 2074 | "0000000000000000000000000000000000000000000000000000000000000001", 2075 | "0000000000000000000000000000000000000000000000000000000000000001", 2076 | "0000000000000000000000000000000000000000000000000000000000000001", 2077 | }, 2078 | deleteIdx: uint(256), 2079 | expected: []string{ 2080 | "0000000000000000000000000000000000000000000000000000000000000001", 2081 | "0000000000000000000000000000000000000000000000000000000000000001", 2082 | "0000000000000000000000000000000000000000000000000000000000000001", 2083 | "0000000000000000000000000000000000000000000000000000000000000001", 2084 | "0000000000000000000000000000000000000000000000000000000000000000", 2085 | }, 2086 | }, 2087 | } 2088 | 2089 | for _, tc := range testCases { 2090 | var input []uint64 2091 | for _, inputElement := range tc.input { 2092 | parsed, _ := strconv.ParseUint(inputElement, 2, 64) 2093 | input = append(input, parsed) 2094 | } 2095 | 2096 | var expected []uint64 2097 | for _, expectedElement := range tc.expected { 2098 | parsed, _ := strconv.ParseUint(expectedElement, 2, 64) 2099 | expected = append(expected, parsed) 2100 | } 2101 | 2102 | b := From(input) 2103 | b.DeleteAt(tc.deleteIdx) 2104 | if len(b.set) != len(expected) { 2105 | t.Errorf("Length of sets expected to be %d, but was %d", len(expected), len(b.set)) 2106 | return 2107 | } 2108 | for i := range b.set { 2109 | if b.set[i] != expected[i] { 2110 | t.Errorf("Unexpected output\nExpected: %b\nGot: %b", expected[i], b.set[i]) 2111 | return 2112 | } 2113 | } 2114 | } 2115 | } 2116 | 2117 | func TestDeleteWithBitSetInstance(t *testing.T) { 2118 | length := uint(256) 2119 | bitSet := New(length) 2120 | 2121 | // the indexes that get set in the bit set 2122 | indexesToSet := []uint{0, 1, 126, 127, 128, 129, 170, 171, 200, 201, 202, 203, 255} 2123 | 2124 | // the position we delete from the bitset 2125 | deleteAt := uint(127) 2126 | 2127 | // the indexes that we expect to be set after the delete 2128 | expectedToBeSet := []uint{0, 1, 126, 127, 128, 169, 170, 199, 200, 201, 202, 254} 2129 | 2130 | expected := make(map[uint]struct{}) 2131 | for _, index := range expectedToBeSet { 2132 | expected[index] = struct{}{} 2133 | } 2134 | 2135 | for _, index := range indexesToSet { 2136 | bitSet.Set(index) 2137 | } 2138 | 2139 | bitSet.DeleteAt(deleteAt) 2140 | 2141 | for i := uint(0); i < length; i++ { 2142 | if _, ok := expected[i]; ok { 2143 | if !bitSet.Test(i) { 2144 | t.Errorf("Expected index %d to be set, but wasn't", i) 2145 | } 2146 | } else { 2147 | if bitSet.Test(i) { 2148 | t.Errorf("Expected index %d to not be set, but was", i) 2149 | } 2150 | } 2151 | } 2152 | } 2153 | 2154 | func TestWriteTo(t *testing.T) { 2155 | const length = 9585 2156 | const oneEvery = 97 2157 | addBuf := []byte(`12345678`) 2158 | bs := New(length) 2159 | // Add some bits 2160 | for i := uint(0); i < length; i += oneEvery { 2161 | bs = bs.Set(i) 2162 | } 2163 | 2164 | var buf bytes.Buffer 2165 | n, err := bs.WriteTo(&buf) 2166 | if err != nil { 2167 | t.Fatal(err) 2168 | } 2169 | wantSz := buf.Len() // Size of the serialized data in bytes. 2170 | if n != int64(wantSz) { 2171 | t.Errorf("want write size to be %d, got %d", wantSz, n) 2172 | } 2173 | buf.Write(addBuf) // Add additional data on stream. 2174 | 2175 | // Generate test input for regression tests: 2176 | if false { 2177 | gzout := bytes.NewBuffer(nil) 2178 | gz, err := gzip.NewWriterLevel(gzout, 9) 2179 | if err != nil { 2180 | t.Fatal(err) 2181 | } 2182 | gz.Write(buf.Bytes()) 2183 | gz.Close() 2184 | t.Log("Encoded:", base64.StdEncoding.EncodeToString(gzout.Bytes())) 2185 | } 2186 | 2187 | // Read back. 2188 | bs = New(length) 2189 | n, err = bs.ReadFrom(&buf) 2190 | if err != nil { 2191 | t.Fatal(err) 2192 | } 2193 | if n != int64(wantSz) { 2194 | t.Errorf("want read size to be %d, got %d", wantSz, n) 2195 | } 2196 | // Check bits 2197 | for i := uint(0); i < length; i += oneEvery { 2198 | if !bs.Test(i) { 2199 | t.Errorf("bit %d was not set", i) 2200 | } 2201 | } 2202 | 2203 | more, err := io.ReadAll(&buf) 2204 | if err != nil { 2205 | t.Fatal(err) 2206 | } 2207 | if !bytes.Equal(more, addBuf) { 2208 | t.Fatalf("extra mismatch. got %v, want %v", more, addBuf) 2209 | } 2210 | } 2211 | 2212 | type inCompleteRetBufReader struct { 2213 | returnEvery int64 2214 | reader io.Reader 2215 | offset int64 2216 | } 2217 | 2218 | func (ir *inCompleteRetBufReader) Read(b []byte) (n int, err error) { 2219 | if ir.returnEvery > 0 { 2220 | maxRead := ir.returnEvery - (ir.offset % ir.returnEvery) 2221 | if len(b) > int(maxRead) { 2222 | b = b[:maxRead] 2223 | } 2224 | } 2225 | n, err = ir.reader.Read(b) 2226 | ir.offset += int64(n) 2227 | return 2228 | } 2229 | 2230 | func TestReadFrom(t *testing.T) { 2231 | addBuf := []byte(`12345678`) // Bytes after stream 2232 | tests := []struct { 2233 | length uint 2234 | oneEvery uint 2235 | input string // base64+gzipped 2236 | wantErr error 2237 | returnEvery int64 2238 | }{ 2239 | { 2240 | length: 9585, 2241 | oneEvery: 97, 2242 | input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwMSACVgYGBg4sIgLMDAwKGARd2BgYGjAFB41noDx6IAJajw64IAajw4UoMajg4ZR4/EaP5pQh1g+MDQyNjE1M7cABAAA//9W5OoOwAQAAA==", 2243 | returnEvery: 127, 2244 | }, 2245 | { 2246 | length: 1337, 2247 | oneEvery: 42, 2248 | input: "H4sIAAAAAAAC/2IAA1ZLBgYWEIPRAUQKgJkMcCZYisEBzkSSYkSTYqCxAYZGxiamZuYWgAAAAP//D0wyWbgAAAA=", 2249 | }, 2250 | { 2251 | length: 1337, // Truncated input. 2252 | oneEvery: 42, 2253 | input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwARmAQIAAP//vR3xdRkAAAA=", 2254 | wantErr: io.ErrUnexpectedEOF, 2255 | }, 2256 | { 2257 | length: 1337, // Empty input. 2258 | oneEvery: 42, 2259 | input: "H4sIAAAAAAAC/wEAAP//AAAAAAAAAAA=", 2260 | wantErr: io.ErrUnexpectedEOF, 2261 | }, 2262 | } 2263 | 2264 | for i, test := range tests { 2265 | t.Run(fmt.Sprint(i), func(t *testing.T) { 2266 | fatalErr := func(err error) { 2267 | t.Helper() 2268 | if err != nil { 2269 | t.Fatal(err) 2270 | } 2271 | } 2272 | 2273 | var buf bytes.Buffer 2274 | b, err := base64.StdEncoding.DecodeString(test.input) 2275 | fatalErr(err) 2276 | gz, err := gzip.NewReader(bytes.NewBuffer(b)) 2277 | fatalErr(err) 2278 | _, err = io.Copy(&buf, gz) 2279 | fatalErr(err) 2280 | fatalErr(gz.Close()) 2281 | 2282 | bs := New(test.length) 2283 | _, err = bs.ReadFrom(&inCompleteRetBufReader{returnEvery: test.returnEvery, reader: &buf}) 2284 | if err != nil { 2285 | if errors.Is(err, test.wantErr) { 2286 | // Correct, nothing more we can test. 2287 | return 2288 | } 2289 | t.Fatalf("did not get expected error %v, got %v", test.wantErr, err) 2290 | } else { 2291 | if test.wantErr != nil { 2292 | t.Fatalf("did not get expected error %v", test.wantErr) 2293 | } 2294 | } 2295 | fatalErr(err) 2296 | 2297 | // Test if correct bits are set. 2298 | for i := uint(0); i < test.length; i++ { 2299 | want := i%test.oneEvery == 0 2300 | got := bs.Test(i) 2301 | if want != got { 2302 | t.Errorf("bit %d was %v, should be %v", i, got, want) 2303 | } 2304 | } 2305 | 2306 | more, err := io.ReadAll(&buf) 2307 | fatalErr(err) 2308 | 2309 | if !bytes.Equal(more, addBuf) { 2310 | t.Errorf("extra mismatch. got %v, want %v", more, addBuf) 2311 | } 2312 | }) 2313 | } 2314 | } 2315 | 2316 | func TestSetAll(t *testing.T) { 2317 | test := func(name string, bs *BitSet, want uint) { 2318 | t.Run(name, func(t *testing.T) { 2319 | bs.SetAll() 2320 | if bs.Count() != want { 2321 | t.Errorf("expected %d bits to be set, got %d", want, bs.Count()) 2322 | } 2323 | }) 2324 | } 2325 | 2326 | test("nil", nil, 0) 2327 | for _, length := range []uint{0, 1, 10, 63, 64, 65, 100, 640} { 2328 | test(fmt.Sprintf("length %d", length), New(length), length) 2329 | } 2330 | } 2331 | 2332 | func TestShiftLeft(t *testing.T) { 2333 | data := []uint{5, 28, 45, 72, 89} 2334 | 2335 | test := func(name string, bits uint) { 2336 | t.Run(name, func(t *testing.T) { 2337 | b := New(200) 2338 | for _, i := range data { 2339 | b.Set(i) 2340 | } 2341 | 2342 | b.ShiftLeft(bits) 2343 | 2344 | if int(b.Count()) != len(data) { 2345 | t.Error("bad bits count") 2346 | } 2347 | 2348 | for _, i := range data { 2349 | if !b.Test(i + bits) { 2350 | t.Errorf("bit %v is not set", i+bits) 2351 | } 2352 | } 2353 | }) 2354 | } 2355 | 2356 | test("zero", 0) 2357 | test("no page change", 19) 2358 | test("shift to full page", 38) 2359 | test("full page shift", 64) 2360 | test("no page split", 80) 2361 | test("with page split", 114) 2362 | test("with extension", 242) 2363 | } 2364 | 2365 | func TestShiftRight(t *testing.T) { 2366 | data := []uint{5, 28, 45, 72, 89} 2367 | 2368 | test := func(name string, bits uint) { 2369 | t.Run(name, func(t *testing.T) { 2370 | b := New(200) 2371 | for _, i := range data { 2372 | b.Set(i) 2373 | } 2374 | 2375 | b.ShiftRight(bits) 2376 | 2377 | count := 0 2378 | for _, i := range data { 2379 | if i > bits { 2380 | count++ 2381 | 2382 | if !b.Test(i - bits) { 2383 | t.Errorf("bit %v is not set", i-bits) 2384 | } 2385 | } 2386 | } 2387 | 2388 | if int(b.Count()) != count { 2389 | t.Error("bad bits count") 2390 | } 2391 | }) 2392 | } 2393 | 2394 | test("zero", 0) 2395 | test("no page change", 3) 2396 | test("no page split", 20) 2397 | test("with page split", 40) 2398 | test("full page shift", 64) 2399 | test("with extension", 70) 2400 | test("full shift", 89) 2401 | test("remove all", 242) 2402 | } 2403 | 2404 | func TestWord(t *testing.T) { 2405 | data := []uint64{0x0bfd85fc01af96dd, 0x3fe212a7eae11414, 0x7aa412221245dee1, 0x557092c1711306d5} 2406 | testCases := map[string]struct { 2407 | index uint 2408 | expected uint64 2409 | }{ 2410 | "first word": { 2411 | index: 0, 2412 | expected: 0x0bfd85fc01af96dd, 2413 | }, 2414 | "third word": { 2415 | index: 128, 2416 | expected: 0x7aa412221245dee1, 2417 | }, 2418 | "off the edge": { 2419 | index: 256, 2420 | expected: 0, 2421 | }, 2422 | "way off the edge": { 2423 | index: 6346235235, 2424 | expected: 0, 2425 | }, 2426 | "split between two words": { 2427 | index: 96, 2428 | expected: 0x1245dee13fe212a7, 2429 | }, 2430 | "partly off edge": { 2431 | index: 254, 2432 | expected: 1, 2433 | }, 2434 | "skip one nibble": { 2435 | index: 4, 2436 | expected: 0x40bfd85fc01af96d, 2437 | }, 2438 | "slightly offset results": { 2439 | index: 65, 2440 | expected: 0x9ff10953f5708a0a, 2441 | }, 2442 | } 2443 | 2444 | for name, testCase := range testCases { 2445 | t.Run(name, func(t *testing.T) { 2446 | bitSet := From(data) 2447 | output := bitSet.GetWord64AtBit(testCase.index) 2448 | 2449 | if output != testCase.expected { 2450 | t.Errorf("Word should have returned %d for input %d, but returned %d", testCase.expected, testCase.index, output) 2451 | } 2452 | }) 2453 | } 2454 | } 2455 | 2456 | func TestPreviousSet(t *testing.T) { 2457 | v := New(128) 2458 | v.Set(0) 2459 | v.Set(2) 2460 | v.Set(4) 2461 | v.Set(120) 2462 | for _, tt := range []struct { 2463 | index uint 2464 | want uint 2465 | wantFound bool 2466 | }{ 2467 | {0, 0, true}, 2468 | {1, 0, true}, 2469 | {2, 2, true}, 2470 | {3, 2, true}, 2471 | {4, 4, true}, 2472 | {5, 4, true}, 2473 | {100, 4, true}, 2474 | {120, 120, true}, 2475 | {121, 120, true}, 2476 | {1024, 0, false}, 2477 | } { 2478 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { 2479 | got, found := v.PreviousSet(tt.index) 2480 | if got != tt.want || found != tt.wantFound { 2481 | t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) 2482 | } 2483 | }) 2484 | } 2485 | v.ClearAll() 2486 | for _, tt := range []struct { 2487 | index uint 2488 | want uint 2489 | wantFound bool 2490 | }{ 2491 | {0, 0, false}, 2492 | {120, 0, false}, 2493 | {1024, 0, false}, 2494 | } { 2495 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { 2496 | got, found := v.PreviousSet(tt.index) 2497 | if got != tt.want || found != tt.wantFound { 2498 | t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) 2499 | } 2500 | }) 2501 | } 2502 | } 2503 | 2504 | func TestPreviousClear(t *testing.T) { 2505 | v := New(128) 2506 | v.Set(0) 2507 | v.Set(2) 2508 | v.Set(4) 2509 | v.Set(120) 2510 | for _, tt := range []struct { 2511 | index uint 2512 | want uint 2513 | wantFound bool 2514 | }{ 2515 | {0, 0, false}, 2516 | {1, 1, true}, 2517 | {2, 1, true}, 2518 | {3, 3, true}, 2519 | {4, 3, true}, 2520 | {5, 5, true}, 2521 | {100, 100, true}, 2522 | {120, 119, true}, 2523 | {121, 121, true}, 2524 | {1024, 0, false}, 2525 | } { 2526 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { 2527 | got, found := v.PreviousClear(tt.index) 2528 | if got != tt.want || found != tt.wantFound { 2529 | t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) 2530 | } 2531 | }) 2532 | } 2533 | v.SetAll() 2534 | for _, tt := range []struct { 2535 | index uint 2536 | want uint 2537 | wantFound bool 2538 | }{ 2539 | {0, 0, false}, 2540 | {120, 0, false}, 2541 | {1024, 0, false}, 2542 | } { 2543 | t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { 2544 | got, found := v.PreviousClear(tt.index) 2545 | if got != tt.want || found != tt.wantFound { 2546 | t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) 2547 | } 2548 | }) 2549 | } 2550 | } 2551 | 2552 | func TestBitSetOnesBetween(t *testing.T) { 2553 | testCases := []struct { 2554 | name string 2555 | input *BitSet 2556 | from uint 2557 | to uint 2558 | expected uint 2559 | }{ 2560 | {"empty range", New(64).Set(0).Set(1), 5, 5, 0}, 2561 | {"invalid range", New(64).Set(0).Set(1), 5, 3, 0}, 2562 | {"single word", New(64).Set(1).Set(2).Set(3), 1, 3, 2}, 2563 | {"single word full", New(64).Set(0).Set(1).Set(2).Set(3), 0, 4, 4}, 2564 | {"cross word boundary", New(128).Set(63).Set(64).Set(65), 63, 66, 3}, 2565 | {"multiple words", New(256).Set(0).Set(63).Set(64).Set(127).Set(128), 0, 129, 5}, 2566 | {"large gap", New(256).Set(0).Set(100).Set(200), 0, 201, 3}, 2567 | } 2568 | 2569 | for _, tc := range testCases { 2570 | t.Run(tc.name, func(t *testing.T) { 2571 | got := tc.input.OnesBetween(tc.from, tc.to) 2572 | if got != tc.expected { 2573 | t.Errorf("OnesBetween(%d, %d) = %d, want %d", 2574 | tc.from, tc.to, got, tc.expected) 2575 | } 2576 | }) 2577 | } 2578 | 2579 | // Property-based testing 2580 | const numTests = 1e5 2581 | seed := time.Now().UnixNano() 2582 | rng := rand.New(rand.NewSource(seed)) 2583 | t.Logf("Seed: %d", seed) 2584 | 2585 | for i := 0; i < numTests; i++ { 2586 | size := uint(rng.Intn(1024) + 64) 2587 | bs := New(size) 2588 | 2589 | // Set random bits 2590 | for j := 0; j < int(size/4); j++ { 2591 | bs.Set(uint(rng.Intn(int(size)))) 2592 | } 2593 | 2594 | // Generate random range 2595 | from := uint(rng.Intn(int(size))) 2596 | to := from + uint(rng.Intn(int(size-from))) 2597 | 2598 | // Compare with naive implementation 2599 | got := bs.OnesBetween(from, to) 2600 | want := uint(0) 2601 | for j := from; j < to; j++ { 2602 | if bs.Test(j) { 2603 | want++ 2604 | } 2605 | } 2606 | 2607 | if got != want { 2608 | t.Errorf("Case %d: OnesBetween(%d, %d) = %d, want %d", 2609 | i, from, to, got, want) 2610 | } 2611 | } 2612 | } 2613 | 2614 | func BenchmarkBitSetOnesBetween(b *testing.B) { 2615 | sizes := []int{64, 256, 1024, 4096, 16384} 2616 | densities := []float64{0.1, 0.5, 0.9} // Different bit densities to test 2617 | rng := rand.New(rand.NewSource(42)) 2618 | 2619 | for _, size := range sizes { 2620 | for _, density := range densities { 2621 | // Create bitset with given density 2622 | bs := New(uint(size)) 2623 | for i := 0; i < int(float64(size)*density); i++ { 2624 | bs.Set(uint(rng.Intn(size))) 2625 | } 2626 | 2627 | // Generate random ranges 2628 | ranges := make([][2]uint, 1000) 2629 | for i := range ranges { 2630 | from := uint(rng.Intn(size)) 2631 | to := from + uint(rng.Intn(size-int(from))) 2632 | ranges[i] = [2]uint{from, to} 2633 | } 2634 | 2635 | name := fmt.Sprintf("size=%d/density=%.1f", size, density) 2636 | b.Run(name, func(b *testing.B) { 2637 | for i := 0; i < b.N; i++ { 2638 | r := ranges[i%len(ranges)] 2639 | _ = bs.OnesBetween(r[0], r[1]) 2640 | } 2641 | }) 2642 | } 2643 | } 2644 | } 2645 | 2646 | func generatePextTestCases(n int) [][2]uint64 { 2647 | cases := make([][2]uint64, n) 2648 | for i := range cases { 2649 | cases[i][0] = rand.Uint64() 2650 | cases[i][1] = rand.Uint64() 2651 | } 2652 | return cases 2653 | } 2654 | 2655 | func BenchmarkPEXT(b *testing.B) { 2656 | // Generate test cases 2657 | testCases := generatePextTestCases(1000) 2658 | 2659 | b.ResetTimer() 2660 | 2661 | var r uint64 2662 | for i := 0; i < b.N; i++ { 2663 | tc := testCases[i%len(testCases)] 2664 | r = pext(tc[0], tc[1]) 2665 | } 2666 | _ = r // prevent optimization 2667 | } 2668 | 2669 | func BenchmarkPDEP(b *testing.B) { 2670 | // Generate test cases 2671 | testCases := generatePextTestCases(1000) 2672 | 2673 | b.ResetTimer() 2674 | 2675 | var r uint64 2676 | for i := 0; i < b.N; i++ { 2677 | tc := testCases[i%len(testCases)] 2678 | r = pdep(tc[0], tc[1]) 2679 | } 2680 | _ = r // prevent optimization 2681 | } 2682 | 2683 | func TestPext(t *testing.T) { 2684 | const numTests = 1e6 2685 | seed := time.Now().UnixNano() 2686 | rng := rand.New(rand.NewSource(seed)) 2687 | t.Logf("Seed: %d", seed) 2688 | 2689 | for i := 0; i < numTests; i++ { 2690 | w := rng.Uint64() 2691 | m := rng.Uint64() 2692 | result := pext(w, m) 2693 | popCount := bits.OnesCount64(m) 2694 | 2695 | // Test invariants 2696 | if popCount > 0 && result >= (uint64(1)< bits.OnesCount64(w&m) { 2702 | t.Fatalf("Case %d: result has more 1s than masked input: result=%x, input&mask=%x", 2703 | i, result, w&m) 2704 | } 2705 | 2706 | // Test that extracted bits preserve relative ordering: 2707 | // For each bit position that's set in the mask (m): 2708 | // 1. Extract a bit from result (resultCopy&1) 2709 | // 2. Get corresponding input bit from w (w>>j&1) 2710 | // 3. XOR them - if different, bits weren't preserved correctly 2711 | resultCopy := result 2712 | for j := 0; j < 64; j++ { 2713 | // Check if mask bit is set at position j 2714 | if m&(uint64(1)<>j&1 gets bit j from original input 2717 | // XOR (^) checks if they match 2718 | if (resultCopy&1)^(w>>j&1) != 0 { 2719 | t.Fatalf("Case %d: bit ordering violation at position %d", i, j) 2720 | } 2721 | // Shift to examine next bit in packed result 2722 | resultCopy >>= 1 2723 | } 2724 | } 2725 | } 2726 | } 2727 | 2728 | func TestPdep(t *testing.T) { 2729 | const numTests = 1e6 2730 | seed := time.Now().UnixNano() 2731 | rng := rand.New(rand.NewSource(seed)) 2732 | t.Logf("Seed: %d", seed) 2733 | 2734 | for i := 0; i < numTests; i++ { 2735 | w := rng.Uint64() // value to deposit 2736 | m := rng.Uint64() // mask 2737 | result := pdep(w, m) 2738 | popCount := bits.OnesCount64(m) 2739 | 2740 | // Test invariants 2741 | if result&^m != 0 { 2742 | t.Fatalf("Case %d: result %x has bits set outside of mask %x", 2743 | i, result, m) 2744 | } 2745 | 2746 | if bits.OnesCount64(result) > bits.OnesCount64(w) { 2747 | t.Fatalf("Case %d: result has more 1s than input: result=%x, input=%x", 2748 | i, result, w) 2749 | } 2750 | 2751 | // Verify by using PEXT to extract bits back 2752 | // The composition of PEXT(PDEP(x,m),m) should equal x masked to popcount bits 2753 | extracted := pext(result, m) 2754 | maskBits := (uint64(1) << popCount) - 1 2755 | if (extracted & maskBits) != (w & maskBits) { 2756 | t.Fatalf("Case %d: PEXT(PDEP(w,m),m) != w: got=%x, want=%x (w=%x, m=%x)", 2757 | i, extracted&maskBits, w&maskBits, w, m) 2758 | } 2759 | } 2760 | } 2761 | 2762 | func TestBitSetExtract(t *testing.T) { 2763 | // Property-based tests 2764 | const numTests = 1e4 2765 | seed := time.Now().UnixNano() 2766 | rng := rand.New(rand.NewSource(seed)) 2767 | t.Logf("Seed: %d", seed) 2768 | 2769 | for i := 0; i < numTests; i++ { 2770 | // Create random bitsets 2771 | size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits 2772 | src := New(size) 2773 | mask := New(size) 2774 | dst := New(size) 2775 | 2776 | // Set random bits 2777 | for j := 0; j < int(size/4); j++ { 2778 | src.Set(uint(rng.Intn(int(size)))) 2779 | mask.Set(uint(rng.Intn(int(size)))) 2780 | } 2781 | 2782 | // Extract bits 2783 | src.ExtractTo(mask, dst) 2784 | 2785 | // Test invariants 2786 | if dst.Count() > src.IntersectionCardinality(mask) { 2787 | t.Errorf("Case %d: result has more 1s than masked input", i) 2788 | } 2789 | 2790 | // Test bits are properly extracted and packed 2791 | pos := uint(0) 2792 | for j := uint(0); j < size; j++ { 2793 | if mask.Test(j) { 2794 | if src.Test(j) != dst.Test(pos) { 2795 | t.Errorf("Case %d: bit ordering violation at source position %d", i, j) 2796 | } 2797 | pos++ 2798 | } 2799 | } 2800 | } 2801 | 2802 | // Keep existing test cases 2803 | testCases := []struct { 2804 | name string 2805 | src *BitSet // source bits 2806 | mask *BitSet // mask bits 2807 | expected *BitSet // expected extracted bits 2808 | }{ 2809 | { 2810 | name: "single bit", 2811 | src: New(8).Set(1), // 0b01 2812 | mask: New(8).Set(1), // 0b01 2813 | expected: New(8).Set(0), // 0b1 2814 | }, 2815 | { 2816 | name: "two sequential bits", 2817 | src: New(8).Set(0).Set(1), // 0b11 2818 | mask: New(8).Set(0).Set(1), // 0b11 2819 | expected: New(8).Set(0).Set(1), // 0b11 2820 | }, 2821 | { 2822 | name: "sparse bits", 2823 | src: New(16).Set(0).Set(10), // 0b10000000001 2824 | mask: New(16).Set(0).Set(5).Set(10), // 0b10000100001 2825 | expected: New(8).Set(0).Set(2), // 0b101 2826 | }, 2827 | { 2828 | name: "masked off bits", 2829 | src: New(8).Set(0).Set(1).Set(2).Set(3), // 0b1111 2830 | mask: New(8).Set(0).Set(2), // 0b0101 2831 | expected: New(8).Set(0).Set(1), // 0b11 2832 | }, 2833 | { 2834 | name: "cross word boundary", 2835 | src: New(128).Set(63).Set(64).Set(65), 2836 | mask: New(128).Set(63).Set(64).Set(65), 2837 | expected: New(8).Set(0).Set(1).Set(2), 2838 | }, 2839 | { 2840 | name: "large gap", 2841 | src: New(256).Set(0).Set(100).Set(200), 2842 | mask: New(256).Set(0).Set(100).Set(200), 2843 | expected: New(8).Set(0).Set(1).Set(2), 2844 | }, 2845 | { 2846 | name: "extracting zeros", 2847 | src: New(8), // 0b00 2848 | mask: New(8).Set(0).Set(1).Set(2), // 0b111 2849 | expected: New(8), // 0b000 2850 | }, 2851 | } 2852 | 2853 | for _, tc := range testCases { 2854 | t.Run(tc.name, func(t *testing.T) { 2855 | dst := New(tc.expected.Len()) 2856 | tc.src.ExtractTo(tc.mask, dst) 2857 | if !dst.Equal(tc.expected) { 2858 | t.Errorf("got %v, expected %v", dst, tc.expected) 2859 | } 2860 | 2861 | // Verify inverse relationship within the mask bits 2862 | deposited := New(tc.src.Len()) 2863 | dst.DepositTo(tc.mask, deposited) 2864 | 2865 | // Only bits selected by the mask should match between source and deposited 2866 | maskedSource := tc.src.Intersection(tc.mask) 2867 | maskedDeposited := deposited.Intersection(tc.mask) 2868 | 2869 | if !maskedSource.Equal(maskedDeposited) { 2870 | t.Error("DepositTo(ExtractTo(x,m),m) doesn't preserve masked source bits") 2871 | } 2872 | }) 2873 | } 2874 | } 2875 | 2876 | func TestBitSetDeposit(t *testing.T) { 2877 | // Property-based tests 2878 | const numTests = 1e4 2879 | seed := time.Now().UnixNano() 2880 | rng := rand.New(rand.NewSource(seed)) 2881 | t.Logf("Seed: %d", seed) 2882 | 2883 | for i := 0; i < numTests; i++ { 2884 | // Create random bitsets 2885 | size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits 2886 | src := New(size) 2887 | mask := New(size) 2888 | dst := New(size) 2889 | 2890 | // Set random bits 2891 | for j := 0; j < int(size/4); j++ { 2892 | src.Set(uint(rng.Intn(int(mask.Count() + 1)))) 2893 | mask.Set(uint(rng.Intn(int(size)))) 2894 | } 2895 | 2896 | // Deposit bits 2897 | src.DepositTo(mask, dst) 2898 | 2899 | // Test invariants 2900 | if dst.Count() > src.Count() { 2901 | t.Errorf("Case %d: result has more 1s than input", i) 2902 | } 2903 | 2904 | if (dst.Bytes()[0] &^ mask.Bytes()[0]) != 0 { 2905 | t.Errorf("Case %d: result has bits set outside of mask", i) 2906 | } 2907 | 2908 | // Extract bits back and verify 2909 | extracted := New(size) 2910 | dst.ExtractTo(mask, extracted) 2911 | maskBits := New(size) 2912 | for j := uint(0); j < mask.Count(); j++ { 2913 | maskBits.Set(j) 2914 | } 2915 | srcMasked := src.Clone() 2916 | srcMasked.InPlaceIntersection(maskBits) 2917 | if !extracted.Equal(srcMasked) { 2918 | t.Errorf("Case %d: ExtractTo(DepositTo(x,m),m) != x", i) 2919 | } 2920 | } 2921 | 2922 | // Keep existing test cases 2923 | testCases := []struct { 2924 | name string 2925 | src *BitSet // source bits (packed in low positions) 2926 | mask *BitSet // mask bits (positions to deposit into) 2927 | dst *BitSet // destination bits (initially set) 2928 | expected *BitSet // expected result 2929 | }{ 2930 | { 2931 | name: "sparse bits", 2932 | src: New(8).Set(0), // 0b01 2933 | mask: New(8).Set(0).Set(5), // 0b100001 2934 | expected: New(8).Set(0), // 0b000001 2935 | }, 2936 | { 2937 | name: "masked off bits", 2938 | src: New(8).Set(0).Set(1), // 0b11 2939 | mask: New(8).Set(0).Set(2), // 0b101 2940 | expected: New(8).Set(0).Set(2), // 0b101 2941 | }, 2942 | { 2943 | name: "cross word boundary", 2944 | src: New(8).Set(0).Set(1), // 0b11 2945 | mask: New(128).Set(63).Set(64), // bits across word boundary 2946 | expected: New(128).Set(63).Set(64), // bits deposited across boundary 2947 | }, 2948 | { 2949 | name: "large gaps", 2950 | src: New(8).Set(0).Set(1), // 0b11 2951 | mask: New(128).Set(0).Set(100), // widely spaced bits 2952 | expected: New(128).Set(0).Set(100), // deposited into sparse positions 2953 | }, 2954 | { 2955 | name: "depositing zeros", 2956 | src: New(8), // 0b00 2957 | mask: New(8).Set(0).Set(1).Set(2), // 0b111 2958 | expected: New(8), // 0b000 2959 | }, 2960 | { 2961 | name: "preserve unmasked bits", 2962 | src: New(8), // empty source 2963 | mask: New(8), // empty mask 2964 | dst: New(8).Set(1).Set(2).Set(3), // dst has some bits set 2965 | expected: New(8).Set(1).Set(2).Set(3), // should remain unchanged 2966 | }, 2967 | { 2968 | name: "preserve bits outside mask within word", 2969 | src: New(8).Set(0), // source has bit 0 set 2970 | mask: New(8).Set(1), // only depositing into bit 1 2971 | dst: New(8).Set(0).Set(2).Set(3), // dst has bits 0,2,3 set 2972 | expected: New(8).Set(0).Set(1).Set(2).Set(3), // bits 0,2,3 should remain, bit 1 should be set 2973 | }, 2974 | } 2975 | 2976 | for _, tc := range testCases { 2977 | t.Run(tc.name, func(t *testing.T) { 2978 | var dst *BitSet 2979 | if tc.dst == nil { 2980 | dst = New(tc.expected.Len()) 2981 | } else { 2982 | dst = tc.dst.Clone() 2983 | } 2984 | tc.src.DepositTo(tc.mask, dst) 2985 | if !dst.Equal(tc.expected) { 2986 | t.Errorf("got %v, expected %v", dst, tc.expected) 2987 | } 2988 | 2989 | // Verify inverse relationship for set bits up to mask cardinality 2990 | extracted := New(tc.src.Len()) 2991 | dst.ExtractTo(tc.mask, extracted) 2992 | 2993 | if !extracted.Equal(tc.src) { 2994 | t.Error("ExtractTo(DepositTo(x,m),m) doesn't preserve source bits that were selected by mask") 2995 | } 2996 | }) 2997 | } 2998 | } 2999 | 3000 | func BenchmarkBitSetExtractDeposit(b *testing.B) { 3001 | sizes := []int{64, 256, 1024, 4096, 16384, 2 << 15} 3002 | rng := rand.New(rand.NewSource(42)) // fixed seed for reproducibility 3003 | 3004 | for _, size := range sizes { 3005 | // Create source with random bits 3006 | src := New(uint(size)) 3007 | for i := 0; i < size/4; i++ { // Set ~25% of bits 3008 | src.Set(uint(rng.Intn(size))) 3009 | } 3010 | 3011 | // Create mask with random bits 3012 | mask := New(uint(size)) 3013 | for i := 0; i < size/4; i++ { 3014 | mask.Set(uint(rng.Intn(size))) 3015 | } 3016 | 3017 | b.Run(fmt.Sprintf("size=%d/fn=ExtractTo", size), func(b *testing.B) { 3018 | dst := New(uint(size)) 3019 | b.ReportAllocs() 3020 | b.ResetTimer() 3021 | for i := 0; i < b.N; i++ { 3022 | src.ExtractTo(mask, dst) 3023 | dst.ClearAll() 3024 | } 3025 | }) 3026 | 3027 | b.Run(fmt.Sprintf("size=%d/fn=DepositTo", size), func(b *testing.B) { 3028 | dst := New(uint(size)) 3029 | b.ReportAllocs() 3030 | b.ResetTimer() 3031 | for i := 0; i < b.N; i++ { 3032 | src.DepositTo(mask, dst) 3033 | dst.ClearAll() 3034 | } 3035 | }) 3036 | } 3037 | } 3038 | -------------------------------------------------------------------------------- /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<> 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 | --------------------------------------------------------------------------------