pax_global_header00006660000000000000000000000064150774376740014536gustar00rootroot0000000000000052 comment=d02158d114ce8c324e9e91077709e7769e805574 bitset-1.24.2/000077500000000000000000000000001507743767400131165ustar00rootroot00000000000000bitset-1.24.2/.github/000077500000000000000000000000001507743767400144565ustar00rootroot00000000000000bitset-1.24.2/.github/FUNDING.yml000066400000000000000000000004441507743767400162750ustar00rootroot00000000000000# You can add one username per supported platform and one custom link patreon: # Replace with your Patreon username open_collective: # Replace with your Open Collective username ko_fi: # Replace with your Ko-fi username custom: https://donate.mcc.org/ # Replace with your custom donation URL bitset-1.24.2/.github/SECURITY.md000066400000000000000000000015651507743767400162560ustar00rootroot00000000000000# Security Policy ## Supported Versions Security updates are applied only to the latest release. ## Reporting a Vulnerability 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. Please use the following contact information for reporting a vulnerability: - [Daniel Lemire](https://github.com/lemire) - [daniel@lemire.me](mailto:daniel@lemire.me) In your report, please include: - A description of the vulnerability and its impact - How to reproduce the it - Affected versions 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. bitset-1.24.2/.github/dependabot.yml000066400000000000000000000007761507743767400173200ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: github-actions: patterns: - "*" bitset-1.24.2/.github/workflows/000077500000000000000000000000001507743767400165135ustar00rootroot00000000000000bitset-1.24.2/.github/workflows/legacy86.go000066400000000000000000000005001507743767400204570ustar00rootroot00000000000000name: Go-legacy-CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest - name: Install Go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v4 - name: Test on 386 run: | GOARCH=386 go testbitset-1.24.2/.github/workflows/scorecard.yml000066400000000000000000000056441507743767400212140ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. They are provided # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. name: Scorecard supply-chain security on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: - cron: '40 9 * * 2' push: branches: [ "master" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write # Uncomment the permissions below if installing in a private repository. # contents: read # actions: read steps: - name: "Checkout code" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 with: results_file: results.sarif results_format: sarif # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecard on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. # repo_token: ${{ secrets.SCORECARD_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers # - Allows the repository to include the Scorecard badge. # - See https://github.com/ossf/scorecard-action#publishing-results. # For private repositories: # - `publish_results` will always be set to `false`, regardless # of the value entered here. publish_results: true # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@8e0b1c74b1d5a0077b04d064c76ee714d3da7637 # v2.14.6 with: sarif_file: results.sarif bitset-1.24.2/.github/workflows/test.yml000066400000000000000000000011301507743767400202100ustar00rootroot00000000000000name: Test on: [push, pull_request] permissions: contents: read jobs: test: strategy: matrix: 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] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - name: Install Go uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 #v5.3.0 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.0 - name: Test run: go test ./... bitset-1.24.2/.gitignore000066400000000000000000000004221507743767400151040ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof target bitset-1.24.2/.travis.yml000066400000000000000000000012271507743767400152310ustar00rootroot00000000000000language: go sudo: false branches: except: - release branches: only: - master - travis go: - "1.11.x" - tip matrix: allow_failures: - go: tip before_install: - if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi; - if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi; - go get github.com/mattn/goveralls before_script: - make deps script: - make qa after_failure: - cat ./target/test/report.xml after_success: - if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi; bitset-1.24.2/LICENSE000066400000000000000000000027101507743767400141230ustar00rootroot00000000000000Copyright (c) 2014 Will Fitzgerald. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. bitset-1.24.2/README.md000066400000000000000000000130171507743767400143770ustar00rootroot00000000000000# bitset *Go language library to map between non-negative integers and boolean values* [![Test](https://github.com/bits-and-blooms/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest) [![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset) [![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) This library is part of the [awesome go collection](https://github.com/avelino/awesome-go). It is used in production by several important systems: * [beego](https://github.com/beego/beego) * [CubeFS](https://github.com/cubefs/cubefs) * [Amazon EKS Distro](https://github.com/aws/eks-distro) * [sourcegraph](https://github.com/sourcegraph/sourcegraph-public-snapshot) * [torrent](https://github.com/anacrolix/torrent) ## Description Package bitset implements bitsets, a mapping between non-negative integers and boolean values. It should be more efficient than map[uint] bool. It provides methods for setting, clearing, flipping, and testing individual integers. 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. 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. Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining. ### Example use: ```go package main import ( "fmt" "math/rand" "github.com/bits-and-blooms/bitset" ) func main() { fmt.Printf("Hello from BitSet!\n") var b bitset.BitSet // play some Go Fish for i := 0; i < 100; i++ { card1 := uint(rand.Intn(52)) card2 := uint(rand.Intn(52)) b.Set(card1) if b.Test(card2) { fmt.Println("Go Fish!") } b.Clear(card1) } // Chaining b.Set(10).Set(11) for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) { fmt.Println("The following bit is set:", i) } if b.Intersection(bitset.New(100).Set(10)).Count() == 1 { fmt.Println("Intersection works.") } else { fmt.Println("Intersection doesn't work???") } } ``` If you have Go 1.23 or better, you can iterate over the set bits like so: ```go for i := range b.EachSet() {} ``` Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc ## Serialization You may serialize a bitset safely and portably to a stream of bytes as follows: ```Go const length = 9585 const oneEvery = 97 bs := bitset.New(length) // Add some bits for i := uint(0); i < length; i += oneEvery { bs = bs.Set(i) } var buf bytes.Buffer n, err := bs.WriteTo(&buf) if err != nil { // failure } // Here n == buf.Len() ``` You can later deserialize the result as follows: ```Go // Read back from buf bs = bitset.New() n, err = bs.ReadFrom(&buf) if err != nil { // error } // n is the number of bytes read ``` The `ReadFrom` function attempts to read the data into the existing BitSet instance, to minimize memory allocations. *Performance tip*: When reading and writing to a file or a network connection, you may get better performance by wrapping your streams with `bufio` instances. E.g., ```Go f, err := os.Create("myfile") w := bufio.NewWriter(f) ``` ```Go f, err := os.Open("myfile") r := bufio.NewReader(f) ``` ## Memory Usage 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). The `roaring` library allows you to go back and forth between compressed Roaring bitmaps and the conventional bitset instances: ```Go mybitset := roaringbitmap.ToBitSet() newroaringbitmap := roaring.FromBitSet(mybitset) ``` ### Goroutine safety In general, it's not safe to access the same BitSet using different goroutines--they are unsynchronized for performance. 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. ## Installation ```bash go get github.com/bits-and-blooms/bitset ``` ## Contributing 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/)") ## Running all tests Before committing the code, please check if it passes tests, has adequate coverage, etc. ```bash go test go test -cover ``` ## Stars [![Star History Chart](https://api.star-history.com/svg?repos=bits-and-blooms/bitset&type=Date)](https://www.star-history.com/#bits-and-blooms/bitset&Date) ## Further reading

Mastering Programming: From Testing to Performance in Go

bitset-1.24.2/SECURITY.md000066400000000000000000000002151507743767400147050ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability You can report privately a vulnerability by email at daniel@lemire.me (current maintainer). bitset-1.24.2/azure-pipelines.yml000066400000000000000000000021001507743767400167460ustar00rootroot00000000000000# Go # Build your Go project. # Add steps that test, save build artifacts, deploy, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/go trigger: - master pool: vmImage: 'Ubuntu-16.04' variables: GOBIN: '$(GOPATH)/bin' # Go binaries path GOROOT: '/usr/local/go1.11' # Go installation path GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code steps: - script: | mkdir -p '$(GOBIN)' mkdir -p '$(GOPATH)/pkg' mkdir -p '$(modulePath)' shopt -s extglob shopt -s dotglob mv !(gopath) '$(modulePath)' echo '##vso[task.prependpath]$(GOBIN)' echo '##vso[task.prependpath]$(GOROOT)/bin' displayName: 'Set up the Go workspace' - script: | go version go get -v -t -d ./... if [ -f Gopkg.toml ]; then curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure fi go build -v . workingDirectory: '$(modulePath)' displayName: 'Get dependencies, then build' bitset-1.24.2/bitset.go000066400000000000000000001355761507743767400147600ustar00rootroot00000000000000/* Package bitset implements bitsets, a mapping between non-negative integers and boolean values. It should be more efficient than map[uint] bool. It provides methods for setting, clearing, flipping, and testing individual integers. 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. 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. On creation, a hint can be given for the number of bits that will be used. Many of the methods, including Set,Clear, and Flip, return a BitSet pointer, which allows for chaining. Example use: import "bitset" var b BitSet b.Set(10).Set(11) if b.Test(1000) { b.Clear(1000) } if B.Intersection(bitset.New(100).Set(10)).Count() > 1 { fmt.Println("Intersection works.") } As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets. */ package bitset import ( "bytes" "encoding/base64" "encoding/binary" "encoding/json" "errors" "fmt" "io" "math/bits" "strconv" ) // the wordSize of a bit set const wordSize = 64 // the wordSize of a bit set in bytes const wordBytes = wordSize / 8 // wordMask is wordSize-1, used for bit indexing in a word const wordMask = wordSize - 1 // log2WordSize is lg(wordSize) const log2WordSize = 6 // allBits has every bit set const allBits uint64 = 0xffffffffffffffff // default binary BigEndian var binaryOrder binary.ByteOrder = binary.BigEndian // default json encoding base64.URLEncoding var base64Encoding = base64.URLEncoding // Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding) func Base64StdEncoding() { base64Encoding = base64.StdEncoding } // LittleEndian sets Marshal/Unmarshal Binary as Little Endian (Default: binary.BigEndian) func LittleEndian() { binaryOrder = binary.LittleEndian } // BigEndian sets Marshal/Unmarshal Binary as Big Endian (Default: binary.BigEndian) func BigEndian() { binaryOrder = binary.BigEndian } // BinaryOrder returns the current binary order, see also LittleEndian() // and BigEndian() to change the order. func BinaryOrder() binary.ByteOrder { return binaryOrder } // A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0. type BitSet struct { length uint set []uint64 } // Error is used to distinguish errors (panics) generated in this package. type Error string // safeSet will fixup b.set to be non-nil and return the field value func (b *BitSet) safeSet() []uint64 { if b.set == nil { b.set = make([]uint64, wordsNeeded(0)) } return b.set } // SetBitsetFrom fills the bitset with an array of integers without creating a new BitSet instance func (b *BitSet) SetBitsetFrom(buf []uint64) { b.length = uint(len(buf)) * 64 b.set = buf } // From is a constructor used to create a BitSet from an array of words func From(buf []uint64) *BitSet { return FromWithLength(uint(len(buf))*64, buf) } // FromWithLength constructs from an array of words and length in bits. // This function is for advanced users, most users should prefer // the From function. // As a user of FromWithLength, you are responsible for ensuring // that the length is correct: your slice should have length at // least (length+63)/64 in 64-bit words. func FromWithLength(length uint, set []uint64) *BitSet { if len(set) < wordsNeeded(length) { panic("BitSet.FromWithLength: slice is too short") } return &BitSet{length, set} } // Bytes returns the bitset as array of 64-bit words, giving direct access to the internal representation. // It is not a copy, so changes to the returned slice will affect the bitset. // It is meant for advanced users. // // Deprecated: Bytes is deprecated. Use [BitSet.Words] instead. func (b *BitSet) Bytes() []uint64 { return b.set } // Words returns the bitset as array of 64-bit words, giving direct access to the internal representation. // It is not a copy, so changes to the returned slice will affect the bitset. // It is meant for advanced users. func (b *BitSet) Words() []uint64 { return b.set } // wordsNeeded calculates the number of words needed for i bits func wordsNeeded(i uint) int { if i > (Cap() - wordMask) { return int(Cap() >> log2WordSize) } return int((i + wordMask) >> log2WordSize) } // wordsNeededUnbound calculates the number of words needed for i bits, possibly exceeding the capacity. // This function is useful if you know that the capacity cannot be exceeded (e.g., you have an existing BitSet). func wordsNeededUnbound(i uint) int { return (int(i) + wordMask) >> log2WordSize } // wordsIndex calculates the index of words in a `uint64` func wordsIndex(i uint) uint { return i & wordMask } // New creates a new BitSet with a hint that length bits will be required. // The memory usage is at least length/8 bytes. // In case of allocation failure, the function will return a BitSet with zero // capacity. func New(length uint) (bset *BitSet) { defer func() { if r := recover(); r != nil { bset = &BitSet{ 0, make([]uint64, 0), } } }() bset = &BitSet{ length, make([]uint64, wordsNeeded(length)), } return bset } // MustNew creates a new BitSet with the given length bits. // It panics if length exceeds the possible capacity or by a lack of memory. func MustNew(length uint) (bset *BitSet) { if length >= Cap() { panic("You are exceeding the capacity") } return &BitSet{ length, make([]uint64, wordsNeeded(length)), // may panic on lack of memory } } // Cap returns the total possible capacity, or number of bits // that can be stored in the BitSet theoretically. Under 32-bit system, // it is 4294967295 and under 64-bit system, it is 18446744073709551615. // Note that this is further limited by the maximum allocation size in Go, // and your available memory, as any Go data structure. func Cap() uint { return ^uint(0) } // Len returns the number of bits in the BitSet. // Note that it differ from Count function. func (b *BitSet) Len() uint { return b.length } // extendSet adds additional words to incorporate new bits if needed func (b *BitSet) extendSet(i uint) { if i >= Cap() { panic("You are exceeding the capacity") } nsize := wordsNeeded(i + 1) if b.set == nil { b.set = make([]uint64, nsize) } else if cap(b.set) >= nsize { b.set = b.set[:nsize] // fast resize } else if len(b.set) < nsize { newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x copy(newset, b.set) b.set = newset } b.length = i + 1 } // Test whether bit i is set. func (b *BitSet) Test(i uint) bool { if i >= b.length { return false } return b.set[i>>log2WordSize]&(1<> log2WordSize) subWordIndex := wordsIndex(i) // The word that the index falls within, shifted so the index is at bit 0 var firstWord, secondWord uint64 if firstWordIndex < len(b.set) { firstWord = b.set[firstWordIndex] >> subWordIndex } // The next word, masked to only include the necessary bits and shifted to cover the // top of the word if (firstWordIndex + 1) < len(b.set) { secondWord = b.set[firstWordIndex+1] << uint64(wordSize-subWordIndex) } return firstWord | secondWord } // Set bit i to 1, the capacity of the bitset is automatically // increased accordingly. // Warning: using a very large value for 'i' // may lead to a memory shortage and a panic: the caller is responsible // for providing sensible parameters in line with their memory capacity. // The memory usage is at least slightly over i/8 bytes. func (b *BitSet) Set(i uint) *BitSet { if i >= b.length { // if we need more bits, make 'em b.extendSet(i) } b.set[i>>log2WordSize] |= 1 << wordsIndex(i) return b } // Clear bit i to 0. This never cause a memory allocation. It is always safe. func (b *BitSet) Clear(i uint) *BitSet { if i >= b.length { return b } b.set[i>>log2WordSize] &^= 1 << wordsIndex(i) return b } // SetTo sets bit i to value. // Warning: using a very large value for 'i' // may lead to a memory shortage and a panic: the caller is responsible // for providing sensible parameters in line with their memory capacity. func (b *BitSet) SetTo(i uint, value bool) *BitSet { if value { return b.Set(i) } return b.Clear(i) } // Flip bit at i. // Warning: using a very large value for 'i' // may lead to a memory shortage and a panic: the caller is responsible // for providing sensible parameters in line with their memory capacity. func (b *BitSet) Flip(i uint) *BitSet { if i >= b.length { return b.Set(i) } b.set[i>>log2WordSize] ^= 1 << wordsIndex(i) return b } // FlipRange bit in [start, end). // Warning: using a very large value for 'end' // may lead to a memory shortage and a panic: the caller is responsible // for providing sensible parameters in line with their memory capacity. func (b *BitSet) FlipRange(start, end uint) *BitSet { if start >= end { return b } if end-1 >= b.length { // if we need more bits, make 'em b.extendSet(end - 1) } startWord := int(start >> log2WordSize) endWord := int(end >> log2WordSize) // b.set[startWord] ^= ^(^uint64(0) << wordsIndex(start)) // e.g: // start = 71, // startWord = 1 // wordsIndex(start) = 71 % 64 = 7 // (^uint64(0) << 7) = 0b111111....11110000000 // // mask = ^(^uint64(0) << 7) = 0b000000....00001111111 // // flips the first 7 bits in b.set[1] and // in the range loop, the b.set[1] gets again flipped // so the two expressions flip results in a flip // in b.set[1] from [7,63] // // handle startWord special, get's reflipped in range loop b.set[startWord] ^= ^(^uint64(0) << wordsIndex(start)) for idx := range b.set[startWord:endWord] { b.set[startWord+idx] = ^b.set[startWord+idx] } // handle endWord special // e.g. // end = 135 // endWord = 2 // // wordsIndex(-7) = 57 // see the golang spec: // "For unsigned integer values, the operations +, -, *, and << are computed // modulo 2n, where n is the bit width of the unsigned integer's type." // // mask = ^uint64(0) >> 57 = 0b00000....0001111111 // // flips in b.set[2] from [0,7] // // is end at word boundary? if idx := wordsIndex(-end); idx != 0 { b.set[endWord] ^= ^uint64(0) >> wordsIndex(idx) } return b } // Shrink shrinks BitSet so that the provided value is the last possible // set value. It clears all bits > the provided index and reduces the size // and length of the set. // // Note that the parameter value is not the new length in bits: it is the // maximal value that can be stored in the bitset after the function call. // The new length in bits is the parameter value + 1. Thus it is not possible // to use this function to set the length to 0, the minimal value of the length // after this function call is 1. // // A new slice is allocated to store the new bits, so you may see an increase in // memory usage until the GC runs. Normally this should not be a problem, but if you // have an extremely large BitSet its important to understand that the old BitSet will // remain in memory until the GC frees it. // If you are memory constrained, this function may cause a panic. func (b *BitSet) Shrink(lastbitindex uint) *BitSet { length := lastbitindex + 1 idx := wordsNeeded(length) if idx > len(b.set) { return b } shrunk := make([]uint64, idx) copy(shrunk, b.set[:idx]) b.set = shrunk b.length = length lastWordUsedBits := length % 64 if lastWordUsedBits != 0 { b.set[idx-1] &= allBits >> uint64(64-wordsIndex(lastWordUsedBits)) } return b } // Compact shrinks BitSet to so that we preserve all set bits, while minimizing // memory usage. Compact calls Shrink. // A new slice is allocated to store the new bits, so you may see an increase in // memory usage until the GC runs. Normally this should not be a problem, but if you // have an extremely large BitSet its important to understand that the old BitSet will // remain in memory until the GC frees it. // If you are memory constrained, this function may cause a panic. func (b *BitSet) Compact() *BitSet { idx := len(b.set) - 1 for ; idx >= 0 && b.set[idx] == 0; idx-- { } newlength := uint((idx + 1) << log2WordSize) if newlength >= b.length { return b // nothing to do } if newlength > 0 { return b.Shrink(newlength - 1) } // We preserve one word return b.Shrink(63) } // InsertAt takes an index which indicates where a bit should be // inserted. Then it shifts all the bits in the set to the left by 1, starting // from the given index position, and sets the index position to 0. // // Depending on the size of your BitSet, and where you are inserting the new entry, // this method could be extremely slow and in some cases might cause the entire BitSet // to be recopied. func (b *BitSet) InsertAt(idx uint) *BitSet { insertAtElement := idx >> log2WordSize // if length of set is a multiple of wordSize we need to allocate more space first if b.isLenExactMultiple() { b.set = append(b.set, uint64(0)) } var i uint for i = uint(len(b.set) - 1); i > insertAtElement; i-- { // all elements above the position where we want to insert can simply by shifted b.set[i] <<= 1 // we take the most significant bit of the previous element and set it as // the least significant bit of the current element b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63 } // generate a mask to extract the data that we need to shift left // within the element where we insert a bit dataMask := uint64(1)< 0x40000 { buffer.WriteString("...") break } buffer.WriteString(strconv.FormatInt(int64(i), 10)) i, e = b.NextSet(i + 1) if e { buffer.WriteString(",") } } buffer.WriteString("}") return buffer.String() } // DeleteAt deletes the bit at the given index position from // within the bitset // All the bits residing on the left of the deleted bit get // shifted right by 1 // The running time of this operation may potentially be // relatively slow, O(length) func (b *BitSet) DeleteAt(i uint) *BitSet { // the index of the slice element where we'll delete a bit deleteAtElement := i >> log2WordSize // generate a mask for the data that needs to be shifted right // within that slice element that gets modified dataMask := ^((uint64(1) << wordsIndex(i)) - 1) // extract the data that we'll shift right from the slice element data := b.set[deleteAtElement] & dataMask // set the masked area to 0 while leaving the rest as it is b.set[deleteAtElement] &= ^dataMask // shift the previously extracted data to the right and then // set it in the previously masked area b.set[deleteAtElement] |= (data >> 1) & dataMask // loop over all the consecutive slice elements to copy each // lowest bit into the highest position of the previous element, // then shift the entire content to the right by 1 for i := int(deleteAtElement) + 1; i < len(b.set); i++ { b.set[i-1] |= (b.set[i] & 1) << 63 b.set[i] >>= 1 } b.length = b.length - 1 return b } // AppendTo appends all set bits to buf and returns the (maybe extended) buf. // In case of allocation failure, the function will panic. // // See also [BitSet.AsSlice] and [BitSet.NextSetMany]. func (b *BitSet) AppendTo(buf []uint) []uint { // In theory, we could overflow uint, but in practice, we will not. for idx, word := range b.set { for word != 0 { // In theory idx<> log2WordSize) if x >= len(b.set) { return 0, false } // process first (partial) word word := b.set[x] >> wordsIndex(i) if word != 0 { return i + uint(bits.TrailingZeros64(word)), true } // process the following full words until next bit is set // x < len(b.set), no out-of-bounds panic in following slice expression x++ for idx, word := range b.set[x:] { if word != 0 { return uint((x+idx)< 0; j, buffer = bitmap.NextSetMany(j,buffer) { // for k := range buffer { // do something with buffer[k] // } // j += 1 // } // // It is possible to retrieve all set bits as follow: // // indices := make([]uint, bitmap.Count()) // bitmap.NextSetMany(0, indices) // // It is also possible to retrieve all set bits with [BitSet.AppendTo] // or [BitSet.AsSlice]. // // However if Count() is large, it might be preferable to // use several calls to NextSetMany for memory reasons. func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) { // In theory, we could overflow uint, but in practice, we will not. capacity := cap(buffer) result := buffer[:capacity] x := int(i >> log2WordSize) if x >= len(b.set) || capacity == 0 { return 0, result[:0] } // process first (partial) word word := b.set[x] >> wordsIndex(i) size := 0 for word != 0 { result[size] = i + uint(bits.TrailingZeros64(word)) size++ if size == capacity { return result[size-1], result[:size] } // clear the rightmost set bit word &= word - 1 } // process the following full words // x < len(b.set), no out-of-bounds panic in following slice expression x++ for idx, word := range b.set[x:] { for word != 0 { result[size] = uint((x+idx)< 0 { return result[size-1], result[:size] } return 0, result[:0] } // NextClear returns the next clear bit from the specified index, // including possibly the current index // along with an error code (true = valid, false = no bit found i.e. all bits are set) func (b *BitSet) NextClear(i uint) (uint, bool) { x := int(i >> log2WordSize) if x >= len(b.set) { return 0, false } // process first (maybe partial) word word := b.set[x] word = word >> wordsIndex(i) wordAll := allBits >> wordsIndex(i) index := i + uint(bits.TrailingZeros64(^word)) if word != wordAll && index < b.length { return index, true } // process the following full words until next bit is cleared // x < len(b.set), no out-of-bounds panic in following slice expression x++ for idx, word := range b.set[x:] { if word != allBits { index = uint((x+idx)*wordSize + bits.TrailingZeros64(^word)) if index < b.length { return index, true } } } return 0, false } // PreviousSet returns the previous set bit from the specified index, // including possibly the current index // along with an error code (true = valid, false = no bit found i.e. all bits are clear) func (b *BitSet) PreviousSet(i uint) (uint, bool) { x := int(i >> log2WordSize) if x >= len(b.set) { return 0, false } word := b.set[x] // Clear the bits above the index word = word & ((1 << (wordsIndex(i) + 1)) - 1) if word != 0 { return uint(x<= 0; x-- { word = b.set[x] if word != 0 { return uint(x<> log2WordSize) if x >= len(b.set) { return 0, false } word := b.set[x] // Flip all bits and find the highest one bit word = ^word // Clear the bits above the index word = word & ((1 << (wordsIndex(i) + 1)) - 1) if word != 0 { return uint(x<= 0; x-- { word = b.set[x] word = ^word if word != 0 { return uint(x< b.wordCount() { l = b.wordCount() } for i := 0; i < l; i++ { result.set[i] = b.set[i] &^ compare.set[i] } return } // DifferenceCardinality computes the cardinality of the difference func (b *BitSet) DifferenceCardinality(compare *BitSet) uint { panicIfNull(b) panicIfNull(compare) l := compare.wordCount() if l > b.wordCount() { l = b.wordCount() } cnt := uint64(0) if l > 0 { cnt += popcntMaskSlice(b.set[:l], compare.set[:l]) } cnt += popcntSlice(b.set[l:]) return uint(cnt) } // InPlaceDifference computes the difference of base set and other set // This is the BitSet equivalent of &^ (and not) func (b *BitSet) InPlaceDifference(compare *BitSet) { panicIfNull(b) panicIfNull(compare) l := compare.wordCount() if l > b.wordCount() { l = b.wordCount() } if l <= 0 { return } // bounds check elimination data, cmpData := b.set, compare.set _ = data[l-1] _ = cmpData[l-1] for i := 0; i < l; i++ { data[i] &^= cmpData[i] } } // Convenience function: return two bitsets ordered by // increasing length. Note: neither can be nil func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) { if a.length <= b.length { ap, bp = a, b } else { ap, bp = b, a } return } // Intersection of base set and other set // This is the BitSet equivalent of & (and) // In case of allocation failure, the function will return an empty BitSet. func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) { panicIfNull(b) panicIfNull(compare) b, compare = sortByLength(b, compare) result = New(b.length) for i, word := range b.set { result.set[i] = word & compare.set[i] } return } // IntersectionCardinality computes the cardinality of the intersection func (b *BitSet) IntersectionCardinality(compare *BitSet) uint { panicIfNull(b) panicIfNull(compare) if b.length == 0 || compare.length == 0 { return 0 } b, compare = sortByLength(b, compare) cnt := popcntAndSlice(b.set, compare.set) return uint(cnt) } // InPlaceIntersection destructively computes the intersection of // base set and the compare set. // This is the BitSet equivalent of & (and) func (b *BitSet) InPlaceIntersection(compare *BitSet) { panicIfNull(b) panicIfNull(compare) l := compare.wordCount() if l > b.wordCount() { l = b.wordCount() } if l > 0 { // bounds check elimination data, cmpData := b.set, compare.set _ = data[l-1] _ = cmpData[l-1] for i := 0; i < l; i++ { data[i] &= cmpData[i] } } if l >= 0 { for i := l; i < len(b.set); i++ { b.set[i] = 0 } } if compare.length > 0 { if compare.length-1 >= b.length { b.extendSet(compare.length - 1) } } } // Union of base set and other set // This is the BitSet equivalent of | (or) func (b *BitSet) Union(compare *BitSet) (result *BitSet) { panicIfNull(b) panicIfNull(compare) b, compare = sortByLength(b, compare) result = compare.Clone() for i, word := range b.set { result.set[i] = word | compare.set[i] } return } // UnionCardinality computes the cardinality of the uniton of the base set // and the compare set. func (b *BitSet) UnionCardinality(compare *BitSet) uint { panicIfNull(b) panicIfNull(compare) b, compare = sortByLength(b, compare) cnt := uint64(0) if len(b.set) > 0 { cnt += popcntOrSlice(b.set, compare.set) } if len(compare.set) > len(b.set) { cnt += popcntSlice(compare.set[len(b.set):]) } return uint(cnt) } // InPlaceUnion creates the destructive union of base set and compare set. // This is the BitSet equivalent of | (or). func (b *BitSet) InPlaceUnion(compare *BitSet) { panicIfNull(b) panicIfNull(compare) l := compare.wordCount() if l > b.wordCount() { l = b.wordCount() } if compare.length > 0 && compare.length-1 >= b.length { b.extendSet(compare.length - 1) } if l > 0 { // bounds check elimination data, cmpData := b.set, compare.set _ = data[l-1] _ = cmpData[l-1] for i := 0; i < l; i++ { data[i] |= cmpData[i] } } if len(compare.set) > l { for i := l; i < len(compare.set); i++ { b.set[i] = compare.set[i] } } } // SymmetricDifference of base set and other set // This is the BitSet equivalent of ^ (xor) func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) { panicIfNull(b) panicIfNull(compare) b, compare = sortByLength(b, compare) // compare is bigger, so clone it result = compare.Clone() for i, word := range b.set { result.set[i] = word ^ compare.set[i] } return } // SymmetricDifferenceCardinality computes the cardinality of the symmetric difference func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint { panicIfNull(b) panicIfNull(compare) b, compare = sortByLength(b, compare) cnt := uint64(0) if len(b.set) > 0 { cnt += popcntXorSlice(b.set, compare.set) } if len(compare.set) > len(b.set) { cnt += popcntSlice(compare.set[len(b.set):]) } return uint(cnt) } // InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set // This is the BitSet equivalent of ^ (xor) func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) { panicIfNull(b) panicIfNull(compare) l := compare.wordCount() if l > b.wordCount() { l = b.wordCount() } if compare.length > 0 && compare.length-1 >= b.length { b.extendSet(compare.length - 1) } if l > 0 { // bounds check elimination data, cmpData := b.set, compare.set _ = data[l-1] _ = cmpData[l-1] for i := 0; i < l; i++ { data[i] ^= cmpData[i] } } if len(compare.set) > l { for i := l; i < len(compare.set); i++ { b.set[i] = compare.set[i] } } } // Is the length an exact multiple of word sizes? func (b *BitSet) isLenExactMultiple() bool { return wordsIndex(b.length) == 0 } // Clean last word by setting unused bits to 0 func (b *BitSet) cleanLastWord() { if !b.isLenExactMultiple() { b.set[len(b.set)-1] &= allBits >> (wordSize - wordsIndex(b.length)) } } // Complement computes the (local) complement of a bitset (up to length bits) // In case of allocation failure, the function will return an empty BitSet. func (b *BitSet) Complement() (result *BitSet) { panicIfNull(b) result = New(b.length) for i, word := range b.set { result.set[i] = ^word } result.cleanLastWord() return } // All returns true if all bits are set, false otherwise. Returns true for // empty sets. func (b *BitSet) All() bool { panicIfNull(b) return b.Count() == b.length } // None returns true if no bit is set, false otherwise. Returns true for // empty sets. func (b *BitSet) None() bool { panicIfNull(b) if b != nil && b.set != nil { for _, word := range b.set { if word > 0 { return false } } } return true } // Any returns true if any bit is set, false otherwise func (b *BitSet) Any() bool { panicIfNull(b) return !b.None() } // IsSuperSet returns true if this is a superset of the other set func (b *BitSet) IsSuperSet(other *BitSet) bool { l := other.wordCount() if b.wordCount() < l { l = b.wordCount() } for i, word := range other.set[:l] { if b.set[i]&word != word { return false } } return popcntSlice(other.set[l:]) == 0 } // IsStrictSuperSet returns true if this is a strict superset of the other set func (b *BitSet) IsStrictSuperSet(other *BitSet) bool { return b.Count() > other.Count() && b.IsSuperSet(other) } // DumpAsBits dumps a bit set as a string of bits. Following the usual convention in Go, // the least significant bits are printed last (index 0 is at the end of the string). // This is useful for debugging and testing. It is not suitable for serialization. func (b *BitSet) DumpAsBits() string { if b.set == nil { return "." } buffer := bytes.NewBufferString("") i := len(b.set) - 1 for ; i >= 0; i-- { fmt.Fprintf(buffer, "%064b.", b.set[i]) } return buffer.String() } // BinaryStorageSize returns the binary storage requirements (see WriteTo) in bytes. func (b *BitSet) BinaryStorageSize() int { return wordBytes + wordBytes*b.wordCount() } func readUint64Array(reader io.Reader, data []uint64) error { length := len(data) bufferSize := 128 buffer := make([]byte, bufferSize*wordBytes) for i := 0; i < length; i += bufferSize { end := i + bufferSize if end > length { end = length buffer = buffer[:wordBytes*(end-i)] } chunk := data[i:end] if _, err := io.ReadFull(reader, buffer); err != nil { return err } for i := range chunk { chunk[i] = uint64(binaryOrder.Uint64(buffer[8*i:])) } } return nil } func writeUint64Array(writer io.Writer, data []uint64) error { bufferSize := 128 buffer := make([]byte, bufferSize*wordBytes) for i := 0; i < len(data); i += bufferSize { end := i + bufferSize if end > len(data) { end = len(data) buffer = buffer[:wordBytes*(end-i)] } chunk := data[i:end] for i, x := range chunk { binaryOrder.PutUint64(buffer[8*i:], x) } _, err := writer.Write(buffer) if err != nil { return err } } return nil } // WriteTo writes a BitSet to a stream. The format is: // 1. uint64 length // 2. []uint64 set // The length is the number of bits in the BitSet. // // The set is a slice of uint64s containing between length and length + 63 bits. // It is interpreted as a big-endian array of uint64s by default (see BinaryOrder()) // meaning that the first 8 bits are stored at byte index 7, the next 8 bits are stored // at byte index 6... the bits 64 to 71 are stored at byte index 8, etc. // If you change the binary order, you need to do so for both reading and writing. // We recommend using the default binary order. // // Upon success, the number of bytes written is returned. // // Performance: if this function is used to write to a disk or network // connection, it might be beneficial to wrap the stream in a bufio.Writer. // E.g., // // f, err := os.Create("myfile") // w := bufio.NewWriter(f) func (b *BitSet) WriteTo(stream io.Writer) (int64, error) { length := uint64(b.length) // Write length err := binary.Write(stream, binaryOrder, &length) if err != nil { // Upon failure, we do not guarantee that we // return the number of bytes written. return int64(0), err } err = writeUint64Array(stream, b.set[:b.wordCount()]) if err != nil { // Upon failure, we do not guarantee that we // return the number of bytes written. return int64(wordBytes), err } return int64(b.BinaryStorageSize()), nil } // ReadFrom reads a BitSet from a stream written using WriteTo // The format is: // 1. uint64 length // 2. []uint64 set // See WriteTo for details. // Upon success, the number of bytes read is returned. // If the current BitSet is not large enough to hold the data, // it is extended. In case of error, the BitSet is either // left unchanged or made empty if the error occurs too late // to preserve the content. // // Performance: if this function is used to read from a disk or network // connection, it might be beneficial to wrap the stream in a bufio.Reader. // E.g., // // f, err := os.Open("myfile") // r := bufio.NewReader(f) func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) { var length uint64 err := binary.Read(stream, binaryOrder, &length) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return 0, err } newlength := uint(length) if uint64(newlength) != length { return 0, errors.New("unmarshalling error: type mismatch") } nWords := wordsNeeded(uint(newlength)) if cap(b.set) >= nWords { b.set = b.set[:nWords] } else { b.set = make([]uint64, nWords) } b.length = newlength err = readUint64Array(stream, b.set) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } // We do not want to leave the BitSet partially filled as // it is error prone. b.set = b.set[:0] b.length = 0 return 0, err } return int64(b.BinaryStorageSize()), nil } // MarshalBinary encodes a BitSet into a binary form and returns the result. // Please see WriteTo for details. func (b *BitSet) MarshalBinary() ([]byte, error) { var buf bytes.Buffer _, err := b.WriteTo(&buf) if err != nil { return []byte{}, err } return buf.Bytes(), err } // UnmarshalBinary decodes the binary form generated by MarshalBinary. // Please see WriteTo for details. func (b *BitSet) UnmarshalBinary(data []byte) error { buf := bytes.NewReader(data) _, err := b.ReadFrom(buf) return err } // MarshalJSON marshals a BitSet as a JSON structure func (b BitSet) MarshalJSON() ([]byte, error) { buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize())) _, err := b.WriteTo(buffer) if err != nil { return nil, err } // URLEncode all bytes return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes())) } // UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON func (b *BitSet) UnmarshalJSON(data []byte) error { // Unmarshal as string var s string err := json.Unmarshal(data, &s) if err != nil { return err } // URLDecode string buf, err := base64Encoding.DecodeString(s) if err != nil { return err } _, err = b.ReadFrom(bytes.NewReader(buf)) return err } // Rank returns the number of set bits up to and including the index // that are set in the bitset. // See https://en.wikipedia.org/wiki/Ranking#Ranking_in_statistics func (b *BitSet) Rank(index uint) (rank uint) { index++ // Rank is up to and including // needed more than once length := len(b.set) // TODO: built-in min requires go1.21 or later // idx := min(int(index>>6), len(b.set)) idx := int(index >> 6) if idx > length { idx = length } // sum up the popcounts until idx ... // TODO: cannot range over idx (...): requires go1.22 or later // for j := range idx { for j := 0; j < idx; j++ { if w := b.set[j]; w != 0 { rank += uint(bits.OnesCount64(w)) } } // ... plus partial word at idx, // make Rank inlineable and faster in the end // don't test index&63 != 0, just add, less branching if idx < length { rank += uint(bits.OnesCount64(b.set[idx] << (64 - index&63))) } return } // Select returns the index of the jth set bit, where j is the argument. // The caller is responsible to ensure that 0 <= j < Count(): when j is // out of range, the function returns the length of the bitset (b.length). // // Note that this function differs in convention from the Rank function which // returns 1 when ranking the smallest value. We follow the conventional // textbook definition of Select and Rank. func (b *BitSet) Select(index uint) uint { leftover := index for idx, word := range b.set { w := uint(bits.OnesCount64(word)) if w > leftover { return uint(idx)*64 + select64(word, leftover) } leftover -= w } return b.length } // top detects the top bit set func (b *BitSet) top() (uint, bool) { for idx := len(b.set) - 1; idx >= 0; idx-- { if word := b.set[idx]; word != 0 { return uint(idx<= b.length { b.length = top + bits + 1 } pad, idx := top%wordSize, top>>log2WordSize shift, pages := bits%wordSize, bits>>log2WordSize if bits%wordSize == 0 { // happy case: just add pages copy(dst[pages:nsize], b.set) } else { if pad+shift >= wordSize { dst[idx+pages+1] = b.set[idx] >> (wordSize - shift) } for i := int(idx); i >= 0; i-- { if i > 0 { dst[i+int(pages)] = (b.set[i] << shift) | (b.set[i-1] >> (wordSize - shift)) } else { dst[i+int(pages)] = b.set[i] << shift } } } // zeroing extra pages for i := 0; i < int(pages); i++ { dst[i] = 0 } b.set = dst } // ShiftRight shifts the bitset like >> operation would do. func (b *BitSet) ShiftRight(bits uint) { panicIfNull(b) if bits == 0 { return } top, ok := b.top() if !ok { return } if bits > top { b.set = make([]uint64, wordsNeeded(b.length)) return } pad, idx := top%wordSize, top>>log2WordSize shift, pages := bits%wordSize, bits>>log2WordSize if bits%wordSize == 0 { // happy case: just clear pages b.set = b.set[pages:] b.length -= pages * wordSize } else { for i := 0; i <= int(idx-pages); i++ { if i < int(idx-pages) { b.set[i] = (b.set[i+int(pages)] >> shift) | (b.set[i+int(pages)+1] << (wordSize - shift)) } else { b.set[i] = b.set[i+int(pages)] >> shift } } if pad < shift { b.set[int(idx-pages)] = 0 } } for i := int(idx-pages) + 1; i <= int(idx); i++ { b.set[i] = 0 } } // OnesBetween returns the number of set bits in the range [from, to). // The range is inclusive of 'from' and exclusive of 'to'. // Returns 0 if from >= to. func (b *BitSet) OnesBetween(from, to uint) uint { panicIfNull(b) if from >= to { return 0 } // Calculate indices and masks for the starting and ending words startWord := from >> log2WordSize // Divide by wordSize endWord := to >> log2WordSize startOffset := from & wordMask // Mod wordSize endOffset := to & wordMask // Case 1: Bits lie within a single word if startWord == endWord { // Create mask for bits between from and to mask := uint64((1<= startOffset count = uint(bits.OnesCount64(b.set[startWord] & startMask)) // 2b: Count all bits in complete words between start and end if endWord > startWord+1 { count += uint(popcntSlice(b.set[startWord+1 : endWord])) } // 2c: Count bits in last word (from start of word to endOffset) if endOffset > 0 { endMask := uint64(1<> log2WordSize bitOffset := outPos & wordMask // Write extracted bits, handling word boundary crossing dst.set[wordIdx] |= extracted << bitOffset if bitOffset+bitsExtracted > wordSize { dst.set[wordIdx+1] = extracted >> (wordSize - bitOffset) } outPos += bitsExtracted } } // Deposit creates a new BitSet and deposits bits according to a mask. // See DepositTo for details. func (b *BitSet) Deposit(mask *BitSet) *BitSet { dst := New(mask.length) b.DepositTo(mask, dst) return dst } // DepositTo spreads bits from a compacted form in the BitSet into positions // specified by mask in dst. This is the inverse operation of Extract. // // For example, if mask has bits set at positions 1,4,5, then DepositTo will // take consecutive bits 0,1,2 from the source BitSet and place them into // positions 1,4,5 in the destination BitSet. func (b *BitSet) DepositTo(mask *BitSet, dst *BitSet) { panicIfNull(b) panicIfNull(mask) panicIfNull(dst) if len(dst.set) == 0 || len(mask.set) == 0 || len(b.set) == 0 { return } inPos := uint(0) length := len(mask.set) if len(dst.set) < length { length = len(dst.set) } // Process each word for i := 0; i < length; i++ { if mask.set[i] == 0 { continue // Skip words with no bits to deposit } // Calculate source word index wordIdx := inPos >> log2WordSize if wordIdx >= uint(len(b.set)) { break // No more source bits available } // Get source bits, handling word boundary crossing sourceBits := b.set[wordIdx] bitOffset := inPos & wordMask if wordIdx+1 < uint(len(b.set)) && bitOffset != 0 { // Combine bits from current and next word sourceBits = (sourceBits >> bitOffset) | (b.set[wordIdx+1] << (wordSize - bitOffset)) } else { sourceBits >>= bitOffset } // Deposit bits according to mask dst.set[i] = (dst.set[i] &^ mask.set[i]) | pdep(sourceBits, mask.set[i]) inPos += uint(bits.OnesCount64(mask.set[i])) } } //go:generate go run cmd/pextgen/main.go -pkg=bitset func pext(w, m uint64) (result uint64) { var outPos uint // Process byte by byte for i := 0; i < 8; i++ { shift := i << 3 // i * 8 using bit shift b := uint8(w >> shift) mask := uint8(m >> shift) extracted := pextLUT[b][mask] bits := popLUT[mask] result |= uint64(extracted) << outPos outPos += uint(bits) } return result } func pdep(w, m uint64) (result uint64) { var inPos uint // Process byte by byte for i := 0; i < 8; i++ { shift := i << 3 // i * 8 using bit shift mask := uint8(m >> shift) bits := popLUT[mask] // Get the bits we'll deposit from the source b := uint8(w >> inPos) // Deposit them according to the mask for this byte deposited := pdepLUT[b][mask] // Add to result result |= uint64(deposited) << shift inPos += uint(bits) } return result } bitset-1.24.2/bitset_benchmark_test.go000066400000000000000000000356511507743767400200220ustar00rootroot00000000000000// Copyright 2014 Will Fitzgerald. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file tests bit sets package bitset import ( "bytes" "fmt" "math/bits" "math/rand" "testing" ) func BenchmarkSet(b *testing.B) { b.StopTimer() r := rand.New(rand.NewSource(0)) sz := 100000 s := New(uint(sz)) b.StartTimer() for i := 0; i < b.N; i++ { s.Set(uint(r.Int31n(int32(sz)))) } } func BenchmarkGetTest(b *testing.B) { b.StopTimer() r := rand.New(rand.NewSource(0)) sz := 100000 s := New(uint(sz)) b.StartTimer() for i := 0; i < b.N; i++ { s.Test(uint(r.Int31n(int32(sz)))) } } func BenchmarkSetExpand(b *testing.B) { b.StopTimer() sz := uint(100000) b.StartTimer() for i := 0; i < b.N; i++ { var s BitSet s.Set(sz) } } // go test -bench=Count func BenchmarkCount(b *testing.B) { b.StopTimer() s := New(100000) for i := 0; i < 100000; i += 100 { s.Set(uint(i)) } b.StartTimer() for i := 0; i < b.N; i++ { s.Count() } } // go test -bench=Iterate func BenchmarkIterate(b *testing.B) { b.StopTimer() s := New(10000) for i := 0; i < 10000; i += 3 { s.Set(uint(i)) } b.StartTimer() for j := 0; j < b.N; j++ { c := uint(0) for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) { c++ } } } // go test -bench=SparseIterate func BenchmarkSparseIterate(b *testing.B) { b.StopTimer() s := New(100000) for i := 0; i < 100000; i += 30 { s.Set(uint(i)) } b.StartTimer() for j := 0; j < b.N; j++ { c := uint(0) for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) { c++ } } } // go test -bench=BitsetOps func BenchmarkBitsetOps(b *testing.B) { // let's not write into s inside the benchmarks s := New(100000) for i := 0; i < 100000; i += 100 { s.Set(uint(i)) } cpy := s.Clone() b.Run("Equal", func(b *testing.B) { for i := 0; i < b.N; i++ { s.Equal(cpy) } }) b.Run("FlipRange", func(b *testing.B) { s = s.Clone() b.ResetTimer() for i := 0; i < b.N; i++ { s.FlipRange(0, 100000) } }) b.Run("NextSet", func(b *testing.B) { s = New(100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.NextSet(0) } }) b.Run("NextClear", func(b *testing.B) { s = New(100000) s.FlipRange(0, 100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.NextClear(0) } }) b.Run("PreviousSet", func(b *testing.B) { s = New(100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.PreviousSet(99999) } }) b.Run("PreviousClear", func(b *testing.B) { s = New(100000) s.FlipRange(0, 100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.PreviousClear(99999) } }) b.Run("DifferenceCardinality", func(b *testing.B) { empty := New(100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.DifferenceCardinality(empty) } }) b.Run("InPlaceDifference", func(b *testing.B) { s = s.Clone() b.ResetTimer() for i := 0; i < b.N; i++ { s.InPlaceDifference(cpy) } }) b.Run("InPlaceUnion", func(b *testing.B) { s = s.Clone() b.ResetTimer() for i := 0; i < b.N; i++ { s.InPlaceUnion(cpy) } }) b.Run("InPlaceIntersection", func(b *testing.B) { s = s.Clone() b.ResetTimer() for i := 0; i < b.N; i++ { s.InPlaceIntersection(cpy) } }) b.Run("InPlaceSymmetricDifference", func(b *testing.B) { s = s.Clone() b.ResetTimer() for i := 0; i < b.N; i++ { s.InPlaceSymmetricDifference(cpy) } }) } // go test -bench=LemireCreate // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ func BenchmarkLemireCreate(b *testing.B) { for i := 0; i < b.N; i++ { bitmap := New(0) // we force dynamic memory allocation for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } } } // go test -bench=LemireCount // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ func BenchmarkLemireCount(b *testing.B) { bitmap := New(100000000) for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { sum += bitmap.Count() } if sum == 0 { // added just to fool ineffassign return } } // go test -bench=LemireIterate // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ func BenchmarkLemireIterate(b *testing.B) { bitmap := New(100000000) for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { sum++ } } if sum == 0 { // added just to fool ineffassign return } } // go test -bench=LemireIterateb // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ func BenchmarkLemireIterateb(b *testing.B) { bitmap := New(100000000) for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { sum += j } } if sum == 0 { // added just to fool ineffassign return } } // go test -bench=BenchmarkLemireIterateManyb // see https://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ func BenchmarkLemireIterateManyb(b *testing.B) { bitmap := New(100000000) for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } buffer := make([]uint, 256) b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { j := uint(0) j, buffer = bitmap.NextSetMany(j, buffer) for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) { for k := range buffer { sum += buffer[k] } j++ } } if sum == 0 { // added just to fool ineffassign return } } func setRnd(bits []uint64, halfings int) { rnd := rand.NewSource(0).(rand.Source64) for i := range bits { bits[i] = 0xFFFFFFFFFFFFFFFF for j := 0; j < halfings; j++ { bits[i] &= rnd.Uint64() } } } // go test -bench=BenchmarkFlorianUekermannIterateMany func BenchmarkFlorianUekermannIterateMany(b *testing.B) { input := make([]uint64, 68) setRnd(input, 4) bitmap := From(input) buffer := make([]uint, 256) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { last, batch := bitmap.NextSetMany(0, buffer) for len(batch) > 0 { for _, idx := range batch { checksum += idx } last, batch = bitmap.NextSetMany(last+1, batch) } } if checksum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannIterateManyReg(b *testing.B) { input := make([]uint64, 68) setRnd(input, 4) bitmap := From(input) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { checksum += j } } if checksum == 0 { // added just to fool ineffassign return } } // function provided by FlorianUekermann func good(set []uint64) (checksum uint) { for wordIdx, word := range set { wordIdx := uint(wordIdx * 64) for word != 0 { bitIdx := uint(bits.TrailingZeros64(word)) word ^= 1 << bitIdx index := wordIdx + bitIdx checksum += index } } return checksum } func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) { input := make([]uint64, 68) setRnd(input, 4) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { checksum += good(input) } if checksum == 0 { // added just to fool ineffassign return } } /////// Mid density // go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany func BenchmarkFlorianUekermannLowDensityIterateMany(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 50000; i++ { input[rnd.Uint64()%1000000] = 1 } bitmap := From(input) buffer := make([]uint, 256) b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { j := uint(0) j, buffer = bitmap.NextSetMany(j, buffer) for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) { for k := range buffer { sum += buffer[k] } j++ } } if sum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannLowDensityIterateManyReg(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 50000; i++ { input[rnd.Uint64()%1000000] = 1 } bitmap := From(input) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { checksum += j } } if checksum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannLowDensityIterateManyComp(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 50000; i++ { input[rnd.Uint64()%1000000] = 1 } b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { checksum += good(input) } if checksum == 0 { // added just to fool ineffassign return } } /////// Mid density // go test -bench=BenchmarkFlorianUekermannMidDensityIterateMany func BenchmarkFlorianUekermannMidDensityIterateMany(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 3000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } bitmap := From(input) buffer := make([]uint, 256) b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { j := uint(0) j, buffer = bitmap.NextSetMany(j, buffer) for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) { for k := range buffer { sum += buffer[k] } j++ } } if sum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannMidDensityIterateManyReg(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 3000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } bitmap := From(input) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { checksum += j } } if checksum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannMidDensityIterateManyComp(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 3000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { checksum += good(input) } if checksum == 0 { // added just to fool ineffassign return } } ////////// High density func BenchmarkFlorianUekermannMidStrongDensityIterateMany(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 20000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } bitmap := From(input) buffer := make([]uint, 256) b.ResetTimer() sum := uint(0) for i := 0; i < b.N; i++ { j := uint(0) j, buffer = bitmap.NextSetMany(j, buffer) for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j, buffer) { for k := range buffer { sum += buffer[k] } j++ } } if sum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannMidStrongDensityIterateManyReg(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 20000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } bitmap := From(input) b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { checksum += j } } if checksum == 0 { // added just to fool ineffassign return } } func BenchmarkFlorianUekermannMidStrongDensityIterateManyComp(b *testing.B) { input := make([]uint64, 1000000) rnd := rand.NewSource(0).(rand.Source64) for i := 0; i < 20000000; i++ { input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64) } b.ResetTimer() checksum := uint(0) for i := 0; i < b.N; i++ { checksum += good(input) } if checksum == 0 { // added just to fool ineffassign return } } func BenchmarkBitsetReadWrite(b *testing.B) { s := New(100000) for i := 0; i < 100000; i += 100 { s.Set(uint(i)) } buffer := bytes.Buffer{} temp := New(100000) b.ResetTimer() for i := 0; i < b.N; i++ { s.WriteTo(&buffer) temp.ReadFrom(&buffer) buffer.Reset() } } func BenchmarkIsSuperSet(b *testing.B) { new := func(len int, density float64) *BitSet { r := rand.New(rand.NewSource(42)) bs := New(uint(len)) for i := 0; i < len; i++ { bs.SetTo(uint(i), r.Float64() < density) } return bs } bench := func(name string, lenS, lenSS int, density float64, overrideS, overrideSS map[int]bool, f func(*BitSet, *BitSet) bool) { s := new(lenS, density) ss := new(lenSS, density) for i, v := range overrideS { s.SetTo(uint(i), v) } for i, v := range overrideSS { ss.SetTo(uint(i), v) } b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { _ = f(ss, s) } }) } f := func(ss, s *BitSet) bool { return ss.IsSuperSet(s) } fStrict := func(ss, s *BitSet) bool { return ss.IsStrictSuperSet(s) } for _, len := range []int{1, 10, 100, 1000, 10000, 100000} { density := 0.5 bench(fmt.Sprintf("equal, len=%d", len), len, len, density, nil, nil, f) bench(fmt.Sprintf("equal, len=%d, strict", len), len, len, density, nil, nil, fStrict) } for _, density := range []float64{0, 0.05, 0.2, 0.8, 0.95, 1} { len := 10000 bench(fmt.Sprintf("equal, density=%.2f", density), len, len, density, nil, nil, f) bench(fmt.Sprintf("equal, density=%.2f, strict", density), len, len, density, nil, nil, fStrict) } for _, diff := range []int{0, 100, 1000, 9999} { len := 10000 density := 0.5 overrideS := map[int]bool{diff: true} overrideSS := map[int]bool{diff: false} bench(fmt.Sprintf("subset, len=%d, diff=%d", len, diff), len, len, density, overrideS, overrideSS, f) bench(fmt.Sprintf("subset, len=%d, diff=%d, strict", len, diff), len, len, density, overrideS, overrideSS, fStrict) } for _, diff := range []int{0, 100, 1000, 9999} { len := 10000 density := 0.5 overrideS := map[int]bool{diff: false} overrideSS := map[int]bool{diff: true} bench(fmt.Sprintf("superset, len=%d, diff=%d", len, diff), len, len, density, overrideS, overrideSS, f) bench(fmt.Sprintf("superset, len=%d, diff=%d, strict", len, diff), len, len, density, overrideS, overrideSS, fStrict) } } // clear the right most bit (C-RMS) // test two different algorithms func BenchmarkClearRMS(b *testing.B) { var word uint64 // cryptic b.Run("cryptic", func(b *testing.B) { word = 0xaaaa_aaaa_aaaa_aaaa for i := 0; i < b.N; i++ { t := word & ((^word) + 1) word = word ^ t } }) // less cryptic b.Run("simple", func(b *testing.B) { word = 0xaaaa_aaaa_aaaa_aaaa for i := 0; i < b.N; i++ { word &= word - 1 } }) } // go test -bench=Rank func BenchmarkRank(b *testing.B) { s := New(100000) for i := 0; i < 100000; i += 100 { s.Set(uint(i)) } for _, u := range []uint{1, 20, 50, 100, 200, 500, 1_000, 10_000, 20_000, 50_000, 100_000, 200_000} { b.Run(fmt.Sprintf("Rank(%d)", u), func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { _ = s.Rank(u) } }) } } bitset-1.24.2/bitset_iter.go000066400000000000000000000006611507743767400157650ustar00rootroot00000000000000//go:build go1.23 // +build go1.23 package bitset import ( "iter" "math/bits" ) func (b *BitSet) EachSet() iter.Seq[uint] { return func(yield func(uint) bool) { for wordIndex, word := range b.set { idx := 0 for trail := bits.TrailingZeros64(word); trail != 64; trail = bits.TrailingZeros64(word >> idx) { if !yield(uint(wordIndex<= len(got) { t.Errorf("Missing expected value %d at position %d", want, i) continue } if got[i] != want { t.Errorf("At position %d: expected %d, got %d", i, want, got[i]) } } // Test 3: Check no extra values if len(got) > len(expected) { t.Errorf("Got extra values: %v", got[len(expected):]) } } func BenchmarkIter(b *testing.B) { b.StopTimer() s := New(10000) for i := 0; i < 10000; i += 3 { s.Set(uint(i)) } b.StartTimer() for j := 0; j < b.N; j++ { c := uint(0) for range s.EachSet() { c++ } } } func BenchmarkNonInter(b *testing.B) { b.StopTimer() s := New(10000) for i := 0; i < 10000; i += 3 { s.Set(uint(i)) } b.StartTimer() for j := 0; j < b.N; j++ { c := uint(0) for i, e := s.NextSet(0); e; i, e = s.NextSet(i + 1) { c++ } } } bitset-1.24.2/bitset_test.go000066400000000000000000002102501507743767400157760ustar00rootroot00000000000000// Copyright 2014 Will Fitzgerald. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file tests bit sets package bitset import ( "bytes" "compress/gzip" "encoding" "encoding/base64" "encoding/binary" "encoding/json" "errors" "fmt" "io" "math" "math/bits" "math/rand" "reflect" "strconv" "testing" "time" ) func TestStringer(t *testing.T) { v := New(0) for i := uint(0); i < 10; i++ { v.Set(i) } if v.String() != "{0,1,2,3,4,5,6,7,8,9}" { t.Error("bad string output") } } func TestStringLong(t *testing.T) { v := New(0) for i := uint(0); i < 262145; i++ { v.Set(i) } str := v.String() if len(str) != 1723903 { t.Error("Unexpected string length: ", len(str)) } } func TestEmptyBitSet(t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("A zero-length bitset should be fine") } }() b := New(0) if b.Len() != 0 { t.Errorf("Empty set should have capacity 0, not %d", b.Len()) } } func TestZeroValueBitSet(t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("A zero-length bitset should be fine") } }() var b BitSet if b.Len() != 0 { t.Errorf("Empty set should have capacity 0, not %d", b.Len()) } } func TestBitSetNew(t *testing.T) { v := New(16) if v.Test(0) { t.Errorf("Unable to make a bit set and read its 0th value.") } } func TestBitSetHuge(t *testing.T) { v := New(uint(math.MaxUint32)) if v.Test(0) { t.Errorf("Unable to make a huge bit set and read its 0th value.") } } func TestLen(t *testing.T) { v := New(1000) if v.Len() != 1000 { t.Errorf("Len should be 1000, but is %d.", v.Len()) } } func TestLenIsNumberOfBitsNotBytes(t *testing.T) { var b BitSet if b.Len() != 0 { t.Errorf("empty bitset should have Len 0, got %v", b.Len()) } b.Set(0) if b.Len() != 1 { t.Errorf("bitset with first bit set should have Len 1, got %v", b.Len()) } b.Set(8) if b.Len() != 9 { t.Errorf("bitset with 0th and 8th bit set should have Len 9, got %v", b.Len()) } b.Set(1) if b.Len() != 9 { t.Errorf("bitset with 0th, 1st and 8th bit set should have Len 9, got %v", b.Len()) } } func ExampleBitSet_Len() { var b BitSet b.Set(8) fmt.Println("len", b.Len()) fmt.Println("count", b.Count()) // Output: // len 9 // count 1 } func TestBitSetIsClear(t *testing.T) { v := New(1000) for i := uint(0); i < 1000; i++ { if v.Test(i) { t.Errorf("Bit %d is set, and it shouldn't be.", i) } } } func TestExtendOnBoundary(t *testing.T) { v := New(32) defer func() { if r := recover(); r != nil { t.Error("Border out of index error should not have caused a panic") } }() v.Set(32) } func TestExceedCap(t *testing.T) { defer func() { if r := recover(); r == nil { t.Error("Set to capacity should have caused a panic") } }() NumHosts := uint(32768) bmp := New(NumHosts) bmp.ClearAll() d := Cap() bmp.Set(d) } func TestExpand(t *testing.T) { v := New(0) defer func() { if r := recover(); r != nil { t.Error("Expansion should not have caused a panic") } }() for i := uint(0); i < 1000; i++ { v.Set(i) } } func TestBitSetAndGet(t *testing.T) { v := New(1000) v.Set(100) if !v.Test(100) { t.Errorf("Bit %d is clear, and it shouldn't be.", 100) } } func TestNextClear(t *testing.T) { v := New(1000) v.Set(0).Set(1) next, found := v.NextClear(0) if !found || next != 2 { t.Errorf("Found next clear bit as %d, it should have been 2", next) } v = New(1000) for i := uint(0); i < 66; i++ { v.Set(i) } next, found = v.NextClear(0) if !found || next != 66 { t.Errorf("Found next clear bit as %d, it should have been 66", next) } v = New(1000) for i := uint(0); i < 64; i++ { v.Set(i) } v.Clear(45) v.Clear(52) next, found = v.NextClear(10) if !found || next != 45 { t.Errorf("Found next clear bit as %d, it should have been 45", next) } v = New(1000) for i := uint(0); i < 128; i++ { v.Set(i) } v.Clear(73) v.Clear(99) next, found = v.NextClear(10) if !found || next != 73 { t.Errorf("Found next clear bit as %d, it should have been 73", next) } next, found = v.NextClear(72) if !found || next != 73 { t.Errorf("Found next clear bit as %d, it should have been 73", next) } next, found = v.NextClear(73) if !found || next != 73 { t.Errorf("Found next clear bit as %d, it should have been 73", next) } next, found = v.NextClear(74) if !found || next != 99 { t.Errorf("Found next clear bit as %d, it should have been 73", next) } v = New(128) next, found = v.NextClear(0) if !found || next != 0 { t.Errorf("Found next clear bit as %d, it should have been 0", next) } for i := uint(0); i < 128; i++ { v.Set(i) } _, found = v.NextClear(0) if found { t.Errorf("There are not clear bits") } b := new(BitSet) c, d := b.NextClear(1) if c != 0 || d { t.Error("Unexpected values") return } v = New(100) for i := uint(0); i != 100; i++ { v.Set(i) } next, found = v.NextClear(0) if found || next != 0 { t.Errorf("Found next clear bit as %d, it should have return (0, false)", next) } } func TestIterate(t *testing.T) { v := New(10000) v.Set(0) v.Set(1) v.Set(2) data := make([]uint, 3) c := 0 for i, e := v.NextSet(0); e; i, e = v.NextSet(i + 1) { data[c] = i c++ } if data[0] != 0 { t.Errorf("bug 0") } if data[1] != 1 { t.Errorf("bug 1") } if data[2] != 2 { t.Errorf("bug 2") } v.Set(10) v.Set(2000) data = make([]uint, 5) c = 0 for i, e := v.NextSet(0); e; i, e = v.NextSet(i + 1) { data[c] = i c++ } if data[0] != 0 { t.Errorf("bug 0") } if data[1] != 1 { t.Errorf("bug 1") } if data[2] != 2 { t.Errorf("bug 2") } if data[3] != 10 { t.Errorf("bug 3") } if data[4] != 2000 { t.Errorf("bug 4") } } func TestNextSet(t *testing.T) { testCases := []struct { name string // set []uint del []uint // startIdx uint wantIdx uint wantOk bool }{ { name: "null", set: []uint{}, startIdx: 0, wantIdx: 0, wantOk: false, }, { name: "zero", set: []uint{0}, startIdx: 0, wantIdx: 0, wantOk: true, }, { name: "1,5", set: []uint{1, 5}, startIdx: 0, wantIdx: 1, wantOk: true, }, { name: "many", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, startIdx: 100, wantIdx: 130, wantOk: true, }, { name: "many-2", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, del: []uint{130, 190, 300, 420}, startIdx: 100, wantIdx: 250, wantOk: true, }, { name: "last", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, startIdx: 511, wantIdx: 511, wantOk: true, }, { name: "last-2", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, del: []uint{511}, startIdx: 511, wantIdx: 0, wantOk: false, }, } for _, tc := range testCases { var b BitSet for _, u := range tc.set { b.Set(u) } for _, u := range tc.del { b.Clear(u) // without compact } idx, ok := b.NextSet(tc.startIdx) if ok != tc.wantOk { t.Errorf("NextSet, %s: got ok: %v, want: %v", tc.name, ok, tc.wantOk) } if idx != tc.wantIdx { t.Errorf("NextSet, %s: got next idx: %d, want: %d", tc.name, idx, tc.wantIdx) } } } func TestNextSetMany(t *testing.T) { testCases := []struct { name string // set []uint del []uint // buf []uint wantData []uint // startIdx uint wantIdx uint }{ { name: "null", set: []uint{}, del: []uint{}, buf: make([]uint, 0, 512), wantData: []uint{}, startIdx: 0, wantIdx: 0, }, { name: "zero", set: []uint{0}, del: []uint{}, buf: make([]uint, 0, 512), wantData: []uint{0}, // bit #0 is set startIdx: 0, wantIdx: 0, }, { name: "1,5", set: []uint{1, 5}, del: []uint{}, buf: make([]uint, 0, 512), wantData: []uint{1, 5}, startIdx: 0, wantIdx: 5, }, { name: "many", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, del: []uint{}, buf: make([]uint, 0, 512), wantData: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, startIdx: 0, wantIdx: 511, }, { name: "start idx", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, del: []uint{}, buf: make([]uint, 0, 512), wantData: []uint{250, 300, 380, 420, 480, 511}, startIdx: 195, wantIdx: 511, }, { name: "zero buffer", set: []uint{1, 2, 3, 4, 511}, del: []uint{}, buf: make([]uint, 0), // buffer wantData: []uint{}, startIdx: 0, wantIdx: 0, }, { name: "buffer too short, first word", set: []uint{1, 2, 3, 4, 5, 6, 7, 8, 9}, del: []uint{}, buf: make([]uint, 0, 5), // buffer wantData: []uint{1, 2, 3, 4, 5}, startIdx: 0, wantIdx: 5, }, { name: "buffer too short", set: []uint{65, 66, 67, 68, 69, 70}, del: []uint{}, buf: make([]uint, 0, 5), // buffer wantData: []uint{65, 66, 67, 68, 69}, startIdx: 0, wantIdx: 69, }, { name: "special, last return", set: []uint{1}, del: []uint{1}, // delete without compact buf: make([]uint, 0, 5), // buffer wantData: []uint{}, startIdx: 0, wantIdx: 0, }, } for _, tc := range testCases { var b BitSet for _, u := range tc.set { b.Set(u) } for _, u := range tc.del { b.Clear(u) // without compact } idx, buf := b.NextSetMany(tc.startIdx, tc.buf) if idx != tc.wantIdx { t.Errorf("NextSetMany, %s: got next idx: %d, want: %d", tc.name, idx, tc.wantIdx) } if !reflect.DeepEqual(buf, tc.wantData) { t.Errorf("NextSetMany, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v", tc.name, buf, tc.wantData) } } } func TestAppendTo(t *testing.T) { testCases := []struct { name string set []uint buf []uint }{ { name: "null", set: nil, buf: nil, }, { name: "one", set: []uint{42}, buf: make([]uint, 0, 5), }, { name: "many", set: []uint{1, 42, 55, 258, 7211, 54666}, buf: make([]uint, 0), }, } for _, tc := range testCases { var b BitSet for _, u := range tc.set { b.Set(u) } tc.buf = b.AppendTo(tc.buf) if !reflect.DeepEqual(tc.buf, tc.set) { t.Errorf("AppendTo, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v", tc.name, tc.buf, tc.set) } } } func TestAsSlice(t *testing.T) { testCases := []struct { name string set []uint buf []uint }{ { name: "null", set: nil, buf: nil, }, { name: "one", set: []uint{42}, buf: make([]uint, 1), }, { name: "many", set: []uint{1, 42, 55, 258, 7211, 54666}, buf: make([]uint, 6), }, } for _, tc := range testCases { var b BitSet for _, u := range tc.set { b.Set(u) } tc.buf = b.AsSlice(tc.buf) if !reflect.DeepEqual(tc.buf, tc.set) { t.Errorf("AsSlice, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v", tc.name, tc.buf, tc.set) } } } func TestPanicAppendTo(t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("AppendTo with empty buf should not have caused a panic") } }() v := New(1000) v.Set(1000) _ = v.AppendTo(nil) } func TestPanicAsSlice(t *testing.T) { defer func() { if r := recover(); r == nil { t.Error("AsSlice with buf too small should have caused a panic") } }() v := New(1000) v.Set(1000) _ = v.AsSlice(nil) } func TestSetTo(t *testing.T) { v := New(1000) v.SetTo(100, true) if !v.Test(100) { t.Errorf("Bit %d is clear, and it shouldn't be.", 100) } v.SetTo(100, false) if v.Test(100) { t.Errorf("Bit %d is set, and it shouldn't be.", 100) } } func TestChain(t *testing.T) { if !New(1000).Set(100).Set(99).Clear(99).Test(100) { t.Errorf("Bit %d is clear, and it shouldn't be.", 100) } } func TestOutOfBoundsLong(t *testing.T) { v := New(64) defer func() { if r := recover(); r != nil { t.Error("Long distance out of index error should not have caused a panic") } }() v.Set(1000) } func TestOutOfBoundsClose(t *testing.T) { v := New(65) defer func() { if r := recover(); r != nil { t.Error("Local out of index error should not have caused a panic") } }() v.Set(66) } func TestCount(t *testing.T) { tot := uint(64*4 + 11) // just some multi unit64 number v := New(tot) checkLast := true for i := uint(0); i < tot; i++ { sz := uint(v.Count()) if sz != i { t.Errorf("Count reported as %d, but it should be %d", sz, i) checkLast = false break } v.Set(i) } if checkLast { sz := uint(v.Count()) if sz != tot { t.Errorf("After all bits set, size reported as %d, but it should be %d", sz, tot) } } } // test setting every 3rd bit, just in case something odd is happening func TestCount2(t *testing.T) { tot := uint(64*4 + 11) // just some multi unit64 number v := New(tot) for i := uint(0); i < tot; i += 3 { sz := uint(v.Count()) if sz != i/3 { t.Errorf("Count reported as %d, but it should be %d", sz, i) break } v.Set(i) } } // nil tests func TestNullTest(t *testing.T) { var v *BitSet defer func() { if r := recover(); r == nil { t.Error("Checking bit of null reference should have caused a panic") } }() v.Test(66) } func TestNullSet(t *testing.T) { var v *BitSet defer func() { if r := recover(); r == nil { t.Error("Setting bit of null reference should have caused a panic") } }() v.Set(66) } func TestNullClear(t *testing.T) { var v *BitSet defer func() { if r := recover(); r == nil { t.Error("Clearning bit of null reference should have caused a panic") } }() v.Clear(66) } func TestNullCount(t *testing.T) { var v *BitSet defer func() { if r := recover(); r != nil { t.Error("Counting null reference should not have caused a panic") } }() cnt := v.Count() if cnt != 0 { t.Errorf("Count reported as %d, but it should be 0", cnt) } } func TestMustNew(t *testing.T) { testCases := []struct { length uint nwords int }{ { length: 0, nwords: 0, }, { length: 1, nwords: 1, }, { length: 64, nwords: 1, }, { length: 65, nwords: 2, }, { length: 512, nwords: 8, }, { length: 513, nwords: 9, }, } for _, tc := range testCases { b := MustNew(tc.length) if len(b.set) != tc.nwords { t.Errorf("length = %d, len(b.set) got: %d, want: %d", tc.length, len(b.set), tc.nwords) } } } func TestPanicMustNew(t *testing.T) { defer func() { if r := recover(); r == nil { t.Error("length too big should have caused a panic") } }() MustNew(Cap()) } func TestPanicDifferenceBNil(t *testing.T) { var b *BitSet compare := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil First should should have caused a panic") } }() b.Difference(compare) } func TestPanicDifferenceCompareNil(t *testing.T) { var compare *BitSet b := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil Second should should have caused a panic") } }() b.Difference(compare) } func TestPanicUnionBNil(t *testing.T) { var b *BitSet compare := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil First should should have caused a panic") } }() b.Union(compare) } func TestPanicUnionCompareNil(t *testing.T) { var compare *BitSet b := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil Second should should have caused a panic") } }() b.Union(compare) } func TestPanicIntersectionBNil(t *testing.T) { var b *BitSet compare := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil First should should have caused a panic") } }() b.Intersection(compare) } func TestPanicIntersectionCompareNil(t *testing.T) { var compare *BitSet b := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil Second should should have caused a panic") } }() b.Intersection(compare) } func TestPanicSymmetricDifferenceBNil(t *testing.T) { var b *BitSet compare := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil First should should have caused a panic") } }() b.SymmetricDifference(compare) } func TestPanicSymmetricDifferenceCompareNil(t *testing.T) { var compare *BitSet b := New(10) defer func() { if r := recover(); r == nil { t.Error("Nil Second should should have caused a panic") } }() b.SymmetricDifference(compare) } func TestPanicComplementBNil(t *testing.T) { var b *BitSet defer func() { if r := recover(); r == nil { t.Error("Nil should should have caused a panic") } }() b.Complement() } func TestPanicAnytBNil(t *testing.T) { var b *BitSet defer func() { if r := recover(); r == nil { t.Error("Nil should should have caused a panic") } }() b.Any() } func TestPanicNonetBNil(t *testing.T) { var b *BitSet defer func() { if r := recover(); r == nil { t.Error("Nil should should have caused a panic") } }() b.None() } func TestPanicAlltBNil(t *testing.T) { var b *BitSet defer func() { if r := recover(); r == nil { t.Error("Nil should should have caused a panic") } }() b.All() } func TestAll(t *testing.T) { v := New(0) if !v.All() { t.Error("Empty sets should return true on All()") } v = New(2) v.SetTo(0, true) v.SetTo(1, true) if !v.All() { t.Error("Non-empty sets with all bits set should return true on All()") } v = New(2) if v.All() { t.Error("Non-empty sets with no bits set should return false on All()") } v = New(2) v.SetTo(0, true) if v.All() { t.Error("Non-empty sets with some bits set should return false on All()") } } func TestShrink(t *testing.T) { bs := New(10) bs.Set(0) bs.Shrink(63) if !bs.Test(0) { t.Error("0 should be set") return } b := New(0) b.Set(0) b.Set(1) b.Set(2) b.Set(3) b.Set(64) b.Compact() if !b.Test(0) { t.Error("0 should be set") return } if !b.Test(1) { t.Error("1 should be set") return } if !b.Test(2) { t.Error("2 should be set") return } if !b.Test(3) { t.Error("3 should be set") return } if !b.Test(64) { t.Error("64 should be set") return } b.Shrink(2) if !b.Test(0) { t.Error("0 should be set") return } if !b.Test(1) { t.Error("1 should be set") return } if !b.Test(2) { t.Error("2 should be set") return } if b.Test(3) { t.Error("3 should not be set") return } if b.Test(64) { t.Error("64 should not be set") return } b.Set(24) b.Shrink(100) if !b.Test(24) { t.Error("24 should be set") return } b.Set(127) b.Set(128) b.Set(129) b.Compact() if !b.Test(127) { t.Error("127 should be set") return } if !b.Test(128) { t.Error("128 should be set") return } if !b.Test(129) { t.Error("129 be set") return } b.Shrink(128) if !b.Test(127) { t.Error("127 should be set") return } if !b.Test(128) { t.Error("128 should be set") return } if b.Test(129) { t.Error("129 should not be set") return } b.Set(129) b.Shrink(129) if !b.Test(129) { t.Error("129 should be set") return } b.Set(1000) b.Set(2000) b.Set(3000) b.Shrink(3000) if len(b.set) != 3000/64+1 { t.Error("Wrong length of BitSet.set") return } if !b.Test(3000) { t.Error("3000 should be set") return } b.Shrink(2000) if len(b.set) != 2000/64+1 { t.Error("Wrong length of BitSet.set") return } if b.Test(3000) { t.Error("3000 should not be set") return } if !b.Test(2000) { t.Error("2000 should be set") return } if !b.Test(1000) { t.Error("1000 should be set") return } if !b.Test(24) { t.Error("24 should be set") return } b = New(110) b.Set(80) b.Shrink(70) for _, word := range b.set { if word != 0 { t.Error("word should be 0", word) } } } func TestInsertAtWithSet(t *testing.T) { b := New(0) b.Set(0) b.Set(1) b.Set(63) b.Set(64) b.Set(65) b.InsertAt(3) if !b.Test(0) { t.Error("0 should be set") return } if !b.Test(1) { t.Error("1 should be set") return } if b.Test(3) { t.Error("3 should not be set") return } if !b.Test(64) { t.Error("64 should be set") return } if !b.Test(65) { t.Error("65 should be set") return } if !b.Test(66) { t.Error("66 should be set") return } } func TestInsertAt(t *testing.T) { type testCase struct { input []string insertIdx uint expected []string } testCases := []testCase{ { input: []string{ "1111111111111111111111111111111111111111111111111111111111111111", }, insertIdx: uint(62), expected: []string{ "1011111111111111111111111111111111111111111111111111111111111111", "0000000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1111111111111111111111111111111111111111111111111111111111111111", }, insertIdx: uint(63), expected: []string{ "0111111111111111111111111111111111111111111111111111111111111111", "0000000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1111111111111111111111111111111111111111111111111111111111111111", }, insertIdx: uint(0), expected: []string{ "1111111111111111111111111111111111111111111111111111111111111110", "0000000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111111111111", }, insertIdx: uint(70), expected: []string{ "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111110111111", "1111111111111111111111111111111111111111111111111111111111111111", "0000000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111111110000", }, insertIdx: uint(70), expected: []string{ "1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111110111111", "1111111111111111111111111111111111111111111111111111111111100001", "0000000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1111111111111111111111111111111111111111111111111111111111110000", }, insertIdx: uint(10), expected: []string{ "1111111111111111111111111111111111111111111111111111101111110000", "0000000000000000000000000000000000000000000000000000000000000001", }, }, } for _, tc := range testCases { var input []uint64 for _, inputElement := range tc.input { parsed, _ := strconv.ParseUint(inputElement, 2, 64) input = append(input, parsed) } var expected []uint64 for _, expectedElement := range tc.expected { parsed, _ := strconv.ParseUint(expectedElement, 2, 64) expected = append(expected, parsed) } b := From(input) b.InsertAt(tc.insertIdx) if len(b.set) != len(expected) { t.Error("Length of sets should be equal") return } for i := range b.set { if b.set[i] != expected[i] { t.Error("Unexpected results found in set") return } } } } func TestNone(t *testing.T) { v := New(0) if !v.None() { t.Error("Empty sets should return true on None()") } v = New(2) v.SetTo(0, true) v.SetTo(1, true) if v.None() { t.Error("Non-empty sets with all bits set should return false on None()") } v = New(2) if !v.None() { t.Error("Non-empty sets with no bits set should return true on None()") } v = New(2) v.SetTo(0, true) if v.None() { t.Error("Non-empty sets with some bits set should return false on None()") } v = new(BitSet) if !v.None() { t.Error("Empty sets should return true on None()") } } func TestEqual(t *testing.T) { a := New(100) b := New(99) c := New(100) if a.Equal(b) { t.Error("Sets of different sizes should be not be equal") } if !a.Equal(c) { t.Error("Two empty sets of the same size should be equal") } a.Set(99) c.Set(0) if a.Equal(c) { t.Error("Two sets with differences should not be equal") } c.Set(99) a.Set(0) if !a.Equal(c) { t.Error("Two sets with the same bits set should be equal") } if a.Equal(nil) { t.Error("The sets should be different") } a = New(0) b = New(0) if !a.Equal(b) { t.Error("Two empty set should be equal") } var x *BitSet var y *BitSet z := New(0) if !x.Equal(y) { t.Error("Two nil bitsets should be equal") } if x.Equal(z) { t.Error("Nil receiver bitset should not be equal to non-nil bitset") } } func TestUnion(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1) } for i := uint(100); i < 200; i++ { b.Set(i) } if a.UnionCardinality(b) != 200 { t.Errorf("Union should have 200 bits set, but had %d", a.UnionCardinality(b)) } if a.UnionCardinality(b) != b.UnionCardinality(a) { t.Errorf("Union should be symmetric") } c := a.Union(b) d := b.Union(a) if c.Count() != 200 { t.Errorf("Union should have 200 bits set, but had %d", c.Count()) } if !c.Equal(d) { t.Errorf("Union should be symmetric") } } func TestEmptyUnionCardinality(t *testing.T) { a := New(0) b := New(0) if a.UnionCardinality(b) != 0 { t.Error("UnionCardinality should be zero") } } func TestInPlaceUnion(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1) } for i := uint(100); i < 200; i++ { b.Set(i) } c := a.Clone() c.InPlaceUnion(b) d := b.Clone() d.InPlaceUnion(a) if c.Count() != 200 { t.Errorf("Union should have 200 bits set, but had %d", c.Count()) } if d.Count() != 200 { t.Errorf("Union should have 200 bits set, but had %d", d.Count()) } if !c.Equal(d) { t.Errorf("Union should be symmetric") } } func TestIntersection(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1).Set(i) } for i := uint(100); i < 200; i++ { b.Set(i) } if a.IntersectionCardinality(b) != 50 { t.Errorf("Intersection should have 50 bits set, but had %d", a.IntersectionCardinality(b)) } if a.IntersectionCardinality(b) != b.IntersectionCardinality(a) { t.Errorf("Intersection should be symmetric") } c := a.Intersection(b) d := b.Intersection(a) if c.Count() != 50 { t.Errorf("Intersection should have 50 bits set, but had %d", c.Count()) } if !c.Equal(d) { t.Errorf("Intersection should be symmetric") } } func TestEmptyIntersectionCardinality(t *testing.T) { a := New(0) b := New(0) if a.IntersectionCardinality(b) != 0 { t.Error("IntersectionCardinality should be zero") } } func TestInplaceIntersection(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1).Set(i) } for i := uint(100); i < 200; i++ { b.Set(i) } c := a.Clone() c.InPlaceIntersection(b) d := b.Clone() d.InPlaceIntersection(a) if c.Count() != 50 { t.Errorf("Intersection should have 50 bits set, but had %d", c.Count()) } if d.Count() != 50 { t.Errorf("Intersection should have 50 bits set, but had %d", d.Count()) } if !c.Equal(d) { t.Errorf("Intersection should be symmetric") } } func TestDifference(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1) } for i := uint(100); i < 200; i++ { b.Set(i) } if a.DifferenceCardinality(b) != 50 { t.Errorf("a-b Difference should have 50 bits set, but had %d", a.DifferenceCardinality(b)) } if b.DifferenceCardinality(a) != 150 { t.Errorf("b-a Difference should have 150 bits set, but had %d", b.DifferenceCardinality(a)) } c := a.Difference(b) d := b.Difference(a) if c.Count() != 50 { t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count()) } if d.Count() != 150 { t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count()) } if c.Equal(d) { t.Errorf("Difference, here, should not be symmetric") } } func TestEmptyDifferenceCardinality(t *testing.T) { a := New(0) b := New(0) if a.DifferenceCardinality(b) != 0 { t.Error("DifferenceCardinality should be zero") } } func TestInPlaceDifference(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) b.Set(i - 1) } for i := uint(100); i < 200; i++ { b.Set(i) } c := a.Clone() c.InPlaceDifference(b) d := b.Clone() d.InPlaceDifference(a) if c.Count() != 50 { t.Errorf("a-b Difference should have 50 bits set, but had %d", c.Count()) } if d.Count() != 150 { t.Errorf("b-a Difference should have 150 bits set, but had %d", d.Count()) } if c.Equal(d) { t.Errorf("Difference, here, should not be symmetric") } } func TestSymmetricDifference(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) // 01010101010 ... 0000000 b.Set(i - 1).Set(i) // 11111111111111111000000 } for i := uint(100); i < 200; i++ { b.Set(i) } if a.SymmetricDifferenceCardinality(b) != 150 { t.Errorf("a^b Difference should have 150 bits set, but had %d", a.SymmetricDifferenceCardinality(b)) } if b.SymmetricDifferenceCardinality(a) != 150 { t.Errorf("b^a Difference should have 150 bits set, but had %d", b.SymmetricDifferenceCardinality(a)) } c := a.SymmetricDifference(b) d := b.SymmetricDifference(a) if c.Count() != 150 { t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count()) } if d.Count() != 150 { t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count()) } if !c.Equal(d) { t.Errorf("SymmetricDifference should be symmetric") } } func TestEmptySymmetricDifferenceCardinality(t *testing.T) { a := New(0) b := New(0) if a.SymmetricDifferenceCardinality(b) != 0 { t.Error("SymmetricDifferenceCardinality should be zero") } } func TestInPlaceSymmetricDifference(t *testing.T) { a := New(100) b := New(200) for i := uint(1); i < 100; i += 2 { a.Set(i) // 01010101010 ... 0000000 b.Set(i - 1).Set(i) // 11111111111111111000000 } for i := uint(100); i < 200; i++ { b.Set(i) } c := a.Clone() c.InPlaceSymmetricDifference(b) d := b.Clone() d.InPlaceSymmetricDifference(a) if c.Count() != 150 { t.Errorf("a^b Difference should have 150 bits set, but had %d", c.Count()) } if d.Count() != 150 { t.Errorf("b^a Difference should have 150 bits set, but had %d", d.Count()) } if !c.Equal(d) { t.Errorf("SymmetricDifference should be symmetric") } } func TestComplement(t *testing.T) { a := New(50) b := a.Complement() if b.Count() != 50 { t.Errorf("Complement failed, size should be 50, but was %d", b.Count()) } a = New(50) a.Set(10).Set(20).Set(42) b = a.Complement() if b.Count() != 47 { t.Errorf("Complement failed, size should be 47, but was %d", b.Count()) } } func TestIsSuperSet(t *testing.T) { test := func(name string, lenS, lenSS int, overrideS, overrideSS map[int]bool, want, wantStrict bool) { t.Run(name, func(t *testing.T) { s := New(uint(lenS)) ss := New(uint(lenSS)) l := lenS if lenSS < lenS { l = lenSS } r := rand.New(rand.NewSource(42)) for i := 0; i < l; i++ { bit := r.Intn(2) == 1 s.SetTo(uint(i), bit) ss.SetTo(uint(i), bit) } for i, v := range overrideS { s.SetTo(uint(i), v) } for i, v := range overrideSS { ss.SetTo(uint(i), v) } if got := ss.IsSuperSet(s); got != want { t.Errorf("IsSuperSet() = %v, want %v", got, want) } if got := ss.IsStrictSuperSet(s); got != wantStrict { t.Errorf("IsStrictSuperSet() = %v, want %v", got, wantStrict) } }) } test("empty", 0, 0, nil, nil, true, false) test("empty vs non-empty", 0, 100, nil, nil, true, false) test("non-empty vs empty", 100, 0, nil, nil, true, false) test("equal", 100, 100, nil, nil, true, false) test("set is shorter, subset", 100, 200, map[int]bool{50: true}, map[int]bool{50: false}, false, false) test("set is shorter, equal", 100, 200, nil, nil, true, false) test("set is shorter, superset", 100, 200, map[int]bool{50: false}, map[int]bool{50: true}, true, true) test("set is shorter, neither", 100, 200, map[int]bool{50: true}, map[int]bool{50: false, 150: true}, false, false) test("set is longer, subset", 200, 100, map[int]bool{50: true}, map[int]bool{50: false}, false, false) test("set is longer, equal", 200, 100, nil, nil, true, false) test("set is longer, superset", 200, 100, nil, map[int]bool{150: true}, true, true) test("set is longer, neither", 200, 100, map[int]bool{50: false, 150: true}, map[int]bool{50: true}, false, false) } func TestDumpAsBits(t *testing.T) { a := New(10).Set(10) astr := "0000000000000000000000000000000000000000000000000000010000000000." if a.DumpAsBits() != astr { t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", astr, a.DumpAsBits()) } var b BitSet // zero value (b.set == nil) bstr := "." if b.DumpAsBits() != bstr { t.Errorf("DumpAsBits failed, output should be \"%s\" but was \"%s\"", bstr, b.DumpAsBits()) } } func TestMarshalUnmarshalBinary(t *testing.T) { a := New(1010).Set(10).Set(1001) b := new(BitSet) copyBinary(t, a, b) // BitSets must be equal after marshalling and unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } aSetBit := uint(128) a = New(256).Set(aSetBit) aExpectedMarshaledSize := 8 /* length: uint64 */ + 4*8 /* set : [4]uint64 */ aMarshaled, err := a.MarshalBinary() if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() { t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes") return } shiftAmount := uint(72) // https://github.com/bits-and-blooms/bitset/issues/114 for i := uint(0); i < shiftAmount; i++ { a.DeleteAt(0) } aExpectedMarshaledSize = 8 /* length: uint64 */ + 3*8 /* set : [3]uint64 */ aMarshaled, err = a.MarshalBinary() if err != nil || aExpectedMarshaledSize != len(aMarshaled) || aExpectedMarshaledSize != a.BinaryStorageSize() { t.Error("MarshalBinary failed to produce expected (", aExpectedMarshaledSize, ") number of bytes") return } copyBinary(t, a, b) if b.Len() != 256-shiftAmount || !b.Test(aSetBit-shiftAmount) { t.Error("Shifted bitset is not copied correctly") } } func TestMarshalUnmarshalBinaryByLittleEndian(t *testing.T) { LittleEndian() defer func() { // Revert when done. binaryOrder = binary.BigEndian }() a := New(1010).Set(10).Set(1001) b := new(BitSet) copyBinary(t, a, b) // BitSets must be equal after marshalling and unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } } func copyBinary(t *testing.T, from encoding.BinaryMarshaler, to encoding.BinaryUnmarshaler) { data, err := from.MarshalBinary() if err != nil { t.Error(err.Error()) return } err = to.UnmarshalBinary(data) if err != nil { t.Error(err.Error()) return } } func TestMarshalUnmarshalJSON(t *testing.T) { t.Run("value", func(t *testing.T) { a := BitSet{} a.Set(10).Set(1001) data, err := json.Marshal(a) if err != nil { t.Error(err.Error()) return } b := new(BitSet) err = json.Unmarshal(data, b) if err != nil { t.Error(err.Error()) return } // Bitsets must be equal after marshalling and unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } }) t.Run("pointer", func(t *testing.T) { a := New(1010).Set(10).Set(1001) data, err := json.Marshal(a) if err != nil { t.Error(err.Error()) return } b := new(BitSet) err = json.Unmarshal(data, b) if err != nil { t.Error(err.Error()) return } // Bitsets must be equal after marshalling and unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } }) } func TestMarshalUnmarshalJSONWithTrailingData(t *testing.T) { a := New(1010).Set(10).Set(1001) data, err := json.Marshal(a) if err != nil { t.Error(err.Error()) return } // appending some noise data = data[:len(data)-3] // remove " data = append(data, []byte(`AAAAAAAAAA"`)...) b := new(BitSet) err = json.Unmarshal(data, b) if err != nil { t.Error(err.Error()) return } // Bitsets must be equal after marshalling and unmarshalling // Do not over-reading when unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } } func TestMarshalUnmarshalJSONByStdEncoding(t *testing.T) { Base64StdEncoding() a := New(1010).Set(10).Set(1001) data, err := json.Marshal(a) if err != nil { t.Error(err.Error()) return } b := new(BitSet) err = json.Unmarshal(data, b) if err != nil { t.Error(err.Error()) return } // Bitsets must be equal after marshalling and unmarshalling if !a.Equal(b) { t.Error("Bitsets are not equal:\n\t", a.DumpAsBits(), "\n\t", b.DumpAsBits()) return } } func TestSafeSet(t *testing.T) { b := new(BitSet) c := b.safeSet() outType := fmt.Sprintf("%T", c) expType := "[]uint64" if outType != expType { t.Error("Expecting type: ", expType, ", gotf:", outType) return } if len(c) != 0 { t.Error("The slice should be empty") return } } func TestSetBitsetFrom(t *testing.T) { u := []uint64{2, 3, 5, 7, 11} b := new(BitSet) b.SetBitsetFrom(u) outType := fmt.Sprintf("%T", b) expType := "*bitset.BitSet" if outType != expType { t.Error("Expecting type: ", expType, ", gotf:", outType) return } } func TestIssue116(t *testing.T) { a := []uint64{2, 3, 5, 7, 11} b := []uint64{2, 3, 5, 7, 11, 0, 1} bitset1 := FromWithLength(320, a) bitset2 := FromWithLength(320, b) if !bitset1.Equal(bitset2) || !bitset2.Equal(bitset1) { t.Error("Bitsets should be equal irrespective of the underlying capacity") } } func TestFrom(t *testing.T) { u := []uint64{2, 3, 5, 7, 11} b := From(u) outType := fmt.Sprintf("%T", b) expType := "*bitset.BitSet" if outType != expType { t.Error("Expecting type: ", expType, ", gotf:", outType) return } } func TestWords(t *testing.T) { b := new(BitSet) c := b.Words() outType := fmt.Sprintf("%T", c) expType := "[]uint64" if outType != expType { t.Error("Expecting type: ", expType, ", gotf:", outType) return } if len(c) != 0 { t.Error("The slice should be empty") return } } // Bytes is deprecated func TestBytes(t *testing.T) { b := new(BitSet) c := b.Bytes() outType := fmt.Sprintf("%T", c) expType := "[]uint64" if outType != expType { t.Error("Expecting type: ", expType, ", gotf:", outType) return } if len(c) != 0 { t.Error("The slice should be empty") return } } func TestCap(t *testing.T) { c := Cap() if c <= 0 { t.Error("The uint capacity should be >= 0") return } } func TestWordsNeededLong(t *testing.T) { i := Cap() out := wordsNeeded(i) if out <= 0 { t.Error("Unexpected value: ", out) return } } func TestTestTooLong(t *testing.T) { b := new(BitSet) if b.Test(1) { t.Error("Unexpected value: true") return } } func TestClearTooLong(t *testing.T) { b := new(BitSet) c := b.Clear(1) if b != c { t.Error("Unexpected value") return } } func TestClearAll(t *testing.T) { u := []uint64{2, 3, 5, 7, 11} b := From(u) c := b.ClearAll() if c.length != 320 { t.Error("Unexpected length: ", b.length) return } if c.Test(0) || c.Test(1) || c.Test(2) || c.Test(3) || c.Test(4) || c.Test(5) { t.Error("All bits should be unset") return } } func TestRankSelect(t *testing.T) { u := []uint{2, 3, 5, 7, 11, 700, 1500} b := BitSet{} for _, v := range u { b.Set(v) } if b.Rank(5) != 3 { t.Error("Unexpected rank") return } if b.Rank(6) != 3 { t.Error("Unexpected rank") return } if b.Rank(1500) != 7 { t.Error("Unexpected rank") return } if b.Select(0) != 2 { t.Error("Unexpected select") return } if b.Select(1) != 3 { t.Error("Unexpected select") return } if b.Select(2) != 5 { t.Error("Unexpected select") return } if b.Select(5) != 700 { t.Error("Unexpected select") return } } func TestFlip(t *testing.T) { b := new(BitSet) c := b.Flip(11) if c.length != 12 { t.Error("Unexpected value: ", c.length) return } d := c.Flip(7) if d.length != 12 { t.Error("Unexpected value: ", d.length) return } } func TestFlipRange(t *testing.T) { b := new(BitSet) b.Set(1).Set(3).Set(5).Set(7).Set(9).Set(11).Set(13).Set(15) c := b.FlipRange(4, 25) if c.length != 25 { t.Error("Unexpected value: ", c.length) return } d := c.FlipRange(8, 24) if d.length != 25 { t.Error("Unexpected value: ", d.length) return } // for i := uint(0); i < 256; i++ { for j := uint(0); j <= i; j++ { bits := New(i) bits.FlipRange(0, j) c := bits.Count() if c != j { t.Error("Unexpected value: ", c, " expected: ", j) return } } } } func TestCopy(t *testing.T) { a := New(10) if a.Copy(nil) != 0 { t.Error("No values should be copied") return } a = New(10) b := New(20) if a.Copy(b) != 10 { t.Error("Unexpected value") return } } func TestCopyUnaligned(t *testing.T) { a := New(16) a.FlipRange(0, 16) b := New(1) a.Copy(b) if b.Count() > b.Len() { t.Errorf("targets copied set count (%d) should never be larger than target's length (%d)", b.Count(), b.Len()) } if !b.Test(0) { t.Errorf("first bit should still be set in copy: %+v", b) } // Test a more complex scenario with a mix of bits set in the unaligned space to verify no bits are lost. a = New(32) a.Set(0).Set(3).Set(4).Set(16).Set(17).Set(29).Set(31) b = New(19) a.Copy(b) const expectedCount = 5 if b.Count() != expectedCount { t.Errorf("targets copied set count: %d, want %d", b.Count(), expectedCount) } if !(b.Test(0) && b.Test(3) && b.Test(4) && b.Test(16) && b.Test(17)) { t.Errorf("expected set bits are not set: %+v", b) } } func TestCopyFull(t *testing.T) { a := New(10) b := &BitSet{} a.CopyFull(b) if b.length != a.length || len(b.set) != len(a.set) { t.Error("Expected full length copy") return } for i, v := range a.set { if v != b.set[i] { t.Error("Unexpected value") return } } } func TestNextSetError(t *testing.T) { b := new(BitSet) c, d := b.NextSet(1) if c != 0 || d { t.Error("Unexpected values") return } } func TestDeleteWithBitStrings(t *testing.T) { type testCase struct { input []string deleteIdx uint expected []string } testCases := []testCase{ { input: []string{ "1110000000000000000000000000000000000000000000000000000000000001", }, deleteIdx: uint(63), expected: []string{ "0110000000000000000000000000000000000000000000000000000000000001", }, }, { input: []string{ "1000000000000000000000000000000000000000000000000000000000010101", }, deleteIdx: uint(0), expected: []string{ "0100000000000000000000000000000000000000000000000000000000001010", }, }, { input: []string{ "0000000000000000000000000000000000000000000000000000000000111000", }, deleteIdx: uint(4), expected: []string{ "0000000000000000000000000000000000000000000000000000000000011000", }, }, { input: []string{ "1000000000000000000000000000000000000000000000000000000000000001", "1010000000000000000000000000000000000000000000000000000000000001", }, deleteIdx: uint(63), expected: []string{ "1000000000000000000000000000000000000000000000000000000000000001", "0101000000000000000000000000000000000000000000000000000000000000", }, }, { input: []string{ "1000000000000000000000000000000000000000000000000000000000000000", "1000000000000000000000000000000000000000000000000000000000000001", "1000000000000000000000000000000000000000000000000000000000000001", }, deleteIdx: uint(64), expected: []string{ "1000000000000000000000000000000000000000000000000000000000000000", "1100000000000000000000000000000000000000000000000000000000000000", "0100000000000000000000000000000000000000000000000000000000000000", }, }, { input: []string{ "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", }, deleteIdx: uint(256), expected: []string{ "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000", }, }, } for _, tc := range testCases { var input []uint64 for _, inputElement := range tc.input { parsed, _ := strconv.ParseUint(inputElement, 2, 64) input = append(input, parsed) } var expected []uint64 for _, expectedElement := range tc.expected { parsed, _ := strconv.ParseUint(expectedElement, 2, 64) expected = append(expected, parsed) } b := From(input) b.DeleteAt(tc.deleteIdx) if len(b.set) != len(expected) { t.Errorf("Length of sets expected to be %d, but was %d", len(expected), len(b.set)) return } for i := range b.set { if b.set[i] != expected[i] { t.Errorf("Unexpected output\nExpected: %b\nGot: %b", expected[i], b.set[i]) return } } } } func TestDeleteWithBitSetInstance(t *testing.T) { length := uint(256) bitSet := New(length) // the indexes that get set in the bit set indexesToSet := []uint{0, 1, 126, 127, 128, 129, 170, 171, 200, 201, 202, 203, 255} // the position we delete from the bitset deleteAt := uint(127) // the indexes that we expect to be set after the delete expectedToBeSet := []uint{0, 1, 126, 127, 128, 169, 170, 199, 200, 201, 202, 254} expected := make(map[uint]struct{}) for _, index := range expectedToBeSet { expected[index] = struct{}{} } for _, index := range indexesToSet { bitSet.Set(index) } bitSet.DeleteAt(deleteAt) for i := uint(0); i < length; i++ { if _, ok := expected[i]; ok { if !bitSet.Test(i) { t.Errorf("Expected index %d to be set, but wasn't", i) } } else { if bitSet.Test(i) { t.Errorf("Expected index %d to not be set, but was", i) } } } } func TestWriteTo(t *testing.T) { const length = 9585 const oneEvery = 97 addBuf := []byte(`12345678`) bs := New(length) // Add some bits for i := uint(0); i < length; i += oneEvery { bs = bs.Set(i) } var buf bytes.Buffer n, err := bs.WriteTo(&buf) if err != nil { t.Fatal(err) } wantSz := buf.Len() // Size of the serialized data in bytes. if n != int64(wantSz) { t.Errorf("want write size to be %d, got %d", wantSz, n) } buf.Write(addBuf) // Add additional data on stream. // Generate test input for regression tests: if false { gzout := bytes.NewBuffer(nil) gz, err := gzip.NewWriterLevel(gzout, 9) if err != nil { t.Fatal(err) } gz.Write(buf.Bytes()) gz.Close() t.Log("Encoded:", base64.StdEncoding.EncodeToString(gzout.Bytes())) } // Read back. bs = New(length) n, err = bs.ReadFrom(&buf) if err != nil { t.Fatal(err) } if n != int64(wantSz) { t.Errorf("want read size to be %d, got %d", wantSz, n) } // Check bits for i := uint(0); i < length; i += oneEvery { if !bs.Test(i) { t.Errorf("bit %d was not set", i) } } more, err := io.ReadAll(&buf) if err != nil { t.Fatal(err) } if !bytes.Equal(more, addBuf) { t.Fatalf("extra mismatch. got %v, want %v", more, addBuf) } } type inCompleteRetBufReader struct { returnEvery int64 reader io.Reader offset int64 } func (ir *inCompleteRetBufReader) Read(b []byte) (n int, err error) { if ir.returnEvery > 0 { maxRead := ir.returnEvery - (ir.offset % ir.returnEvery) if len(b) > int(maxRead) { b = b[:maxRead] } } n, err = ir.reader.Read(b) ir.offset += int64(n) return } func TestReadFrom(t *testing.T) { addBuf := []byte(`12345678`) // Bytes after stream tests := []struct { length uint oneEvery uint input string // base64+gzipped wantErr error returnEvery int64 }{ { length: 9585, oneEvery: 97, input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwMSACVgYGBg4sIgLMDAwKGARd2BgYGjAFB41noDx6IAJajw64IAajw4UoMajg4ZR4/EaP5pQh1g+MDQyNjE1M7cABAAA//9W5OoOwAQAAA==", returnEvery: 127, }, { length: 1337, oneEvery: 42, input: "H4sIAAAAAAAC/2IAA1ZLBgYWEIPRAUQKgJkMcCZYisEBzkSSYkSTYqCxAYZGxiamZuYWgAAAAP//D0wyWbgAAAA=", }, { length: 1337, // Truncated input. oneEvery: 42, input: "H4sIAAAAAAAC/2IAA9VCCM3AyMDAwARmAQIAAP//vR3xdRkAAAA=", wantErr: io.ErrUnexpectedEOF, }, { length: 1337, // Empty input. oneEvery: 42, input: "H4sIAAAAAAAC/wEAAP//AAAAAAAAAAA=", wantErr: io.ErrUnexpectedEOF, }, } for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { fatalErr := func(err error) { t.Helper() if err != nil { t.Fatal(err) } } var buf bytes.Buffer b, err := base64.StdEncoding.DecodeString(test.input) fatalErr(err) gz, err := gzip.NewReader(bytes.NewBuffer(b)) fatalErr(err) _, err = io.Copy(&buf, gz) fatalErr(err) fatalErr(gz.Close()) bs := New(test.length) _, err = bs.ReadFrom(&inCompleteRetBufReader{returnEvery: test.returnEvery, reader: &buf}) if err != nil { if errors.Is(err, test.wantErr) { // Correct, nothing more we can test. return } t.Fatalf("did not get expected error %v, got %v", test.wantErr, err) } else { if test.wantErr != nil { t.Fatalf("did not get expected error %v", test.wantErr) } } fatalErr(err) // Test if correct bits are set. for i := uint(0); i < test.length; i++ { want := i%test.oneEvery == 0 got := bs.Test(i) if want != got { t.Errorf("bit %d was %v, should be %v", i, got, want) } } more, err := io.ReadAll(&buf) fatalErr(err) if !bytes.Equal(more, addBuf) { t.Errorf("extra mismatch. got %v, want %v", more, addBuf) } }) } } func TestSetAll(t *testing.T) { test := func(name string, bs *BitSet, want uint) { t.Run(name, func(t *testing.T) { bs.SetAll() if bs.Count() != want { t.Errorf("expected %d bits to be set, got %d", want, bs.Count()) } }) } test("nil", nil, 0) for _, length := range []uint{0, 1, 10, 63, 64, 65, 100, 640} { test(fmt.Sprintf("length %d", length), New(length), length) } } func TestShiftLeft(t *testing.T) { data := []uint{5, 28, 45, 72, 89, 560} test := func(name string, bits uint) { t.Run(name, func(t *testing.T) { b := New(200) for _, i := range data { b.Set(i) } b.ShiftLeft(bits) if int(b.Count()) != len(data) { t.Error("bad bits count") } for _, i := range data { if !b.Test(i + bits) { t.Errorf("bit %v is not set", i+bits) } } }) } test("zero", 0) test("no page change", 19) test("shift to full page", 38) test("full page shift", 64) test("no page split", 80) test("with page split", 114) test("with extension", 242) test("with extra word", 16) } func TestShiftRight(t *testing.T) { data := []uint{5, 28, 45, 72, 89} test := func(name string, bits uint) { t.Run(name, func(t *testing.T) { b := New(200) for _, i := range data { b.Set(i) } b.ShiftRight(bits) count := 0 for _, i := range data { if i >= bits { count++ if !b.Test(i - bits) { t.Errorf("bit %v is not set", i-bits) } } } if int(b.Count()) != count { t.Errorf("bad bits count: expected %d, got %d", count, b.Count()) } }) } test("zero", 0) test("no page change", 3) test("no page split", 20) test("with page split", 40) test("full page shift", 64) test("with extension", 70) test("full shift", 89) test("remove all", 242) } func TestShiftRightFull(t *testing.T) { testCases := []struct{ data []uint shiftDistance uint }{ { []uint{20}, 20, }, { []uint{0, 20, 40, 1260, 1280}, 1, }, { []uint{0, 20, 40, 1260, 1280}, 1281, }, } test := func(data []uint, shiftDistance uint) { b := New(0) for i := range data { b.Set(data[i]) } b.ShiftRight(shiftDistance) for i := range data { shiftedBit := int(data[i])-int(shiftDistance) if shiftedBit >= 0 { if !b.Test(uint(shiftedBit)) { t.Errorf("bit %d should be set after ShiftRight(%d) if bit %d was set prior", data[i]-shiftDistance, shiftDistance, data[i]) } } } } for i := range testCases { test(testCases[i].data, testCases[i].shiftDistance) } } func TestWord(t *testing.T) { data := []uint64{0x0bfd85fc01af96dd, 0x3fe212a7eae11414, 0x7aa412221245dee1, 0x557092c1711306d5} testCases := map[string]struct { index uint expected uint64 }{ "first word": { index: 0, expected: 0x0bfd85fc01af96dd, }, "third word": { index: 128, expected: 0x7aa412221245dee1, }, "off the edge": { index: 256, expected: 0, }, "way off the edge": { index: 63235235, expected: 0, }, "split between two words": { index: 96, expected: 0x1245dee13fe212a7, }, "partly off edge": { index: 254, expected: 1, }, "skip one nibble": { index: 4, expected: 0x40bfd85fc01af96d, }, "slightly offset results": { index: 65, expected: 0x9ff10953f5708a0a, }, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { bitSet := From(data) output := bitSet.GetWord64AtBit(testCase.index) if output != testCase.expected { t.Errorf("Word should have returned %d for input %d, but returned %d", testCase.expected, testCase.index, output) } }) } } func TestPreviousSet(t *testing.T) { v := New(128) v.Set(0) v.Set(2) v.Set(4) v.Set(120) for _, tt := range []struct { index uint want uint wantFound bool }{ {0, 0, true}, {1, 0, true}, {2, 2, true}, {3, 2, true}, {4, 4, true}, {5, 4, true}, {100, 4, true}, {120, 120, true}, {121, 120, true}, {1024, 0, false}, } { t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { got, found := v.PreviousSet(tt.index) if got != tt.want || found != tt.wantFound { t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) } }) } v.ClearAll() for _, tt := range []struct { index uint want uint wantFound bool }{ {0, 0, false}, {120, 0, false}, {1024, 0, false}, } { t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { got, found := v.PreviousSet(tt.index) if got != tt.want || found != tt.wantFound { t.Errorf("PreviousSet(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) } }) } } func TestPreviousClear(t *testing.T) { v := New(128) v.Set(0) v.Set(2) v.Set(4) v.Set(120) for _, tt := range []struct { index uint want uint wantFound bool }{ {0, 0, false}, {1, 1, true}, {2, 1, true}, {3, 3, true}, {4, 3, true}, {5, 5, true}, {100, 100, true}, {120, 119, true}, {121, 121, true}, {1024, 0, false}, } { t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { got, found := v.PreviousClear(tt.index) if got != tt.want || found != tt.wantFound { t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) } }) } v.SetAll() for _, tt := range []struct { index uint want uint wantFound bool }{ {0, 0, false}, {120, 0, false}, {1024, 0, false}, } { t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) { got, found := v.PreviousClear(tt.index) if got != tt.want || found != tt.wantFound { t.Errorf("PreviousClear(%d) = %d, %v, want %d, %v", tt.index, got, found, tt.want, tt.wantFound) } }) } } func TestBitSetOnesBetween(t *testing.T) { testCases := []struct { name string input *BitSet from uint to uint expected uint }{ {"empty range", New(64).Set(0).Set(1), 5, 5, 0}, {"invalid range", New(64).Set(0).Set(1), 5, 3, 0}, {"single word", New(64).Set(1).Set(2).Set(3), 1, 3, 2}, {"single word full", New(64).Set(0).Set(1).Set(2).Set(3), 0, 4, 4}, {"cross word boundary", New(128).Set(63).Set(64).Set(65), 63, 66, 3}, {"multiple words", New(256).Set(0).Set(63).Set(64).Set(127).Set(128), 0, 129, 5}, {"large gap", New(256).Set(0).Set(100).Set(200), 0, 201, 3}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got := tc.input.OnesBetween(tc.from, tc.to) if got != tc.expected { t.Errorf("OnesBetween(%d, %d) = %d, want %d", tc.from, tc.to, got, tc.expected) } }) } // Property-based testing const numTests = 1e5 seed := time.Now().UnixNano() rng := rand.New(rand.NewSource(seed)) t.Logf("Seed: %d", seed) for i := 0; i < numTests; i++ { size := uint(rng.Intn(1024) + 64) bs := New(size) // Set random bits for j := 0; j < int(size/4); j++ { bs.Set(uint(rng.Intn(int(size)))) } // Generate random range from := uint(rng.Intn(int(size))) to := from + uint(rng.Intn(int(size-from))) // Compare with naive implementation got := bs.OnesBetween(from, to) want := uint(0) for j := from; j < to; j++ { if bs.Test(j) { want++ } } if got != want { t.Errorf("Case %d: OnesBetween(%d, %d) = %d, want %d", i, from, to, got, want) } } } func BenchmarkBitSetOnesBetween(b *testing.B) { sizes := []int{64, 256, 1024, 4096, 16384} densities := []float64{0.1, 0.5, 0.9} // Different bit densities to test rng := rand.New(rand.NewSource(42)) for _, size := range sizes { for _, density := range densities { // Create bitset with given density bs := New(uint(size)) for i := 0; i < int(float64(size)*density); i++ { bs.Set(uint(rng.Intn(size))) } // Generate random ranges ranges := make([][2]uint, 1000) for i := range ranges { from := uint(rng.Intn(size)) to := from + uint(rng.Intn(size-int(from))) ranges[i] = [2]uint{from, to} } name := fmt.Sprintf("size=%d/density=%.1f", size, density) b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { r := ranges[i%len(ranges)] _ = bs.OnesBetween(r[0], r[1]) } }) } } } func generatePextTestCases(n int) [][2]uint64 { cases := make([][2]uint64, n) for i := range cases { cases[i][0] = rand.Uint64() cases[i][1] = rand.Uint64() } return cases } func BenchmarkPEXT(b *testing.B) { // Generate test cases testCases := generatePextTestCases(1000) b.ResetTimer() var r uint64 for i := 0; i < b.N; i++ { tc := testCases[i%len(testCases)] r = pext(tc[0], tc[1]) } _ = r // prevent optimization } func BenchmarkPDEP(b *testing.B) { // Generate test cases testCases := generatePextTestCases(1000) b.ResetTimer() var r uint64 for i := 0; i < b.N; i++ { tc := testCases[i%len(testCases)] r = pdep(tc[0], tc[1]) } _ = r // prevent optimization } func TestPext(t *testing.T) { const numTests = 1e6 seed := time.Now().UnixNano() rng := rand.New(rand.NewSource(seed)) t.Logf("Seed: %d", seed) for i := 0; i < numTests; i++ { w := rng.Uint64() m := rng.Uint64() result := pext(w, m) popCount := bits.OnesCount64(m) // Test invariants if popCount > 0 && result >= (uint64(1)< bits.OnesCount64(w&m) { t.Fatalf("Case %d: result has more 1s than masked input: result=%x, input&mask=%x", i, result, w&m) } // Test that extracted bits preserve relative ordering: // For each bit position that's set in the mask (m): // 1. Extract a bit from result (resultCopy&1) // 2. Get corresponding input bit from w (w>>j&1) // 3. XOR them - if different, bits weren't preserved correctly resultCopy := result for j := 0; j < 64; j++ { // Check if mask bit is set at position j if m&(uint64(1)<>j&1 gets bit j from original input // XOR (^) checks if they match if (resultCopy&1)^(w>>j&1) != 0 { t.Fatalf("Case %d: bit ordering violation at position %d", i, j) } // Shift to examine next bit in packed result resultCopy >>= 1 } } } } func TestPdep(t *testing.T) { const numTests = 1e6 seed := time.Now().UnixNano() rng := rand.New(rand.NewSource(seed)) t.Logf("Seed: %d", seed) for i := 0; i < numTests; i++ { w := rng.Uint64() // value to deposit m := rng.Uint64() // mask result := pdep(w, m) popCount := bits.OnesCount64(m) // Test invariants if result&^m != 0 { t.Fatalf("Case %d: result %x has bits set outside of mask %x", i, result, m) } if bits.OnesCount64(result) > bits.OnesCount64(w) { t.Fatalf("Case %d: result has more 1s than input: result=%x, input=%x", i, result, w) } // Verify by using PEXT to extract bits back // The composition of PEXT(PDEP(x,m),m) should equal x masked to popcount bits extracted := pext(result, m) maskBits := (uint64(1) << popCount) - 1 if (extracted & maskBits) != (w & maskBits) { t.Fatalf("Case %d: PEXT(PDEP(w,m),m) != w: got=%x, want=%x (w=%x, m=%x)", i, extracted&maskBits, w&maskBits, w, m) } } } func TestBitSetExtract(t *testing.T) { // Property-based tests const numTests = 1e4 seed := time.Now().UnixNano() rng := rand.New(rand.NewSource(seed)) t.Logf("Seed: %d", seed) for i := 0; i < numTests; i++ { // Create random bitsets size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits src := New(size) mask := New(size) dst := New(size) // Set random bits for j := 0; j < int(size/4); j++ { src.Set(uint(rng.Intn(int(size)))) mask.Set(uint(rng.Intn(int(size)))) } // Extract bits src.ExtractTo(mask, dst) // Test invariants if dst.Count() > src.IntersectionCardinality(mask) { t.Errorf("Case %d: result has more 1s than masked input", i) } // Test bits are properly extracted and packed pos := uint(0) for j := uint(0); j < size; j++ { if mask.Test(j) { if src.Test(j) != dst.Test(pos) { t.Errorf("Case %d: bit ordering violation at source position %d", i, j) } pos++ } } } // Keep existing test cases testCases := []struct { name string src *BitSet // source bits mask *BitSet // mask bits expected *BitSet // expected extracted bits }{ { name: "single bit", src: New(8).Set(1), // 0b01 mask: New(8).Set(1), // 0b01 expected: New(8).Set(0), // 0b1 }, { name: "two sequential bits", src: New(8).Set(0).Set(1), // 0b11 mask: New(8).Set(0).Set(1), // 0b11 expected: New(8).Set(0).Set(1), // 0b11 }, { name: "sparse bits", src: New(16).Set(0).Set(10), // 0b10000000001 mask: New(16).Set(0).Set(5).Set(10), // 0b10000100001 expected: New(8).Set(0).Set(2), // 0b101 }, { name: "masked off bits", src: New(8).Set(0).Set(1).Set(2).Set(3), // 0b1111 mask: New(8).Set(0).Set(2), // 0b0101 expected: New(8).Set(0).Set(1), // 0b11 }, { name: "cross word boundary", src: New(128).Set(63).Set(64).Set(65), mask: New(128).Set(63).Set(64).Set(65), expected: New(8).Set(0).Set(1).Set(2), }, { name: "large gap", src: New(256).Set(0).Set(100).Set(200), mask: New(256).Set(0).Set(100).Set(200), expected: New(8).Set(0).Set(1).Set(2), }, { name: "extracting zeros", src: New(8), // 0b00 mask: New(8).Set(0).Set(1).Set(2), // 0b111 expected: New(8), // 0b000 }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { dst := New(tc.expected.Len()) tc.src.ExtractTo(tc.mask, dst) if !dst.Equal(tc.expected) { t.Errorf("got %v, expected %v", dst, tc.expected) } // Verify inverse relationship within the mask bits deposited := New(tc.src.Len()) dst.DepositTo(tc.mask, deposited) // Only bits selected by the mask should match between source and deposited maskedSource := tc.src.Intersection(tc.mask) maskedDeposited := deposited.Intersection(tc.mask) if !maskedSource.Equal(maskedDeposited) { t.Error("DepositTo(ExtractTo(x,m),m) doesn't preserve masked source bits") } }) } } func TestBitSetDeposit(t *testing.T) { // Property-based tests const numTests = 1e4 seed := time.Now().UnixNano() rng := rand.New(rand.NewSource(seed)) t.Logf("Seed: %d", seed) for i := 0; i < numTests; i++ { // Create random bitsets size := uint(rng.Intn(1024) + 64) // Random size between 64-1087 bits src := New(size) mask := New(size) dst := New(size) // Set random bits for j := 0; j < int(size/4); j++ { src.Set(uint(rng.Intn(int(mask.Count() + 1)))) mask.Set(uint(rng.Intn(int(size)))) } // Deposit bits src.DepositTo(mask, dst) // Test invariants if dst.Count() > src.Count() { t.Errorf("Case %d: result has more 1s than input", i) } if (dst.Bytes()[0] &^ mask.Bytes()[0]) != 0 { t.Errorf("Case %d: result has bits set outside of mask", i) } // Extract bits back and verify extracted := New(size) dst.ExtractTo(mask, extracted) maskBits := New(size) for j := uint(0); j < mask.Count(); j++ { maskBits.Set(j) } srcMasked := src.Clone() srcMasked.InPlaceIntersection(maskBits) if !extracted.Equal(srcMasked) { t.Errorf("Case %d: ExtractTo(DepositTo(x,m),m) != x", i) } } // Keep existing test cases testCases := []struct { name string src *BitSet // source bits (packed in low positions) mask *BitSet // mask bits (positions to deposit into) dst *BitSet // destination bits (initially set) expected *BitSet // expected result }{ { name: "sparse bits", src: New(8).Set(0), // 0b01 mask: New(8).Set(0).Set(5), // 0b100001 expected: New(8).Set(0), // 0b000001 }, { name: "masked off bits", src: New(8).Set(0).Set(1), // 0b11 mask: New(8).Set(0).Set(2), // 0b101 expected: New(8).Set(0).Set(2), // 0b101 }, { name: "cross word boundary", src: New(8).Set(0).Set(1), // 0b11 mask: New(128).Set(63).Set(64), // bits across word boundary expected: New(128).Set(63).Set(64), // bits deposited across boundary }, { name: "large gaps", src: New(8).Set(0).Set(1), // 0b11 mask: New(128).Set(0).Set(100), // widely spaced bits expected: New(128).Set(0).Set(100), // deposited into sparse positions }, { name: "depositing zeros", src: New(8), // 0b00 mask: New(8).Set(0).Set(1).Set(2), // 0b111 expected: New(8), // 0b000 }, { name: "preserve unmasked bits", src: New(8), // empty source mask: New(8), // empty mask dst: New(8).Set(1).Set(2).Set(3), // dst has some bits set expected: New(8).Set(1).Set(2).Set(3), // should remain unchanged }, { name: "preserve bits outside mask within word", src: New(8).Set(0), // source has bit 0 set mask: New(8).Set(1), // only depositing into bit 1 dst: New(8).Set(0).Set(2).Set(3), // dst has bits 0,2,3 set expected: New(8).Set(0).Set(1).Set(2).Set(3), // bits 0,2,3 should remain, bit 1 should be set }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var dst *BitSet if tc.dst == nil { dst = New(tc.expected.Len()) } else { dst = tc.dst.Clone() } tc.src.DepositTo(tc.mask, dst) if !dst.Equal(tc.expected) { t.Errorf("got %v, expected %v", dst, tc.expected) } // Verify inverse relationship for set bits up to mask cardinality extracted := New(tc.src.Len()) dst.ExtractTo(tc.mask, extracted) if !extracted.Equal(tc.src) { t.Error("ExtractTo(DepositTo(x,m),m) doesn't preserve source bits that were selected by mask") } }) } } func BenchmarkBitSetExtractDeposit(b *testing.B) { sizes := []int{64, 256, 1024, 4096, 16384, 2 << 15} rng := rand.New(rand.NewSource(42)) // fixed seed for reproducibility for _, size := range sizes { // Create source with random bits src := New(uint(size)) for i := 0; i < size/4; i++ { // Set ~25% of bits src.Set(uint(rng.Intn(size))) } // Create mask with random bits mask := New(uint(size)) for i := 0; i < size/4; i++ { mask.Set(uint(rng.Intn(size))) } b.Run(fmt.Sprintf("size=%d/fn=ExtractTo", size), func(b *testing.B) { dst := New(uint(size)) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { src.ExtractTo(mask, dst) dst.ClearAll() } }) b.Run(fmt.Sprintf("size=%d/fn=DepositTo", size), func(b *testing.B) { dst := New(uint(size)) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { src.DepositTo(mask, dst) dst.ClearAll() } }) } } bitset-1.24.2/cmd/000077500000000000000000000000001507743767400136615ustar00rootroot00000000000000bitset-1.24.2/cmd/pextgen/000077500000000000000000000000001507743767400153335ustar00rootroot00000000000000bitset-1.24.2/cmd/pextgen/main.go000066400000000000000000000057101507743767400166110ustar00rootroot00000000000000package main import ( "bytes" "flag" "fmt" "go/format" "math/bits" "os" ) // pextByte handles single-byte PEXT operation func pextByte(b, m uint8) uint8 { var result, bitPos uint8 for i := uint8(0); i < 8; i++ { if m&(1<= len(s) > 0. func popcntAndSlice(s, m []uint64) (cnt uint64) { // The next line is to help the bounds checker, it matters! _ = m[len(s)-1] // BCE for i := range s { cnt += uint64(bits.OnesCount64(s[i] & m[i])) } return } // popcntOrSlice computes the population count of the OR of two slices. // It assumes that len(m) >= len(s) > 0. func popcntOrSlice(s, m []uint64) (cnt uint64) { // The next line is to help the bounds checker, it matters! _ = m[len(s)-1] // BCE for i := range s { cnt += uint64(bits.OnesCount64(s[i] | m[i])) } return } // popcntXorSlice computes the population count of the XOR of two slices. // It assumes that len(m) >= len(s) > 0. func popcntXorSlice(s, m []uint64) (cnt uint64) { // The next line is to help the bounds checker, it matters! _ = m[len(s)-1] // BCE for i := range s { cnt += uint64(bits.OnesCount64(s[i] ^ m[i])) } return } bitset-1.24.2/popcnt_test.go000066400000000000000000000024401507743767400160070ustar00rootroot00000000000000// This file tests the popcnt functions package bitset import ( "testing" ) func TestPopcntSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} res := popcntSlice(s) const l uint64 = 27 if res != l { t.Errorf("Wrong popcount %d != %d", res, l) } } func TestPopcntMaskSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} res := popcntMaskSlice(s, m) const l uint64 = 9 if res != l { t.Errorf("Wrong mask %d != %d", res, l) } } func TestPopcntAndSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} res := popcntAndSlice(s, m) const l uint64 = 18 if res != l { t.Errorf("Wrong And %d != %d", res, l) } } func TestPopcntOrSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} res := popcntOrSlice(s, m) const l uint64 = 50 if res != l { t.Errorf("Wrong OR %d != %d", res, l) } } func TestPopcntXorSlice(t *testing.T) { s := []uint64{2, 3, 5, 7, 11, 13, 17, 19, 23, 29} m := []uint64{31, 37, 41, 43, 47, 53, 59, 61, 67, 71} res := popcntXorSlice(s, m) const l uint64 = 32 if res != l { t.Errorf("Wrong OR %d != %d", res, l) } } bitset-1.24.2/select.go000066400000000000000000000012401507743767400147210ustar00rootroot00000000000000package bitset import "math/bits" func select64(w uint64, j uint) uint { seen := 0 // Divide 64bit part := w & 0xFFFFFFFF n := uint(bits.OnesCount64(part)) if n <= j { part = w >> 32 seen += 32 j -= n } ww := part // Divide 32bit part = ww & 0xFFFF n = uint(bits.OnesCount64(part)) if n <= j { part = ww >> 16 seen += 16 j -= n } ww = part // Divide 16bit part = ww & 0xFF n = uint(bits.OnesCount64(part)) if n <= j { part = ww >> 8 seen += 8 j -= n } ww = part // Lookup in final byte counter := 0 for ; counter < 8; counter++ { j -= uint((ww >> counter) & 1) if j+1 == 0 { break } } return uint(seen + counter) }