pax_global_header 0000666 0000000 0000000 00000000064 15077437674 0014536 g ustar 00root root 0000000 0000000 52 comment=d02158d114ce8c324e9e91077709e7769e805574
bitset-1.24.2/ 0000775 0000000 0000000 00000000000 15077437674 0013116 5 ustar 00root root 0000000 0000000 bitset-1.24.2/.github/ 0000775 0000000 0000000 00000000000 15077437674 0014456 5 ustar 00root root 0000000 0000000 bitset-1.24.2/.github/FUNDING.yml 0000664 0000000 0000000 00000000444 15077437674 0016275 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000001565 15077437674 0016256 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000000776 15077437674 0017320 0 ustar 00root root 0000000 0000000 # 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/ 0000775 0000000 0000000 00000000000 15077437674 0016513 5 ustar 00root root 0000000 0000000 bitset-1.24.2/.github/workflows/legacy86.go 0000664 0000000 0000000 00000000500 15077437674 0020457 0 ustar 00root root 0000000 0000000 name: 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 test bitset-1.24.2/.github/workflows/scorecard.yml 0000664 0000000 0000000 00000005644 15077437674 0021214 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000001130 15077437674 0020210 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000422 15077437674 0015104 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000001227 15077437674 0015231 0 ustar 00root root 0000000 0000000 language: 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/LICENSE 0000664 0000000 0000000 00000002710 15077437674 0014123 0 ustar 00root root 0000000 0000000 Copyright (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.md 0000664 0000000 0000000 00000013017 15077437674 0014377 0 ustar 00root root 0000000 0000000 # bitset
*Go language library to map between non-negative integers and boolean values*
[](https://github.com/willf/bitset/actions?query=workflow%3ATest)
[](https://goreportcard.com/report/github.com/willf/bitset)
[](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
[](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.md 0000664 0000000 0000000 00000000215 15077437674 0014705 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000002100 15077437674 0016746 0 ustar 00root root 0000000 0000000 # 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.go 0000664 0000000 0000000 00000135576 15077437674 0014760 0 ustar 00root root 0000000 0000000 /*
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.go 0000664 0000000 0000000 00000035651 15077437674 0020022 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000000661 15077437674 0015765 0 ustar 00root root 0000000 0000000 //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<