build(deps): bump github.com/blevesearch/bleve/v2 from 2.4.4 to 2.5.0
Bumps [github.com/blevesearch/bleve/v2](https://github.com/blevesearch/bleve) from 2.4.4 to 2.5.0. - [Release notes](https://github.com/blevesearch/bleve/releases) - [Commits](https://github.com/blevesearch/bleve/compare/v2.4.4...v2.5.0) --- updated-dependencies: - dependency-name: github.com/blevesearch/bleve/v2 dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
-13
@@ -1,13 +0,0 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
// "go1.9", from Go version 1.9 onward
|
||||
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
|
||||
|
||||
package roaring
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func countLeadingZeros(x uint64) int {
|
||||
return bits.LeadingZeros64(x)
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
// "go1.9", from Go version 1.9 onward
|
||||
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
|
||||
|
||||
package roaring
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func countTrailingZeros(x uint64) int {
|
||||
return bits.TrailingZeros64(x)
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
+14
-10
@@ -10,7 +10,7 @@
|
||||
This is a go version of the Roaring bitmap data structure.
|
||||
|
||||
Roaring bitmaps are used by several major systems such as [Apache Lucene][lucene] and derivative systems such as [Solr][solr] and
|
||||
[Elasticsearch][elasticsearch], [Apache Druid (Incubating)][druid], [LinkedIn Pinot][pinot], [Netflix Atlas][atlas], [Apache Spark][spark], [OpenSearchServer][opensearchserver], [anacrolix/torrent][anacrolix/torrent], [Whoosh][whoosh], [Pilosa][pilosa], [Microsoft Visual Studio Team Services (VSTS)][vsts], and eBay's [Apache Kylin][kylin]. The YouTube SQL Engine, [Google Procella](https://research.google/pubs/pub48388/), uses Roaring bitmaps for indexing.
|
||||
[Elasticsearch][elasticsearch], [Apache Druid (Incubating)][druid], [LinkedIn Pinot][pinot], [Netflix Atlas][atlas], [Apache Spark][spark], [OpenSearchServer][opensearchserver], [anacrolix/torrent][anacrolix/torrent], [Whoosh][whoosh], [Redpanda](https://github.com/redpanda-data/redpanda), [Pilosa][pilosa], [Microsoft Visual Studio Team Services (VSTS)][vsts], and eBay's [Apache Kylin][kylin]. The YouTube SQL Engine, [Google Procella](https://research.google/pubs/pub48388/), uses Roaring bitmaps for indexing.
|
||||
|
||||
[lucene]: https://lucene.apache.org/
|
||||
[solr]: https://lucene.apache.org/solr/
|
||||
@@ -163,7 +163,7 @@ they include
|
||||
- github.com/philhofer/fwd
|
||||
- github.com/jtolds/gls
|
||||
|
||||
Note that the smat library requires Go 1.6 or better.
|
||||
Note that the smat library requires Go 1.15 or better.
|
||||
|
||||
#### Installation
|
||||
|
||||
@@ -188,7 +188,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RoaringBitmap/roaring"
|
||||
"github.com/RoaringBitmap/roaring/v2"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
@@ -249,15 +249,20 @@ consider the following sample of code:
|
||||
buf := new(bytes.Buffer)
|
||||
size,err:=rb.WriteTo(buf)
|
||||
if err != nil {
|
||||
t.Errorf("Failed writing")
|
||||
fmt.Println("Failed writing") // return or panic
|
||||
}
|
||||
newrb:= New()
|
||||
size,err=newrb.ReadFrom(buf)
|
||||
if err != nil {
|
||||
t.Errorf("Failed reading")
|
||||
fmt.Println("Failed reading") // return or panic
|
||||
}
|
||||
// if buf is an untrusted source, you should validate the result
|
||||
// (this adds a bit of complexity but it is necessary for security)
|
||||
if newrb.Validate() != nil {
|
||||
fmt.Println("Failed validation") // return or panic
|
||||
}
|
||||
if ! rb.Equals(newrb) {
|
||||
t.Errorf("Cannot retrieve serialized version")
|
||||
fmt.Println("Cannot retrieve serialized version")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -280,7 +285,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/RoaringBitmap/roaring/roaring64"
|
||||
"github.com/RoaringBitmap/roaring/v2/roaring64"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
@@ -356,7 +361,7 @@ https://coveralls.io/github/RoaringBitmap/roaring?branch=master
|
||||
Type
|
||||
|
||||
go test -bench Benchmark -run -
|
||||
|
||||
|
||||
To run benchmarks on [Real Roaring Datasets](https://github.com/RoaringBitmap/real-roaring-datasets)
|
||||
run the following:
|
||||
|
||||
@@ -369,9 +374,8 @@ BENCH_REAL_DATA=1 go test -bench BenchmarkRealData -run -
|
||||
|
||||
You can use roaring with gore:
|
||||
|
||||
- go get -u github.com/motemen/gore
|
||||
- go install github.com/x-motemen/gore/cmd/gore@latest
|
||||
- Make sure that ``$GOPATH/bin`` is in your ``$PATH``.
|
||||
- go get github.com/RoaringBitmap/roaring
|
||||
|
||||
```go
|
||||
$ gore
|
||||
Generated
Vendored
+224
-15
@@ -1,6 +1,7 @@
|
||||
package roaring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -8,6 +9,11 @@ type arrayContainer struct {
|
||||
content []uint16
|
||||
}
|
||||
|
||||
var (
|
||||
ErrArrayIncorrectSort = errors.New("incorrectly sorted array")
|
||||
ErrArrayInvalidSize = errors.New("invalid array size")
|
||||
)
|
||||
|
||||
func (ac *arrayContainer) String() string {
|
||||
s := "{"
|
||||
for it := ac.getShortIterator(); it.hasNext(); {
|
||||
@@ -26,8 +32,7 @@ func (ac *arrayContainer) fillLeastSignificant16bits(x []uint32, i int, mask uin
|
||||
_ = x[len(ac.content)-1+i]
|
||||
_ = ac.content[len(ac.content)-1]
|
||||
for k := 0; k < len(ac.content); k++ {
|
||||
x[k+i] =
|
||||
uint32(ac.content[k]) | mask
|
||||
x[k+i] = uint32(ac.content[k]) | mask
|
||||
}
|
||||
return i + len(ac.content)
|
||||
}
|
||||
@@ -60,10 +65,26 @@ func (ac *arrayContainer) minimum() uint16 {
|
||||
return ac.content[0] // assume not empty
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) safeMinimum() (uint16, error) {
|
||||
if len(ac.content) == 0 {
|
||||
return 0, errors.New("empty array")
|
||||
}
|
||||
|
||||
return ac.minimum(), nil
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) maximum() uint16 {
|
||||
return ac.content[len(ac.content)-1] // assume not empty
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) safeMaximum() (uint16, error) {
|
||||
if len(ac.content) == 0 {
|
||||
return 0, errors.New("empty array")
|
||||
}
|
||||
|
||||
return ac.maximum(), nil
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) getSizeInBytes() int {
|
||||
return ac.getCardinality() * 2
|
||||
}
|
||||
@@ -168,7 +189,7 @@ func (ac *arrayContainer) notClose(firstOfRange, lastOfRange int) container {
|
||||
return ac.toBitmapContainer().not(firstOfRange, lastOfRange+1)
|
||||
}
|
||||
answer := newArrayContainer()
|
||||
answer.content = make([]uint16, newCardinality, newCardinality) //a hack for sure
|
||||
answer.content = make([]uint16, newCardinality, newCardinality) // a hack for sure
|
||||
|
||||
copy(answer.content, ac.content[:startIndex])
|
||||
outPos := startIndex
|
||||
@@ -194,11 +215,9 @@ func (ac *arrayContainer) notClose(firstOfRange, lastOfRange int) container {
|
||||
}
|
||||
answer.content = answer.content[:newCardinality]
|
||||
return answer
|
||||
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) equals(o container) bool {
|
||||
|
||||
srb, ok := o.(*arrayContainer)
|
||||
if ok {
|
||||
// Check if the containers are the same object.
|
||||
@@ -239,8 +258,8 @@ func (ac *arrayContainer) toBitmapContainer() *bitmapContainer {
|
||||
bc := newBitmapContainer()
|
||||
bc.loadData(ac)
|
||||
return bc
|
||||
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) iadd(x uint16) (wasNew bool) {
|
||||
// Special case adding to the end of the container.
|
||||
l := len(ac.content)
|
||||
@@ -352,7 +371,6 @@ func (ac *arrayContainer) ior(a container) container {
|
||||
return ac.iorArray(x)
|
||||
case *bitmapContainer:
|
||||
return a.(*bitmapContainer).orArray(ac)
|
||||
//return ac.iorBitmap(x) // note: this does not make sense
|
||||
case *runContainer16:
|
||||
if x.isFull() {
|
||||
return x.clone()
|
||||
@@ -589,7 +607,6 @@ func (ac *arrayContainer) iandBitmap(bc *bitmapContainer) container {
|
||||
}
|
||||
ac.content = ac.content[:pos]
|
||||
return ac
|
||||
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) xor(a container) container {
|
||||
@@ -630,7 +647,6 @@ func (ac *arrayContainer) xorArray(value2 *arrayContainer) container {
|
||||
length := exclusiveUnion2by2(value1.content, value2.content, answer.content)
|
||||
answer.content = answer.content[:length]
|
||||
return answer
|
||||
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) andNot(a container) container {
|
||||
@@ -822,7 +838,6 @@ func (ac *arrayContainer) inotClose(firstOfRange, lastOfRange int) container {
|
||||
} else { // no expansion needed
|
||||
ac.negateRange(buffer, startIndex, lastIndex, firstOfRange, lastOfRange+1)
|
||||
if cardinalityChange < 0 {
|
||||
|
||||
for i := startIndex + newValuesInRange; i < newCardinality; i++ {
|
||||
ac.content[i] = ac.content[i-cardinalityChange]
|
||||
}
|
||||
@@ -915,7 +930,6 @@ func (ac *arrayContainer) rank(x uint16) int {
|
||||
return answer + 1
|
||||
}
|
||||
return -answer - 1
|
||||
|
||||
}
|
||||
|
||||
func (ac *arrayContainer) selectInt(x uint16) int {
|
||||
@@ -971,6 +985,179 @@ func (ac *arrayContainer) realloc(size int) {
|
||||
}
|
||||
}
|
||||
|
||||
// previousValue returns either the target if found or the previous smaller present value.
|
||||
// If the target is out of bounds a -1 is returned.
|
||||
// Ex: target=4 ac=[2,3,4,6,7] returns 4
|
||||
// Ex: target=5 ac=[2,3,4,6,7] returns 4
|
||||
// Ex: target=6 ac=[2,3,4,6,7] returns 6
|
||||
// Ex: target=8 ac=[2,3,4,6,7] returns 7
|
||||
// Ex: target=1 ac=[2,3,4,6,7] returns -1
|
||||
// Ex: target=0 ac=[2,3,4,6,7] returns -1
|
||||
func (ac *arrayContainer) previousValue(target uint16) int {
|
||||
result := binarySearchUntil(ac.content, target)
|
||||
|
||||
if result.index == len(ac.content) {
|
||||
return int(ac.maximum())
|
||||
}
|
||||
|
||||
if result.outOfBounds() {
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(result.value)
|
||||
}
|
||||
|
||||
// previousAbsentValue returns either the target if not found or the next larger missing value.
|
||||
// If the target is out of bounds a -1 is returned
|
||||
// Ex: target=4 ac=[1,2,3,4,6,7] returns 0
|
||||
// Ex: target=5 ac=[1,2,3,4,6,7] returns 5
|
||||
// Ex: target=6 ac=[1,2,3,4,6,7] returns 5
|
||||
// Ex: target=8 ac=[1,2,3,4,6,7] returns 8
|
||||
func (ac *arrayContainer) previousAbsentValue(target uint16) int {
|
||||
cardinality := len(ac.content)
|
||||
|
||||
if cardinality == 0 {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
if target > ac.maximum() {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
result := binarySearchPast(ac.content, target)
|
||||
|
||||
if result.notFound() {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
// If the target was found at index 1, then the next value down must be result.value-1
|
||||
if result.index == 1 {
|
||||
if ac.minimum() != result.value-1 {
|
||||
return int(result.value - 1)
|
||||
}
|
||||
}
|
||||
|
||||
low := -1
|
||||
high := result.index
|
||||
|
||||
// This uses the pigeon-hole principle.
|
||||
// the if statement compares the difference in indices vs
|
||||
// the difference in values. Suppose mid = 10 and result.index = 5
|
||||
// with ac.content[mid] = 100 and target = 10
|
||||
// then we have 5 slots for values but we need to fit in 90 values
|
||||
// so some of the values must be missing
|
||||
for low+1 < high {
|
||||
midIndex := (high + low) >> 1
|
||||
indexDifference := result.index - midIndex
|
||||
valueDifference := target - ac.content[midIndex]
|
||||
if indexDifference < int(valueDifference) {
|
||||
low = midIndex
|
||||
} else {
|
||||
high = midIndex
|
||||
}
|
||||
}
|
||||
|
||||
if high == 0 {
|
||||
return int(ac.minimum()) - 1
|
||||
}
|
||||
|
||||
return int(ac.content[high] - 1)
|
||||
}
|
||||
|
||||
// nextAbsentValue returns either the target if not found or the next larger missing value.
|
||||
// If the target is out of bounds a -1 is returned
|
||||
// Ex: target=4 ac=[1,2,3,4,6,7] returns 5
|
||||
// Ex: target=5 ac=[1,2,3,4,6,7] returns 5
|
||||
// Ex: target=0 ac=[1,2,3,4,6,7] returns 0
|
||||
// Ex: target=8 ac=[1,2,3,4,6,7] returns 8
|
||||
func (ac *arrayContainer) nextAbsentValue(target uint16) int {
|
||||
cardinality := len(ac.content)
|
||||
|
||||
if cardinality == 0 {
|
||||
return int(target)
|
||||
}
|
||||
if target < ac.minimum() {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
result := binarySearchPast(ac.content, target)
|
||||
|
||||
if result.notFound() {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
if result.index == cardinality-2 {
|
||||
if ac.maximum() != result.value+1 {
|
||||
return int(result.value + 1)
|
||||
}
|
||||
}
|
||||
|
||||
low := result.index
|
||||
high := len(ac.content)
|
||||
|
||||
// This uses the pigeon-hole principle.
|
||||
// the if statement compares the difference in indices vs
|
||||
// the difference in values. Suppose mid = 10 and result.index = 5
|
||||
// with ac.content[mid] = 100 and target = 10
|
||||
// then we have 5 slots for values but we need to fit in 90 values
|
||||
// so some of the values must be missing
|
||||
for low+1 < high {
|
||||
midIndex := (high + low) >> 1
|
||||
indexDifference := midIndex - result.index
|
||||
valueDifference := ac.content[midIndex] - target
|
||||
if indexDifference < int(valueDifference) {
|
||||
high = midIndex
|
||||
} else {
|
||||
low = midIndex
|
||||
}
|
||||
}
|
||||
|
||||
if low == cardinality-1 {
|
||||
return int(ac.content[cardinality-1] + 1)
|
||||
}
|
||||
|
||||
return int(ac.content[low] + 1)
|
||||
}
|
||||
|
||||
// nextValue returns either the target if found or the next larger value.
|
||||
// if the target is out of bounds a -1 is returned
|
||||
//
|
||||
// Ex: target=4 ac=[1,2,3,4,6,7] returns 4
|
||||
// Ex: target=5 ac=[1,2,3,4,6,7] returns 6
|
||||
// Ex: target=6 ac=[1,2,3,4,6,7] returns 6
|
||||
// Ex: target=0 ac=[1,2,3,4,6,7] returns 1
|
||||
// Ex: target=100 ac=[1,2,3,4,6,7] returns -1
|
||||
func (ac *arrayContainer) nextValue(target uint16) int {
|
||||
cardinality := len(ac.content)
|
||||
if cardinality == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
//if target < ac.minimum() {
|
||||
// return -1
|
||||
//}
|
||||
//if target > ac.maximum() {
|
||||
// return -1
|
||||
// }
|
||||
|
||||
result := binarySearchUntil(ac.content, target)
|
||||
if result.exactMatch {
|
||||
return int(result.value)
|
||||
}
|
||||
|
||||
if !result.exactMatch && result.index == -1 {
|
||||
return int(ac.content[0])
|
||||
}
|
||||
if result.outOfBounds() {
|
||||
return -1
|
||||
}
|
||||
|
||||
if result.index < len(ac.content)-1 {
|
||||
return int(ac.content[result.index+1])
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func newArrayContainer() *arrayContainer {
|
||||
p := new(arrayContainer)
|
||||
return p
|
||||
@@ -1039,15 +1226,12 @@ func (ac *arrayContainer) numberOfRuns() (nr int) {
|
||||
|
||||
// convert to run or array *if needed*
|
||||
func (ac *arrayContainer) toEfficientContainer() container {
|
||||
|
||||
numRuns := ac.numberOfRuns()
|
||||
|
||||
sizeAsRunContainer := runContainer16SerializedSizeInBytes(numRuns)
|
||||
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
|
||||
card := ac.getCardinality()
|
||||
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
|
||||
|
||||
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
return newRunContainer16FromArray(ac)
|
||||
}
|
||||
if card <= arrayDefaultMaxSize {
|
||||
@@ -1099,3 +1283,28 @@ func (ac *arrayContainer) addOffset(x uint16) (container, container) {
|
||||
|
||||
return low, high
|
||||
}
|
||||
|
||||
// validate checks cardinality and sort order of the array container
|
||||
func (ac *arrayContainer) validate() error {
|
||||
cardinality := ac.getCardinality()
|
||||
|
||||
if cardinality <= 0 {
|
||||
return ErrArrayInvalidSize
|
||||
}
|
||||
|
||||
if cardinality > arrayDefaultMaxSize {
|
||||
return ErrArrayInvalidSize
|
||||
}
|
||||
|
||||
previous := ac.content[0]
|
||||
for i := 1; i < len(ac.content); i++ {
|
||||
next := ac.content[i]
|
||||
if previous > next {
|
||||
return ErrArrayIncorrectSort
|
||||
}
|
||||
previous = next
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+223
-18
@@ -1,7 +1,9 @@
|
||||
package roaring
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -56,6 +58,17 @@ func (bc *bitmapContainer) minimum() uint16 {
|
||||
return MaxUint16
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) safeMinimum() (uint16, error) {
|
||||
if len(bc.bitmap) == 0 {
|
||||
return 0, errors.New("Empty bitmap")
|
||||
}
|
||||
val := bc.minimum()
|
||||
if val == MaxUint16 {
|
||||
return 0, errors.New("Empty bitmap")
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// i should be non-zero
|
||||
func clz(i uint64) int {
|
||||
n := 1
|
||||
@@ -94,6 +107,17 @@ func (bc *bitmapContainer) maximum() uint16 {
|
||||
return uint16(0)
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) safeMaximum() (uint16, error) {
|
||||
if len(bc.bitmap) == 0 {
|
||||
return 0, errors.New("Empty bitmap")
|
||||
}
|
||||
val := bc.maximum()
|
||||
if val == uint16(0) {
|
||||
return 0, errors.New("Empty bitmap")
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) iterate(cb func(x uint16) bool) bool {
|
||||
iterator := bitmapContainerShortIterator{bc, bc.NextSetBit(0)}
|
||||
|
||||
@@ -116,6 +140,7 @@ func (bcsi *bitmapContainerShortIterator) next() uint16 {
|
||||
bcsi.i = bcsi.ptr.NextSetBit(uint(bcsi.i) + 1)
|
||||
return uint16(j)
|
||||
}
|
||||
|
||||
func (bcsi *bitmapContainerShortIterator) hasNext() bool {
|
||||
return bcsi.i >= 0
|
||||
}
|
||||
@@ -201,6 +226,7 @@ func (bcmi *bitmapContainerManyIterator) nextMany(hs uint32, buf []uint32) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// nextMany64 returns the number of values added to the buffer
|
||||
func (bcmi *bitmapContainerManyIterator) nextMany64(hs uint64, buf []uint64) int {
|
||||
n := 0
|
||||
base := bcmi.base
|
||||
@@ -237,11 +263,10 @@ func (bc *bitmapContainer) getManyIterator() manyIterable {
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) getSizeInBytes() int {
|
||||
return len(bc.bitmap) * 8 // + bcBaseBytes
|
||||
return len(bc.bitmap) * 8
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) serializedSizeInBytes() int {
|
||||
//return bc.Msgsize()// NOO! This breaks GetSerializedSizeInBytes
|
||||
return len(bc.bitmap) * 8
|
||||
}
|
||||
|
||||
@@ -313,6 +338,7 @@ func (bc *bitmapContainer) iaddReturnMinimized(i uint16) container {
|
||||
return bc
|
||||
}
|
||||
|
||||
// iadd adds the arg i, returning true if not already present
|
||||
func (bc *bitmapContainer) iadd(i uint16) bool {
|
||||
x := int(i)
|
||||
previous := bc.bitmap[x/64]
|
||||
@@ -441,7 +467,7 @@ func (bc *bitmapContainer) ior(a container) container {
|
||||
if bc.isFull() {
|
||||
return newRunContainer16Range(0, MaxUint16)
|
||||
}
|
||||
//bc.computeCardinality()
|
||||
// bc.computeCardinality()
|
||||
return bc
|
||||
}
|
||||
panic(fmt.Errorf("unsupported container type %T", a))
|
||||
@@ -516,7 +542,7 @@ func (bc *bitmapContainer) orArray(value2 *arrayContainer) container {
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) orArrayCardinality(value2 *arrayContainer) int {
|
||||
answer := 0
|
||||
answer := bc.getCardinality()
|
||||
c := value2.getCardinality()
|
||||
for k := 0; k < c; k++ {
|
||||
// branchless:
|
||||
@@ -819,9 +845,8 @@ func (bc *bitmapContainer) andBitmap(value2 *bitmapContainer) container {
|
||||
}
|
||||
ac := newArrayContainerSize(newcardinality)
|
||||
fillArrayAND(ac.content, bc.bitmap, value2.bitmap)
|
||||
ac.content = ac.content[:newcardinality] //not sure why i need this
|
||||
ac.content = ac.content[:newcardinality]
|
||||
return ac
|
||||
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) intersectsArray(value2 *arrayContainer) bool {
|
||||
@@ -842,7 +867,6 @@ func (bc *bitmapContainer) intersectsBitmap(value2 *bitmapContainer) bool {
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) iandBitmap(value2 *bitmapContainer) container {
|
||||
@@ -995,7 +1019,7 @@ func (bc *bitmapContainer) iandNotBitmapSurely(value2 *bitmapContainer) containe
|
||||
return bc
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) contains(i uint16) bool { //testbit
|
||||
func (bc *bitmapContainer) contains(i uint16) bool { // testbit
|
||||
x := uint(i)
|
||||
w := bc.bitmap[x>>6]
|
||||
mask := uint64(1) << (x & 63)
|
||||
@@ -1051,7 +1075,7 @@ func (bc *bitmapContainer) toArrayContainer() *arrayContainer {
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) fillArray(container []uint16) {
|
||||
//TODO: rewrite in assembly
|
||||
// TODO: rewrite in assembly
|
||||
pos := 0
|
||||
base := 0
|
||||
for k := 0; k < len(bc.bitmap); k++ {
|
||||
@@ -1066,6 +1090,7 @@ func (bc *bitmapContainer) fillArray(container []uint16) {
|
||||
}
|
||||
}
|
||||
|
||||
// NextSetBit returns the next set bit e.g the next int packed into the bitmaparray
|
||||
func (bc *bitmapContainer) NextSetBit(i uint) int {
|
||||
var (
|
||||
x = i / 64
|
||||
@@ -1088,12 +1113,22 @@ func (bc *bitmapContainer) NextSetBit(i uint) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// PrevSetBit returns the previous set bit e.g the previous int packed into the bitmaparray
|
||||
func (bc *bitmapContainer) PrevSetBit(i int) int {
|
||||
if i < 0 {
|
||||
return -1
|
||||
}
|
||||
x := i / 64
|
||||
if x >= len(bc.bitmap) {
|
||||
|
||||
return bc.uPrevSetBit(uint(i))
|
||||
}
|
||||
|
||||
func (bc *bitmapContainer) uPrevSetBit(i uint) int {
|
||||
var (
|
||||
x = i >> 6
|
||||
length = uint(len(bc.bitmap))
|
||||
)
|
||||
|
||||
if x >= length {
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -1103,12 +1138,16 @@ func (bc *bitmapContainer) PrevSetBit(i int) int {
|
||||
|
||||
w = w << uint(63-b)
|
||||
if w != 0 {
|
||||
return i - countLeadingZeros(w)
|
||||
return int(i) - countLeadingZeros(w)
|
||||
}
|
||||
orig := x
|
||||
x--
|
||||
for ; x >= 0; x-- {
|
||||
if x > orig {
|
||||
return -1
|
||||
}
|
||||
for ; x < orig; x-- {
|
||||
if bc.bitmap[x] != 0 {
|
||||
return (x * 64) + 63 - countLeadingZeros(bc.bitmap[x])
|
||||
return int((x*64)+63) - countLeadingZeros(bc.bitmap[x])
|
||||
}
|
||||
}
|
||||
return -1
|
||||
@@ -1141,7 +1180,6 @@ func (bc *bitmapContainer) numberOfRuns() int {
|
||||
|
||||
// convert to run or array *if needed*
|
||||
func (bc *bitmapContainer) toEfficientContainer() container {
|
||||
|
||||
numRuns := bc.numberOfRuns()
|
||||
|
||||
sizeAsRunContainer := runContainer16SerializedSizeInBytes(numRuns)
|
||||
@@ -1149,7 +1187,7 @@ func (bc *bitmapContainer) toEfficientContainer() container {
|
||||
card := bc.getCardinality()
|
||||
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
|
||||
|
||||
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
return newRunContainer16FromBitmapContainer(bc)
|
||||
}
|
||||
if card <= arrayDefaultMaxSize {
|
||||
@@ -1159,7 +1197,6 @@ func (bc *bitmapContainer) toEfficientContainer() container {
|
||||
}
|
||||
|
||||
func newBitmapContainerFromRun(rc *runContainer16) *bitmapContainer {
|
||||
|
||||
if len(rc.iv) == 1 {
|
||||
return newBitmapContainerwithRange(int(rc.iv[0].start), int(rc.iv[0].last()))
|
||||
}
|
||||
@@ -1169,7 +1206,7 @@ func newBitmapContainerFromRun(rc *runContainer16) *bitmapContainer {
|
||||
setBitmapRange(bc.bitmap, int(rc.iv[i].start), int(rc.iv[i].last())+1)
|
||||
bc.cardinality += int(rc.iv[i].last()) + 1 - int(rc.iv[i].start)
|
||||
}
|
||||
//bc.computeCardinality()
|
||||
// bc.computeCardinality()
|
||||
return bc
|
||||
}
|
||||
|
||||
@@ -1234,3 +1271,171 @@ func (bc *bitmapContainer) addOffset(x uint16) (container, container) {
|
||||
|
||||
return low, high
|
||||
}
|
||||
|
||||
// nextValue returns either the `target` if found or the next largest value.
|
||||
// if the target is out of bounds a -1 is returned
|
||||
//
|
||||
// Example :
|
||||
// Suppose the bitmap container represents the following slice
|
||||
// [1,2,10,11,100]
|
||||
// target=0 returns 1
|
||||
// target=1 returns 1
|
||||
// target=10 returns 10
|
||||
// target=90 returns 100
|
||||
func (bc *bitmapContainer) nextValue(target uint16) int {
|
||||
if bc.cardinality == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return bc.NextSetBit(uint(target))
|
||||
}
|
||||
|
||||
// nextAbsentValue returns the next absent value.
|
||||
// if the target is out of bounds a -1 is returned
|
||||
func (bc *bitmapContainer) nextAbsentValue(target uint16) int {
|
||||
if bc.cardinality == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
var (
|
||||
x = target >> 6
|
||||
length = uint(len(bc.bitmap))
|
||||
)
|
||||
if uint(x) >= length {
|
||||
return -1
|
||||
}
|
||||
w := bc.bitmap[x]
|
||||
w = w >> uint(target%64)
|
||||
if w == 0 {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
// Check if all 1's
|
||||
// if statement - we skip the if we have all ones [1,1,1,1...1]
|
||||
if ^w != 0 {
|
||||
|
||||
if countTrailingZeros(w) > 0 {
|
||||
// we have something like [X,Y,Z, 0,0,0]. This means the target bit is zero
|
||||
return int(target)
|
||||
}
|
||||
|
||||
// other wise something like [X,Y,0,1,1,1..1], where x and y can be either 1 or 0.
|
||||
|
||||
trailing := countTrailingOnes(w)
|
||||
return int(target) + trailing
|
||||
|
||||
}
|
||||
x++
|
||||
for ; uint(x) < length; x++ {
|
||||
if bc.bitmap[x] == 0 {
|
||||
return int(x * 64)
|
||||
}
|
||||
if ^bc.bitmap[x] != 0 {
|
||||
trailing := countTrailingOnes(bc.bitmap[x])
|
||||
return int(x*64) + trailing
|
||||
}
|
||||
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// previousValue returns either the `target` if found or the previous largest value.
|
||||
// if the target is out of bounds a -1 is returned
|
||||
|
||||
// Example :
|
||||
// Suppose the bitmap container represents the following slice
|
||||
// [1,2,10,11,100]
|
||||
// target=0 returns -1
|
||||
// target=1 returns -1
|
||||
// target=2 returns -1
|
||||
// target=10 returns 9
|
||||
// target=50 returns 10
|
||||
// target=100 returns 99
|
||||
func (bc *bitmapContainer) previousValue(target uint16) int {
|
||||
if bc.cardinality == 0 {
|
||||
return -1
|
||||
}
|
||||
return bc.uPrevSetBit(uint(target))
|
||||
}
|
||||
|
||||
// previousAbsentValue returns the next absent value.
|
||||
func (bc *bitmapContainer) previousAbsentValue(target uint16) int {
|
||||
if bc.cardinality == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
var (
|
||||
x = target >> 6
|
||||
length = uint(len(bc.bitmap))
|
||||
)
|
||||
if uint(x) >= length {
|
||||
return -1
|
||||
}
|
||||
w := bc.bitmap[x]
|
||||
shifted := w >> uint(target%64)
|
||||
if shifted == 0 {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
// Check if all 1's
|
||||
// if statement - we skip if we have all ones [1,1,1,1...1] as no value is absent
|
||||
if ^shifted != 0 {
|
||||
|
||||
if countTrailingZeros(shifted) > 0 {
|
||||
// we have something like shifted=[X,Y,Z,..., 0,0,0]. This means the target bit is zero
|
||||
return int(target)
|
||||
}
|
||||
|
||||
// The rotate will rotate the target bit into the leading position.
|
||||
// We know the target bit is not zero because of the countTrailingZero check above
|
||||
// We then shift the target bit out of the way.
|
||||
// Assume a structure like an original structure like [X,Y,Z,..., Target, A, B,C...]
|
||||
// shifted will be [X,Y,Z...Target]
|
||||
// shiftedRotated will be [A,B,C....]
|
||||
// If countLeadingZeros > 0 then A is zero, if not at least A is 1 return
|
||||
// Else count the number of ones's until a 0
|
||||
shiftedRotated := bits.RotateLeft64(w, int(64-uint(target%64))-1) << 1
|
||||
leadingZeros := countLeadingZeros(shiftedRotated)
|
||||
if leadingZeros > 0 {
|
||||
return int(target) - 1
|
||||
}
|
||||
leadingOnes := countLeadingOnes(shiftedRotated)
|
||||
if leadingOnes > 0 {
|
||||
return int(target) - leadingOnes - 1
|
||||
}
|
||||
|
||||
}
|
||||
x++
|
||||
for ; uint(x) < length; x++ {
|
||||
if bc.bitmap[x] == 0 {
|
||||
return int(x * 64)
|
||||
}
|
||||
if ^bc.bitmap[x] != 0 {
|
||||
trailing := countTrailingOnes(bc.bitmap[x])
|
||||
return int(x*64) + trailing
|
||||
}
|
||||
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// validate checks that the container size is non-negative
|
||||
func (bc *bitmapContainer) validate() error {
|
||||
if bc.cardinality < arrayDefaultMaxSize {
|
||||
return fmt.Errorf("bitmap container size was less than: %d", arrayDefaultMaxSize)
|
||||
}
|
||||
|
||||
if maxCapacity < len(bc.bitmap)*64 {
|
||||
return fmt.Errorf("bitmap slize size %d exceeded max capacity %d", maxCapacity, len(bc.bitmap)*64)
|
||||
}
|
||||
|
||||
if bc.cardinality > maxCapacity {
|
||||
return fmt.Errorf("bitmap container size was greater than: %d", maxCapacity)
|
||||
}
|
||||
|
||||
if bc.cardinality != int(popcntSlice(bc.bitmap)) {
|
||||
return fmt.Errorf("bitmap container size %d did not match underlying slice length: %d", bc.cardinality, int(popcntSlice(bc.bitmap)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
// "go1.9", from Go version 1.9 onward
|
||||
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
|
||||
|
||||
package roaring
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// countLeadingOnes returns the number of leading zeros bits in x; the result is 64 for x == 0.
|
||||
func countLeadingZeros(x uint64) int {
|
||||
return bits.LeadingZeros64(x)
|
||||
}
|
||||
|
||||
// countLeadingOnes returns the number of leading ones bits in x; the result is 0 for x == 0.
|
||||
func countLeadingOnes(x uint64) int {
|
||||
return bits.LeadingZeros64(^x)
|
||||
}
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
// "go1.9", from Go version 1.9 onward
|
||||
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
|
||||
|
||||
package roaring
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// countTrailingZeros returns the number of trailing zero bits in x; the result is 64 for x == 0.
|
||||
func countTrailingZeros(x uint64) int {
|
||||
return bits.TrailingZeros64(x)
|
||||
}
|
||||
|
||||
// countTrailingOnes returns the number of trailing one bits in x
|
||||
// The result is 64 for x == 9,223,372,036,854,775,807.
|
||||
// The result is 0 for x == 0.
|
||||
func countTrailingOnes(x uint64) int {
|
||||
return bits.TrailingZeros64(^x)
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
+242
-19
@@ -12,7 +12,7 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/RoaringBitmap/roaring/internal"
|
||||
"github.com/RoaringBitmap/roaring/v2/internal"
|
||||
"github.com/bits-and-blooms/bitset"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,6 @@ func (rb *Bitmap) ToBase64() (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := rb.WriteTo(buf)
|
||||
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
|
||||
|
||||
}
|
||||
|
||||
// FromBase64 deserializes a bitmap from Base64
|
||||
@@ -54,10 +53,12 @@ func (rb *Bitmap) ToBytes() ([]byte, error) {
|
||||
return rb.highlowcontainer.toBytes()
|
||||
}
|
||||
|
||||
const wordSize = uint64(64)
|
||||
const log2WordSize = uint64(6)
|
||||
const capacity = ^uint64(0)
|
||||
const bitmapContainerSize = (1 << 16) / 64 // bitmap size in words
|
||||
const (
|
||||
wordSize = uint64(64)
|
||||
log2WordSize = uint64(6)
|
||||
capacity = ^uint64(0)
|
||||
bitmapContainerSize = (1 << 16) / 64 // bitmap size in words
|
||||
)
|
||||
|
||||
// DenseSize returns the size of the bitmap when stored as a dense bitmap.
|
||||
func (rb *Bitmap) DenseSize() uint64 {
|
||||
@@ -276,14 +277,19 @@ func (rb *Bitmap) Checksum() uint64 {
|
||||
return hash
|
||||
}
|
||||
|
||||
// FromUnsafeBytes reads a serialized version of this bitmap from the byte buffer without copy.
|
||||
// FromUnsafeBytes reads a serialized version of this bitmap from the byte buffer without copy
|
||||
// (for advanced users only, you must be an expert Go programmer!).
|
||||
// E.g., you can use this method to read a serialized bitmap from a memory-mapped file written out
|
||||
// with the WriteTo method.
|
||||
// The format specification is
|
||||
// https://github.com/RoaringBitmap/RoaringFormatSpec
|
||||
// It is the caller's responsibility to ensure that the input data is not modified and remains valid for the entire lifetime of this bitmap.
|
||||
// This method avoids small allocations but holds references to the input data buffer. It is GC-friendly, but it may consume more memory eventually.
|
||||
// The containers in the resulting bitmap are immutable containers tied to the provided byte array and they rely on
|
||||
// copy-on-write which means that modifying them creates copies. Thus FromUnsafeBytes is more likely to be appropriate for read-only use cases,
|
||||
// when the resulting bitmap can be considered immutable.
|
||||
//
|
||||
// See also the FromBuffer function.
|
||||
// See also the FromBuffer function. We recommend benchmarking both functions to determine which one is more suitable for your use case.
|
||||
// See https://github.com/RoaringBitmap/roaring/pull/395 for more details.
|
||||
func (rb *Bitmap) FromUnsafeBytes(data []byte, cookieHeader ...byte) (p int64, err error) {
|
||||
stream := internal.NewByteBuffer(data)
|
||||
@@ -291,11 +297,13 @@ func (rb *Bitmap) FromUnsafeBytes(data []byte, cookieHeader ...byte) (p int64, e
|
||||
}
|
||||
|
||||
// ReadFrom reads a serialized version of this bitmap from stream.
|
||||
// E.g., you can use this method to read a serialized bitmap from a file written
|
||||
// with the WriteTo method.
|
||||
// The format is compatible with other RoaringBitmap
|
||||
// implementations (Java, C) and is documented here:
|
||||
// https://github.com/RoaringBitmap/RoaringFormatSpec
|
||||
// Since io.Reader is regarded as a stream and cannot be read twice.
|
||||
// So add cookieHeader to accept the 4-byte data that has been read in roaring64.ReadFrom.
|
||||
// Since io.Reader is regarded as a stream and cannot be read twice,
|
||||
// we add cookieHeader to accept the 4-byte data that has been read in roaring64.ReadFrom.
|
||||
// It is not necessary to pass cookieHeader when call roaring.ReadFrom to read the roaring32 data directly.
|
||||
func (rb *Bitmap) ReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err error) {
|
||||
stream, ok := reader.(internal.ByteInput)
|
||||
@@ -313,7 +321,18 @@ func (rb *Bitmap) ReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err
|
||||
return
|
||||
}
|
||||
|
||||
// FromBuffer creates a bitmap from its serialized version stored in buffer
|
||||
// MustReadFrom calls ReadFrom internally.
|
||||
// After deserialization Validate will be called.
|
||||
// If the Bitmap fails to validate, a panic with the validation error will be thrown
|
||||
func (rb *Bitmap) MustReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err error) {
|
||||
rb.ReadFrom(reader, cookieHeader...)
|
||||
if err := rb.Validate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FromBuffer creates a bitmap from its serialized version stored in buffer (E.g., as written by WriteTo).
|
||||
//
|
||||
// The format specification is available here:
|
||||
// https://github.com/RoaringBitmap/RoaringFormatSpec
|
||||
@@ -960,7 +979,6 @@ func (rb *Bitmap) CheckedAdd(x uint32) bool {
|
||||
newac := newArrayContainer()
|
||||
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, newac.iaddReturnMinimized(lowbits(x)))
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// AddInt adds the integer x to the bitmap (convenience method: the parameter is casted to uint32 and we call Add)
|
||||
@@ -998,7 +1016,6 @@ func (rb *Bitmap) CheckedRemove(x uint32) bool {
|
||||
return C.getCardinality() < oldcard
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the Bitmap is empty (it is faster than doing (GetCardinality() == 0))
|
||||
@@ -1088,7 +1105,7 @@ main:
|
||||
break main
|
||||
}
|
||||
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
|
||||
} else { //s1 > s2
|
||||
} else { // s1 > s2
|
||||
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
|
||||
if pos2 == length2 {
|
||||
break main
|
||||
@@ -1187,7 +1204,7 @@ main:
|
||||
break main
|
||||
}
|
||||
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
|
||||
} else { //s1 > s2
|
||||
} else { // s1 > s2
|
||||
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
|
||||
if pos2 == length2 {
|
||||
break main
|
||||
@@ -1256,7 +1273,7 @@ main:
|
||||
break main
|
||||
}
|
||||
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
|
||||
} else { //s1 > s2
|
||||
} else { // s1 > s2
|
||||
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
|
||||
if pos2 == length2 {
|
||||
break main
|
||||
@@ -1396,7 +1413,7 @@ main:
|
||||
break main
|
||||
}
|
||||
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
|
||||
} else { //s1 > s2
|
||||
} else { // s1 > s2
|
||||
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
|
||||
if pos2 == length2 {
|
||||
break main
|
||||
@@ -1584,7 +1601,7 @@ main:
|
||||
}
|
||||
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
|
||||
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
|
||||
} else { //s1 > s2
|
||||
} else { // s1 > s2
|
||||
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
|
||||
if pos2 == length2 {
|
||||
break main
|
||||
@@ -1632,7 +1649,6 @@ func BitmapOf(dat ...uint32) *Bitmap {
|
||||
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
|
||||
// while uint64(0x100000000) cannot be represented as a 32-bit value.
|
||||
func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) {
|
||||
|
||||
if rangeEnd > MaxUint32+1 {
|
||||
panic("rangeEnd > MaxUint32+1")
|
||||
}
|
||||
@@ -1869,6 +1885,206 @@ func (rb *Bitmap) CloneCopyOnWriteContainers() {
|
||||
rb.highlowcontainer.cloneCopyOnWriteContainers()
|
||||
}
|
||||
|
||||
// NextValue returns the next largest value in the bitmap, or -1
|
||||
// if none is present. This function should not be used inside
|
||||
// a performance-sensitive loop: prefer iterators if
|
||||
// performance is a concern.
|
||||
func (rb *Bitmap) NextValue(target uint32) int64 {
|
||||
originalKey := highbits(target)
|
||||
query := lowbits(target)
|
||||
var nextValue int64
|
||||
nextValue = -1
|
||||
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
|
||||
for containerIndex < rb.highlowcontainer.size() && nextValue == -1 {
|
||||
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
container := rb.highlowcontainer.getContainer(containerKey)
|
||||
// if containerKey > orginalKey then we are past the container which mapped to the orignal key
|
||||
// in that case we can just return the minimum from that container
|
||||
var responseBit int64
|
||||
if containerKey > originalKey {
|
||||
bit, err := container.safeMinimum()
|
||||
if err == nil {
|
||||
responseBit = -1
|
||||
}
|
||||
responseBit = int64(bit)
|
||||
} else {
|
||||
responseBit = int64(container.nextValue(query))
|
||||
}
|
||||
|
||||
if responseBit == -1 {
|
||||
nextValue = -1
|
||||
} else {
|
||||
nextValue = int64(combineLoHi32(uint32(responseBit), uint32(containerKey)))
|
||||
}
|
||||
containerIndex++
|
||||
}
|
||||
|
||||
return nextValue
|
||||
}
|
||||
|
||||
// PreviousValue returns the previous largest value in the bitmap, or -1
|
||||
// if none is present. This function should not be used inside
|
||||
// a performance-sensitive loop: prefer iterators if
|
||||
// performance is a concern.
|
||||
func (rb *Bitmap) PreviousValue(target uint32) int64 {
|
||||
if rb.IsEmpty() {
|
||||
return -1
|
||||
}
|
||||
|
||||
originalKey := highbits(uint32(target))
|
||||
query := lowbits(uint32(target))
|
||||
var prevValue int64
|
||||
prevValue = -1
|
||||
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
|
||||
|
||||
if containerIndex == rb.highlowcontainer.size() {
|
||||
return int64(rb.Maximum())
|
||||
}
|
||||
|
||||
if rb.highlowcontainer.getKeyAtIndex(containerIndex) > originalKey {
|
||||
// target absent, key of first container after target too high
|
||||
containerIndex--
|
||||
}
|
||||
|
||||
for containerIndex != -1 && prevValue == -1 {
|
||||
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
container := rb.highlowcontainer.getContainer(containerKey)
|
||||
// if containerKey > originalKey then we are past the container which mapped to the original key
|
||||
// in that case we can just return the minimum from that container
|
||||
var responseBit int
|
||||
if containerKey < originalKey {
|
||||
bit, err := container.safeMaximum()
|
||||
|
||||
if err == nil {
|
||||
responseBit = -1
|
||||
}
|
||||
responseBit = int(bit)
|
||||
} else {
|
||||
responseBit = container.previousValue(query)
|
||||
}
|
||||
|
||||
if responseBit == -1 {
|
||||
prevValue = -1
|
||||
} else {
|
||||
prevValue = int64(combineLoHi32(uint32(responseBit), uint32(containerKey)))
|
||||
}
|
||||
containerIndex--
|
||||
}
|
||||
|
||||
return prevValue
|
||||
}
|
||||
|
||||
// NextAbsentValue returns the next largest missing value in the bitmap, or -1
|
||||
// if none is present. This function should not be used inside
|
||||
// a performance-sensitive loop: prefer iterators if
|
||||
// performance is a concern.
|
||||
func (rb *Bitmap) NextAbsentValue(target uint32) int64 {
|
||||
originalKey := highbits(target)
|
||||
query := lowbits(target)
|
||||
var nextValue int64
|
||||
nextValue = -1
|
||||
|
||||
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
|
||||
if containerIndex == rb.highlowcontainer.size() {
|
||||
// if we are here it means no container found, just return the target
|
||||
return int64(target)
|
||||
}
|
||||
|
||||
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
|
||||
keyspace := uint32(containerKey) << 16
|
||||
if target < keyspace {
|
||||
// target is less than the start of the keyspace start
|
||||
// that means target cannot be in the keyspace
|
||||
return int64(target)
|
||||
}
|
||||
|
||||
container := rb.highlowcontainer.getContainer(containerKey)
|
||||
nextValue = int64(container.nextAbsentValue(query))
|
||||
for {
|
||||
if nextValue != (1 << 16) {
|
||||
return int64(combineLoHi32(uint32(nextValue), keyspace))
|
||||
}
|
||||
|
||||
if containerIndex == rb.highlowcontainer.size()-1 {
|
||||
val, err := container.safeMaximum()
|
||||
if err == nil {
|
||||
return -1
|
||||
}
|
||||
return int64(val) + 1
|
||||
}
|
||||
containerIndex++
|
||||
nextContainerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
if containerKey < nextContainerKey {
|
||||
// There is a gap between keys
|
||||
// Just increment the current key and shift to get HoB
|
||||
return int64(containerKey+1) << 16
|
||||
}
|
||||
containerKey = nextContainerKey
|
||||
container = rb.highlowcontainer.getContainer(containerKey)
|
||||
nextValue = int64(container.nextAbsentValue(0))
|
||||
}
|
||||
}
|
||||
|
||||
// PreviousAbsentValue returns the previous largest missing value in the bitmap, or -1
|
||||
// if none is present. This function should not be used inside
|
||||
// a performance-sensitive loop: prefer iterators if
|
||||
// performance is a concern.
|
||||
func (rb *Bitmap) PreviousAbsentValue(target uint32) int64 {
|
||||
originalKey := highbits(target)
|
||||
query := lowbits(target)
|
||||
var prevValue int64
|
||||
prevValue = -1
|
||||
|
||||
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
|
||||
|
||||
if containerIndex == rb.highlowcontainer.size() {
|
||||
// if we are here it means no container found, just return the target
|
||||
return int64(target)
|
||||
}
|
||||
|
||||
if containerIndex == -1 {
|
||||
// if we are here it means no container found, just return the target
|
||||
return int64(target)
|
||||
}
|
||||
|
||||
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
keyspace := uint32(containerKey) << 16
|
||||
if target < keyspace {
|
||||
// target is less than the start of the keyspace start
|
||||
// that means target cannot be in the keyspace
|
||||
return int64(target)
|
||||
}
|
||||
|
||||
container := rb.highlowcontainer.getContainer(containerKey)
|
||||
prevValue = int64(container.previousAbsentValue(query))
|
||||
for {
|
||||
if prevValue != -1 {
|
||||
return int64(combineLoHi32(uint32(prevValue), keyspace))
|
||||
}
|
||||
|
||||
if containerIndex == 0 {
|
||||
val, err := container.safeMinimum()
|
||||
if err == nil {
|
||||
// OR panic, Java panics
|
||||
return -1
|
||||
}
|
||||
return int64(val) - 1
|
||||
}
|
||||
containerIndex--
|
||||
nextContainerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
|
||||
if nextContainerKey < containerKey-1 {
|
||||
// There is a gap between keys, eg missing container
|
||||
// Just decrement the current key and shift to get HoB of the missing container
|
||||
return (int64(containerKey) << 16) - 1
|
||||
}
|
||||
containerKey = nextContainerKey
|
||||
container = rb.highlowcontainer.getContainer(containerKey)
|
||||
highestPossible16 := (1 << 16) - 1
|
||||
prevValue = int64(container.previousAbsentValue(uint16(highestPossible16)))
|
||||
}
|
||||
}
|
||||
|
||||
// FlipInt calls Flip after casting the parameters (convenience method)
|
||||
func FlipInt(bm *Bitmap, rangeStart, rangeEnd int) *Bitmap {
|
||||
return Flip(bm, uint64(rangeStart), uint64(rangeEnd))
|
||||
@@ -1916,3 +2132,10 @@ func (rb *Bitmap) Stats() Statistics {
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
// Validate checks if the bitmap is internally consistent.
|
||||
// You may call it after deserialization to check that the bitmap is valid.
|
||||
// This function returns an error if the bitmap is invalid, nil otherwise.
|
||||
func (rb *Bitmap) Validate() error {
|
||||
return rb.highlowcontainer.validate()
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
+278
-85
@@ -3,17 +3,9 @@ package roaring64
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/bits"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
// Min64BitSigned - Minimum 64 bit value
|
||||
Min64BitSigned = -9223372036854775808
|
||||
// Max64BitSigned - Maximum 64 bit value
|
||||
Max64BitSigned = 9223372036854775807
|
||||
)
|
||||
|
||||
// BSI is at its simplest is an array of bitmaps that represent an encoded
|
||||
@@ -32,13 +24,16 @@ type BSI struct {
|
||||
runOptimized bool
|
||||
}
|
||||
|
||||
// NewBSI constructs a new BSI. Min/Max values are optional. If set to 0
|
||||
// then the underlying BSI will be automatically sized.
|
||||
// NewBSI constructs a new BSI. Note that it is your responsibility to ensure that
|
||||
// the min/max values are set correctly. Queries CompareValue, MinMax, etc. will not
|
||||
// work correctly if the min/max values are not set correctly.
|
||||
func NewBSI(maxValue int64, minValue int64) *BSI {
|
||||
|
||||
bitsz := bits.Len64(uint64(minValue))
|
||||
if bits.Len64(uint64(maxValue)) > bitsz {
|
||||
bitsz = bits.Len64(uint64(maxValue))
|
||||
bitszmin := big.NewInt(minValue).BitLen() + 1
|
||||
bitszmax := big.NewInt(maxValue).BitLen() + 1
|
||||
bitsz := bitszmin
|
||||
if bitszmax > bitsz {
|
||||
bitsz = bitszmax
|
||||
}
|
||||
ba := make([]Bitmap, bitsz)
|
||||
return &BSI{bA: ba, MaxValue: maxValue, MinValue: minValue}
|
||||
@@ -81,41 +76,97 @@ func (b *BSI) GetCardinality() uint64 {
|
||||
|
||||
// BitCount returns the number of bits needed to represent values.
|
||||
func (b *BSI) BitCount() int {
|
||||
return len(b.bA)
|
||||
return len(b.bA) - 1 // Exclude sign bit
|
||||
}
|
||||
|
||||
// SetValue sets a value for a given columnID.
|
||||
func (b *BSI) SetValue(columnID uint64, value int64) {
|
||||
// IsBigUInt returns the number of bits needed to represent values.
|
||||
func (b *BSI) isBig() bool {
|
||||
return len(b.bA) > 64
|
||||
}
|
||||
|
||||
// IsNegative returns true for negative values
|
||||
func (b *BSI) IsNegative(columnID uint64) bool {
|
||||
if len(b.bA) == 0 {
|
||||
return false
|
||||
}
|
||||
return b.bA[b.BitCount()].Contains(columnID)
|
||||
}
|
||||
|
||||
// SetBigValue sets a value that exceeds 64 bits
|
||||
func (b *BSI) SetBigValue(columnID uint64, value *big.Int) {
|
||||
// If max/min values are set to zero then automatically determine bit array size
|
||||
if b.MaxValue == 0 && b.MinValue == 0 {
|
||||
minBits := bits.Len64(uint64(value))
|
||||
minBits := value.BitLen() + 1
|
||||
if minBits == 1 {
|
||||
minBits = 2
|
||||
}
|
||||
for len(b.bA) < minBits {
|
||||
b.bA = append(b.bA, Bitmap{})
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < b.BitCount(); i++ {
|
||||
if uint64(value)&(1<<uint64(i)) > 0 {
|
||||
b.bA[i].Add(columnID)
|
||||
} else {
|
||||
for i := b.BitCount(); i >= 0; i-- {
|
||||
if value.Bit(i) == 0 {
|
||||
b.bA[i].Remove(columnID)
|
||||
} else {
|
||||
b.bA[i].Add(columnID)
|
||||
}
|
||||
}
|
||||
b.eBM.Add(columnID)
|
||||
}
|
||||
|
||||
// SetValue sets a value for a given columnID.
|
||||
func (b *BSI) SetValue(columnID uint64, value int64) {
|
||||
b.SetBigValue(columnID, big.NewInt(value))
|
||||
}
|
||||
|
||||
// GetValue gets the value at the column ID. Second param will be false for non-existent values.
|
||||
func (b *BSI) GetValue(columnID uint64) (value int64, exists bool) {
|
||||
bv, exists := b.GetBigValue(columnID)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
if !bv.IsInt64() {
|
||||
if bv.Sign() == -1 {
|
||||
msg := fmt.Errorf("can't represent a negative %d bit value as an int64", b.BitCount())
|
||||
panic(msg)
|
||||
}
|
||||
if bv.Sign() == 1 {
|
||||
msg := fmt.Errorf("can't represent a positive %d bit value as an int64", b.BitCount())
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
return bv.Int64(), exists
|
||||
}
|
||||
|
||||
// GetBigValue gets the value at the column ID. Second param will be false for non-existent values.
|
||||
func (b *BSI) GetBigValue(columnID uint64) (value *big.Int, exists bool) {
|
||||
exists = b.eBM.Contains(columnID)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
for i := 0; i < b.BitCount(); i++ {
|
||||
val := big.NewInt(0)
|
||||
for i := b.BitCount(); i >= 0; i-- {
|
||||
if b.bA[i].Contains(columnID) {
|
||||
value |= 1 << i
|
||||
bigBit := big.NewInt(1)
|
||||
bigBit.Lsh(bigBit, uint(i))
|
||||
val.Or(val, bigBit)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
if b.IsNegative(columnID) {
|
||||
val = negativeTwosComplementToInt(val)
|
||||
}
|
||||
return val, exists
|
||||
}
|
||||
|
||||
func negativeTwosComplementToInt(val *big.Int) *big.Int {
|
||||
inverted := new(big.Int).Not(val)
|
||||
mask := new(big.Int).Lsh(big.NewInt(1), uint(val.BitLen()))
|
||||
inverted.And(inverted, mask.Sub(mask, big.NewInt(1)))
|
||||
inverted.Add(inverted, big.NewInt(1))
|
||||
val.Neg(inverted)
|
||||
return val
|
||||
}
|
||||
|
||||
type action func(t *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.WaitGroup)
|
||||
@@ -235,13 +286,15 @@ const (
|
||||
type task struct {
|
||||
bsi *BSI
|
||||
op Operation
|
||||
valueOrStart int64
|
||||
end int64
|
||||
values map[int64]struct{}
|
||||
valueOrStart *big.Int
|
||||
end *big.Int
|
||||
values map[string]struct{}
|
||||
bits *Bitmap
|
||||
}
|
||||
|
||||
// CompareValue compares value.
|
||||
// Values should be in the range of the BSI (max, min). If the value is outside the range, the result
|
||||
// might erroneous. The operation parameter indicates the type of comparison to be made.
|
||||
// For all operations with the exception of RANGE, the value to be compared is specified by valueOrStart.
|
||||
// For the RANGE parameter the comparison criteria is >= valueOrStart and <= end.
|
||||
// The parallelism parameter indicates the number of CPU threads to be applied for processing. A value
|
||||
@@ -249,6 +302,26 @@ type task struct {
|
||||
func (b *BSI) CompareValue(parallelism int, op Operation, valueOrStart, end int64,
|
||||
foundSet *Bitmap) *Bitmap {
|
||||
|
||||
return b.CompareBigValue(parallelism, op, big.NewInt(valueOrStart), big.NewInt(end), foundSet)
|
||||
}
|
||||
|
||||
// CompareBigValue compares value.
|
||||
// Values should be in the range of the BSI (max, min). If the value is outside the range, the result
|
||||
// might erroneous. The operation parameter indicates the type of comparison to be made.
|
||||
// For all operations with the exception of RANGE, the value to be compared is specified by valueOrStart.
|
||||
// For the RANGE parameter the comparison criteria is >= valueOrStart and <= end.
|
||||
// The parallelism parameter indicates the number of CPU threads to be applied for processing. A value
|
||||
// of zero indicates that all available CPU resources will be potentially utilized.
|
||||
func (b *BSI) CompareBigValue(parallelism int, op Operation, valueOrStart, end *big.Int,
|
||||
foundSet *Bitmap) *Bitmap {
|
||||
|
||||
if valueOrStart == nil {
|
||||
valueOrStart = b.MinMaxBig(parallelism, MIN, &b.eBM)
|
||||
}
|
||||
if end == nil && op == RANGE {
|
||||
end = b.MinMaxBig(parallelism, MAX, &b.eBM)
|
||||
}
|
||||
|
||||
comp := &task{bsi: b, op: op, valueOrStart: valueOrStart, end: end}
|
||||
if foundSet == nil {
|
||||
return parallelExecutor(parallelism, comp, compareValue, &b.eBM)
|
||||
@@ -256,6 +329,53 @@ func (b *BSI) CompareValue(parallelism int, op Operation, valueOrStart, end int6
|
||||
return parallelExecutor(parallelism, comp, compareValue, foundSet)
|
||||
}
|
||||
|
||||
// Returns a twos complement value given a value, the return will be bit extended to 'bits' length
|
||||
// if the value is negative
|
||||
func twosComplement(num *big.Int, bitCount int) *big.Int {
|
||||
// Check if the number is negative
|
||||
isNegative := num.Sign() < 0
|
||||
|
||||
// Get the absolute value if negative
|
||||
abs := new(big.Int).Abs(num)
|
||||
|
||||
// Convert to binary string
|
||||
binStr := abs.Text(2)
|
||||
|
||||
// Pad with zeros to the left
|
||||
if len(binStr) < bitCount {
|
||||
binStr = fmt.Sprintf("%0*s", bitCount, binStr)
|
||||
}
|
||||
|
||||
// If negative, calculate two's complement
|
||||
if isNegative {
|
||||
// Invert bits
|
||||
inverted := make([]byte, len(binStr))
|
||||
for i := range binStr {
|
||||
if binStr[i] == '0' {
|
||||
inverted[i] = '1'
|
||||
} else {
|
||||
inverted[i] = '0'
|
||||
}
|
||||
}
|
||||
|
||||
// Add 1
|
||||
carry := byte(1)
|
||||
for i := len(inverted) - 1; i >= 0; i-- {
|
||||
inverted[i] += carry
|
||||
if inverted[i] == '2' {
|
||||
inverted[i] = '0'
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
binStr = string(inverted)
|
||||
}
|
||||
|
||||
bigInt := new(big.Int)
|
||||
_, _ = bigInt.SetString(binStr, 2)
|
||||
return bigInt
|
||||
}
|
||||
|
||||
func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.WaitGroup) {
|
||||
|
||||
defer wg.Done()
|
||||
@@ -265,32 +385,31 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
results.RunOptimize()
|
||||
}
|
||||
|
||||
x := e.bsi.BitCount()
|
||||
startIsNegative := x == 64 && uint64(e.valueOrStart)&(1<<uint64(x-1)) > 0
|
||||
endIsNegative := x == 64 && uint64(e.end)&(1<<uint64(x-1)) > 0
|
||||
startIsNegative := e.valueOrStart.Sign() == -1
|
||||
endIsNegative := true
|
||||
if e.end != nil {
|
||||
endIsNegative = e.end.Sign() == -1
|
||||
}
|
||||
|
||||
for i := 0; i < len(batch); i++ {
|
||||
cID := batch[i]
|
||||
eq1, eq2 := true, true
|
||||
lt1, lt2, gt1 := false, false, false
|
||||
j := e.bsi.BitCount() - 1
|
||||
isNegative := false
|
||||
if x == 64 {
|
||||
isNegative = e.bsi.bA[j].Contains(cID)
|
||||
j--
|
||||
}
|
||||
j := e.bsi.BitCount()
|
||||
isNegative := e.bsi.IsNegative(cID)
|
||||
compStartValue := e.valueOrStart
|
||||
compEndValue := e.end
|
||||
if isNegative != startIsNegative {
|
||||
compStartValue = ^e.valueOrStart + 1
|
||||
compStartValue = twosComplement(e.valueOrStart, e.bsi.BitCount()+1)
|
||||
}
|
||||
if isNegative != endIsNegative {
|
||||
compEndValue = ^e.end + 1
|
||||
if isNegative != endIsNegative && e.end != nil {
|
||||
compEndValue = twosComplement(e.end, e.bsi.BitCount()+1)
|
||||
}
|
||||
|
||||
for ; j >= 0; j-- {
|
||||
sliceContainsBit := e.bsi.bA[j].Contains(cID)
|
||||
|
||||
if uint64(compStartValue)&(1<<uint64(j)) > 0 {
|
||||
if compStartValue.Bit(j) == 1 {
|
||||
// BIT in value is SET
|
||||
if !sliceContainsBit {
|
||||
if eq1 {
|
||||
@@ -303,7 +422,9 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
}
|
||||
}
|
||||
eq1 = false
|
||||
break
|
||||
if e.op != RANGE {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -319,6 +440,7 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
}
|
||||
}
|
||||
eq1 = false
|
||||
|
||||
if e.op != RANGE {
|
||||
break
|
||||
}
|
||||
@@ -326,7 +448,7 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
}
|
||||
}
|
||||
|
||||
if e.op == RANGE && uint64(compEndValue)&(1<<uint64(j)) > 0 {
|
||||
if e.op == RANGE && compEndValue.Bit(j) == 1 {
|
||||
// BIT in value is SET
|
||||
if !sliceContainsBit {
|
||||
if eq2 {
|
||||
@@ -347,11 +469,9 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
lt2 = true
|
||||
}
|
||||
eq2 = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch e.op {
|
||||
@@ -387,15 +507,24 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
|
||||
resultsChan <- results
|
||||
}
|
||||
|
||||
// MinMax - Find minimum or maximum value.
|
||||
// MinMax - Find minimum or maximum int64 value.
|
||||
func (b *BSI) MinMax(parallelism int, op Operation, foundSet *Bitmap) int64 {
|
||||
return b.MinMaxBig(parallelism, op, foundSet).Int64()
|
||||
}
|
||||
|
||||
// MinMaxBig - Find minimum or maximum value.
|
||||
func (b *BSI) MinMaxBig(parallelism int, op Operation, foundSet *Bitmap) *big.Int {
|
||||
|
||||
var n int = parallelism
|
||||
if n == 0 {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
|
||||
resultsChan := make(chan int64, n)
|
||||
resultsChan := make(chan *big.Int, n)
|
||||
|
||||
if foundSet == nil {
|
||||
foundSet = &b.eBM
|
||||
}
|
||||
|
||||
card := foundSet.GetCardinality()
|
||||
x := card / uint64(n)
|
||||
@@ -418,63 +547,87 @@ func (b *BSI) MinMax(parallelism int, op Operation, foundSet *Bitmap) int64 {
|
||||
wg.Wait()
|
||||
|
||||
close(resultsChan)
|
||||
var minMax int64
|
||||
var minMax *big.Int
|
||||
minSigned, maxSigned := minMaxSignedInt(b.BitCount() + 1)
|
||||
if op == MAX {
|
||||
minMax = Min64BitSigned
|
||||
minMax = minSigned
|
||||
} else {
|
||||
minMax = Max64BitSigned
|
||||
minMax = maxSigned
|
||||
}
|
||||
|
||||
for val := range resultsChan {
|
||||
if (op == MAX && val > minMax) || (op == MIN && val <= minMax) {
|
||||
if (op == MAX && val.Cmp(minMax) > 0) || (op == MIN && val.Cmp(minMax) <= 0) {
|
||||
minMax = val
|
||||
}
|
||||
}
|
||||
return minMax
|
||||
}
|
||||
|
||||
func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg *sync.WaitGroup) {
|
||||
func minMaxSignedInt(bits int) (*big.Int, *big.Int) {
|
||||
// Calculate the maximum value
|
||||
max := new(big.Int).Lsh(big.NewInt(1), uint(bits-1))
|
||||
max.Sub(max, big.NewInt(1))
|
||||
|
||||
// Calculate the minimum value
|
||||
min := new(big.Int).Neg(max)
|
||||
min.Sub(min, big.NewInt(1))
|
||||
|
||||
return min, max
|
||||
}
|
||||
|
||||
func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan *big.Int, wg *sync.WaitGroup) {
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
x := b.BitCount()
|
||||
var value int64 = Max64BitSigned
|
||||
x := b.BitCount() + 1
|
||||
var value *big.Int
|
||||
minSigned, maxSigned := minMaxSignedInt(x)
|
||||
if op == MAX {
|
||||
value = Min64BitSigned
|
||||
value = minSigned
|
||||
} else {
|
||||
value = maxSigned
|
||||
}
|
||||
|
||||
for i := 0; i < len(batch); i++ {
|
||||
cID := batch[i]
|
||||
eq := true
|
||||
lt, gt := false, false
|
||||
j := b.BitCount() - 1
|
||||
var cVal int64
|
||||
valueIsNegative := uint64(value)&(1<<uint64(x-1)) > 0 && bits.Len64(uint64(value)) == 64
|
||||
isNegative := false
|
||||
if x == 64 {
|
||||
isNegative = b.bA[j].Contains(cID)
|
||||
if isNegative {
|
||||
cVal |= 1 << uint64(j)
|
||||
}
|
||||
j--
|
||||
}
|
||||
j := b.BitCount()
|
||||
cVal := new(big.Int)
|
||||
valueIsNegative := value.Sign() == -1
|
||||
isNegative := b.IsNegative(cID)
|
||||
|
||||
compValue := value
|
||||
if isNegative != valueIsNegative {
|
||||
compValue = ^value + 1
|
||||
// convert compValue to twos complement
|
||||
inverted := new(big.Int).Not(compValue)
|
||||
mask := new(big.Int).Lsh(big.NewInt(1), uint(compValue.BitLen()))
|
||||
inverted.And(inverted, mask.Sub(mask, big.NewInt(1)))
|
||||
inverted.Add(inverted, big.NewInt(1))
|
||||
}
|
||||
|
||||
done := false
|
||||
for ; j >= 0; j-- {
|
||||
sliceContainsBit := b.bA[j].Contains(cID)
|
||||
if sliceContainsBit {
|
||||
cVal |= 1 << uint64(j)
|
||||
bigBit := big.NewInt(1)
|
||||
bigBit.Lsh(bigBit, uint(j))
|
||||
cVal.Or(cVal, bigBit)
|
||||
if isNegative {
|
||||
cVal = negativeTwosComplementToInt(cVal)
|
||||
}
|
||||
}
|
||||
if uint64(compValue)&(1<<uint64(j)) > 0 {
|
||||
if done {
|
||||
continue
|
||||
}
|
||||
if compValue.Bit(j) == 1 {
|
||||
// BIT in value is SET
|
||||
if !sliceContainsBit {
|
||||
if eq {
|
||||
eq = false
|
||||
if op == MAX && valueIsNegative && !isNegative {
|
||||
gt = true
|
||||
break
|
||||
done = true
|
||||
}
|
||||
if op == MIN && (!valueIsNegative || (valueIsNegative == isNegative)) {
|
||||
lt = true
|
||||
@@ -491,11 +644,13 @@ func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg
|
||||
}
|
||||
if op == MAX && (valueIsNegative || (valueIsNegative == isNegative)) {
|
||||
gt = true
|
||||
done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lt || gt {
|
||||
value = cVal
|
||||
}
|
||||
@@ -506,19 +661,37 @@ func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg
|
||||
|
||||
// Sum all values contained within the foundSet. As a convenience, the cardinality of the foundSet
|
||||
// is also returned (for calculating the average).
|
||||
func (b *BSI) Sum(foundSet *Bitmap) (sum int64, count uint64) {
|
||||
func (b *BSI) Sum(foundSet *Bitmap) (int64, uint64) {
|
||||
val, count := b.SumBigValues(foundSet)
|
||||
return val.Int64(), count
|
||||
}
|
||||
|
||||
// SumBigValues - Sum all values contained within the foundSet. As a convenience, the cardinality of the foundSet
|
||||
// is also returned (for calculating the average). This method will sum arbitrarily large values.
|
||||
func (b *BSI) SumBigValues(foundSet *Bitmap) (sum *big.Int, count uint64) {
|
||||
if foundSet == nil {
|
||||
foundSet = &b.eBM
|
||||
}
|
||||
sum = new(big.Int)
|
||||
count = foundSet.GetCardinality()
|
||||
resultsChan := make(chan int64, b.BitCount())
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < b.BitCount(); i++ {
|
||||
wg.Add(1)
|
||||
go func(j int) {
|
||||
defer wg.Done()
|
||||
atomic.AddInt64(&sum, int64(foundSet.AndCardinality(&b.bA[j])<<uint(j)))
|
||||
resultsChan <- int64(foundSet.AndCardinality(&b.bA[j]) << uint(j))
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
return
|
||||
close(resultsChan)
|
||||
|
||||
for val := range resultsChan {
|
||||
sum.Add(sum, big.NewInt(val))
|
||||
}
|
||||
sum.Sub(sum, big.NewInt(int64(foundSet.AndCardinality(&b.bA[b.BitCount()])<<uint(b.BitCount()))))
|
||||
|
||||
return sum, count
|
||||
}
|
||||
|
||||
// Transpose calls b.IntersectAndTranspose(0, b.eBM)
|
||||
@@ -533,7 +706,9 @@ func (b *BSI) Transpose() *Bitmap {
|
||||
//
|
||||
// TODO: This implementation is functional but not performant, needs to be re-written perhaps using SIMD SSE2 instructions.
|
||||
func (b *BSI) IntersectAndTranspose(parallelism int, foundSet *Bitmap) *Bitmap {
|
||||
|
||||
if foundSet == nil {
|
||||
foundSet = &b.eBM
|
||||
}
|
||||
trans := &task{bsi: b}
|
||||
return parallelExecutor(parallelism, trans, transpose, foundSet)
|
||||
}
|
||||
@@ -563,12 +738,12 @@ func (b *BSI) ParOr(parallelism int, bsis ...*BSI) {
|
||||
bits := len(b.bA)
|
||||
for i := 0; i < len(bsis); i++ {
|
||||
if len(bsis[i].bA) > bits {
|
||||
bits = bsis[i].BitCount()
|
||||
bits = len(bsis[i].bA )
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we have enough bit slices
|
||||
for bits > b.BitCount() {
|
||||
for bits > len(b.bA) {
|
||||
bm := Bitmap{}
|
||||
bm.RunOptimize()
|
||||
b.bA = append(b.bA, bm)
|
||||
@@ -725,10 +900,20 @@ func (b *BSI) WriteTo(w io.Writer) (n int64, err error) {
|
||||
|
||||
// BatchEqual returns a bitmap containing the column IDs where the values are contained within the list of values provided.
|
||||
func (b *BSI) BatchEqual(parallelism int, values []int64) *Bitmap {
|
||||
//convert list of int64 values to big.Int(s)
|
||||
bigValues := make([]*big.Int, len(values))
|
||||
for i, v := range values {
|
||||
bigValues[i] = big.NewInt(v)
|
||||
}
|
||||
return b.BatchEqualBig(parallelism, bigValues)
|
||||
}
|
||||
|
||||
valMap := make(map[int64]struct{}, len(values))
|
||||
// BatchEqualBig returns a bitmap containing the column IDs where the values are contained within the list of values provided.
|
||||
func (b *BSI) BatchEqualBig(parallelism int, values []*big.Int) *Bitmap {
|
||||
|
||||
valMap := make(map[string]struct{}, len(values))
|
||||
for i := 0; i < len(values); i++ {
|
||||
valMap[values[i]] = struct{}{}
|
||||
valMap[string(values[i].Bytes())] = struct{}{}
|
||||
}
|
||||
comp := &task{bsi: b, values: valMap}
|
||||
return parallelExecutor(parallelism, comp, batchEqual, &b.eBM)
|
||||
@@ -746,8 +931,8 @@ func batchEqual(e *task, batch []uint64, resultsChan chan *Bitmap,
|
||||
|
||||
for i := 0; i < len(batch); i++ {
|
||||
cID := batch[i]
|
||||
if value, ok := e.bsi.GetValue(uint64(cID)); ok {
|
||||
if _, yes := e.values[int64(value)]; yes {
|
||||
if value, ok := e.bsi.GetBigValue(uint64(cID)); ok {
|
||||
if _, yes := e.values[string(value.Bytes())]; yes {
|
||||
results.Add(cID)
|
||||
}
|
||||
}
|
||||
@@ -786,8 +971,8 @@ func (b *BSI) ClearValues(foundSet *Bitmap) {
|
||||
// NewBSIRetainSet - Construct a new BSI from a clone of existing BSI, retain only values contained in foundSet
|
||||
func (b *BSI) NewBSIRetainSet(foundSet *Bitmap) *BSI {
|
||||
|
||||
newBSI := NewBSI(b.MaxValue, b.MinValue)
|
||||
newBSI.bA = make([]Bitmap, b.BitCount())
|
||||
newBSI := NewDefaultBSI()
|
||||
newBSI.bA = make([]Bitmap, b.BitCount()+1)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
@@ -823,13 +1008,13 @@ func (b *BSI) Add(other *BSI) {
|
||||
|
||||
func (b *BSI) addDigit(foundSet *Bitmap, i int) {
|
||||
|
||||
if i >= len(b.bA) {
|
||||
if i >= b.BitCount()+1 || b.BitCount() == 0 {
|
||||
b.bA = append(b.bA, Bitmap{})
|
||||
}
|
||||
carry := And(&b.bA[i], foundSet)
|
||||
b.bA[i].Xor(foundSet)
|
||||
if !carry.IsEmpty() {
|
||||
if i+1 >= len(b.bA) {
|
||||
if i+1 >= b.BitCount() {
|
||||
b.bA = append(b.bA, Bitmap{})
|
||||
}
|
||||
b.addDigit(carry, i+1)
|
||||
@@ -841,7 +1026,12 @@ func (b *BSI) addDigit(foundSet *Bitmap, i int) {
|
||||
// is useful for situations where there is a one-to-many relationship between the vectored integer sets. The resulting BSI
|
||||
// contains the number of times a particular value appeared in the input BSI.
|
||||
func (b *BSI) TransposeWithCounts(parallelism int, foundSet, filterSet *Bitmap) *BSI {
|
||||
|
||||
if foundSet == nil {
|
||||
foundSet = &b.eBM
|
||||
}
|
||||
if filterSet == nil {
|
||||
filterSet = &b.eBM
|
||||
}
|
||||
return parallelExecutorBSIResults(parallelism, b, transposeWithCounts, foundSet, filterSet, true)
|
||||
}
|
||||
|
||||
@@ -871,6 +1061,9 @@ func transposeWithCounts(input *BSI, filterSet *Bitmap, batch []uint64, resultsC
|
||||
|
||||
// Increment - In-place increment of values in a BSI. Found set select columns for incrementing.
|
||||
func (b *BSI) Increment(foundSet *Bitmap) {
|
||||
if foundSet == nil {
|
||||
foundSet = &b.eBM
|
||||
}
|
||||
b.addDigit(foundSet, 0)
|
||||
b.eBM.Or(foundSet)
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
package roaring64
|
||||
|
||||
import (
|
||||
"github.com/RoaringBitmap/roaring"
|
||||
"github.com/RoaringBitmap/roaring/v2"
|
||||
)
|
||||
|
||||
// IntIterable64 allows you to iterate over the values in a Bitmap
|
||||
Generated
Vendored
+5
-4
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/RoaringBitmap/roaring"
|
||||
"github.com/RoaringBitmap/roaring/v2"
|
||||
)
|
||||
|
||||
var defaultWorkerCount = runtime.NumCPU()
|
||||
@@ -144,6 +144,8 @@ func (c parChunk) size() int {
|
||||
return c.ra.size()
|
||||
}
|
||||
|
||||
// parNaiveStartAt returns the index of the first key that is inclusive between start and last
|
||||
// Returns the size if there is no such key
|
||||
func parNaiveStartAt(ra *roaringArray64, start uint32, last uint32) int {
|
||||
for idx, key := range ra.keys {
|
||||
if key >= start && key <= last {
|
||||
@@ -170,7 +172,6 @@ func orOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
|
||||
key2 = ra2.getKeyAtIndex(idx2)
|
||||
|
||||
for key1 <= last && key2 <= last {
|
||||
|
||||
if key1 < key2 {
|
||||
answer.appendCopy(*ra1, idx1)
|
||||
idx1++
|
||||
@@ -188,7 +189,7 @@ func orOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
|
||||
} else {
|
||||
c1 := ra1.getContainerAtIndex(idx1)
|
||||
|
||||
//answer.appendContainer(key1, c1.lazyOR(ra2.getContainerAtIndex(idx2)), false)
|
||||
// answer.appendContainer(key1, c1.lazyOR(ra2.getContainerAtIndex(idx2)), false)
|
||||
answer.appendContainer(key1, roaring.Or(c1, ra2.getContainerAtIndex(idx2)), false)
|
||||
idx1++
|
||||
idx2++
|
||||
@@ -261,7 +262,7 @@ func iorOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
|
||||
} else {
|
||||
c1 := ra1.getWritableContainerAtIndex(idx1)
|
||||
|
||||
//ra1.containers[idx1] = c1.lazyIOR(ra2.getContainerAtIndex(idx2))
|
||||
// ra1.containers[idx1] = c1.lazyIOR(ra2.getContainerAtIndex(idx2))
|
||||
c1.Or(ra2.getContainerAtIndex(idx2))
|
||||
ra1.setContainerAtIndex(idx1, c1)
|
||||
|
||||
Generated
Vendored
+14
-20
@@ -8,12 +8,14 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/RoaringBitmap/roaring"
|
||||
"github.com/RoaringBitmap/roaring/internal"
|
||||
"github.com/RoaringBitmap/roaring/v2"
|
||||
"github.com/RoaringBitmap/roaring/v2/internal"
|
||||
)
|
||||
|
||||
const serialCookieNoRunContainer = 12346 // only arrays and bitmaps
|
||||
const serialCookie = 12347 // runs, arrays, and bitmaps
|
||||
const (
|
||||
serialCookieNoRunContainer = 12346 // only arrays and bitmaps
|
||||
serialCookie = 12347 // runs, arrays, and bitmaps
|
||||
)
|
||||
|
||||
// Bitmap represents a compressed bitmap where you can add integers.
|
||||
type Bitmap struct {
|
||||
@@ -25,7 +27,6 @@ func (rb *Bitmap) ToBase64() (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := rb.WriteTo(buf)
|
||||
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
|
||||
|
||||
}
|
||||
|
||||
// FromBase64 deserializes a bitmap from Base64
|
||||
@@ -52,7 +53,6 @@ func (rb *Bitmap) ToBytes() ([]byte, error) {
|
||||
// implementations (Java, Go, C++) and it has a specification :
|
||||
// https://github.com/RoaringBitmap/RoaringFormatSpec#extention-for-64-bit-implementations
|
||||
func (rb *Bitmap) WriteTo(stream io.Writer) (int64, error) {
|
||||
|
||||
var n int64
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, uint64(rb.highlowcontainer.size()))
|
||||
@@ -87,11 +87,10 @@ func (rb *Bitmap) WriteTo(stream io.Writer) (int64, error) {
|
||||
func (rb *Bitmap) FromUnsafeBytes(data []byte) (p int64, err error) {
|
||||
stream := internal.NewByteBuffer(data)
|
||||
sizeBuf := make([]byte, 8)
|
||||
n, err := stream.Read(sizeBuf)
|
||||
_, err = stream.Read(sizeBuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
p += int64(n)
|
||||
size := binary.LittleEndian.Uint64(sizeBuf)
|
||||
|
||||
rb.highlowcontainer.resize(0)
|
||||
@@ -115,17 +114,16 @@ func (rb *Bitmap) FromUnsafeBytes(data []byte) (p int64, err error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error in bitmap.UnsafeFromBytes: could not read key #%d: %w", i, err)
|
||||
}
|
||||
p += 4
|
||||
rb.highlowcontainer.keys[i] = binary.LittleEndian.Uint32(keyBuf)
|
||||
rb.highlowcontainer.containers[i] = roaring.NewBitmap()
|
||||
n, err := rb.highlowcontainer.containers[i].ReadFrom(stream)
|
||||
|
||||
if n == 0 || err != nil {
|
||||
return int64(n), fmt.Errorf("Could not deserialize bitmap for key #%d: %s", i, err)
|
||||
}
|
||||
p += int64(n)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
return stream.GetReadBytes(), nil
|
||||
}
|
||||
|
||||
// ReadFrom reads a serialized version of this bitmap from stream.
|
||||
@@ -167,23 +165,15 @@ func (rb *Bitmap) ReadFrom(stream io.Reader) (p int64, err error) {
|
||||
rb.highlowcontainer.keys[i] = binary.LittleEndian.Uint32(keyBuf)
|
||||
rb.highlowcontainer.containers[i] = roaring.NewBitmap()
|
||||
n, err := rb.highlowcontainer.containers[i].ReadFrom(stream)
|
||||
|
||||
if n == 0 || err != nil {
|
||||
return int64(n), fmt.Errorf("Could not deserialize bitmap for key #%d: %s", i, err)
|
||||
}
|
||||
p += int64(n)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// FromBuffer creates a bitmap from its serialized version stored in buffer
|
||||
// func (rb *Bitmap) FromBuffer(data []byte) (p int64, err error) {
|
||||
//
|
||||
// // TODO: Add buffer interning as in base roaring package.
|
||||
// buf := bytes.NewBuffer(data)
|
||||
// return rb.ReadFrom(buf)
|
||||
// }
|
||||
|
||||
// MarshalBinary implements the encoding.BinaryMarshaler interface for the bitmap
|
||||
// (same as ToBytes)
|
||||
func (rb *Bitmap) MarshalBinary() ([]byte, error) {
|
||||
@@ -1251,6 +1241,10 @@ func (rb *Bitmap) GetSerializedSizeInBytes() uint64 {
|
||||
return rb.highlowcontainer.serializedSizeInBytes()
|
||||
}
|
||||
|
||||
func (rb *Bitmap) Validate() error {
|
||||
return rb.highlowcontainer.validate()
|
||||
}
|
||||
|
||||
// Roaring32AsRoaring64 inserts a 32-bit roaring bitmap into
|
||||
// a 64-bit roaring bitmap. No copy is made.
|
||||
func Roaring32AsRoaring64(bm32 *roaring.Bitmap) *Bitmap {
|
||||
Generated
Vendored
+61
-2
@@ -1,7 +1,9 @@
|
||||
package roaring64
|
||||
|
||||
import (
|
||||
"github.com/RoaringBitmap/roaring"
|
||||
"errors"
|
||||
|
||||
"github.com/RoaringBitmap/roaring/v2"
|
||||
)
|
||||
|
||||
type roaringArray64 struct {
|
||||
@@ -11,6 +13,11 @@ type roaringArray64 struct {
|
||||
copyOnWrite bool
|
||||
}
|
||||
|
||||
var (
|
||||
ErrKeySortOrder = errors.New("keys were out of order")
|
||||
ErrCardinalityConstraint = errors.New("size of arrays was not coherent")
|
||||
)
|
||||
|
||||
// runOptimize compresses the element containers to minimize space consumed.
|
||||
// Q: how does this interact with copyOnWrite and needCopyOnWrite?
|
||||
// A: since we aren't changing the logical content, just the representation,
|
||||
@@ -140,7 +147,6 @@ func (ra *roaringArray64) clear() {
|
||||
}
|
||||
|
||||
func (ra *roaringArray64) clone() *roaringArray64 {
|
||||
|
||||
sa := roaringArray64{}
|
||||
sa.copyOnWrite = ra.copyOnWrite
|
||||
|
||||
@@ -328,6 +334,15 @@ func (ra *roaringArray64) hasRunCompression() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the smallest integer index strictly larger than pos such that array[index].key>=min. If none can
|
||||
* be found, return size. Based on code by O. Kaser.
|
||||
*
|
||||
* @param min minimal value
|
||||
* @param pos index to exceed
|
||||
* @return the smallest index greater than pos such that array[index].key is at least as large as
|
||||
* min, or size if it is not possible.
|
||||
*/
|
||||
func (ra *roaringArray64) advanceUntil(min uint32, pos int) int {
|
||||
lower := pos + 1
|
||||
|
||||
@@ -401,3 +416,47 @@ func (ra *roaringArray64) serializedSizeInBytes() uint64 {
|
||||
}
|
||||
return answer
|
||||
}
|
||||
|
||||
func (ra *roaringArray64) checkKeysSorted() bool {
|
||||
if len(ra.keys) == 0 || len(ra.keys) == 1 {
|
||||
return true
|
||||
}
|
||||
previous := ra.keys[0]
|
||||
for nextIdx := 1; nextIdx < len(ra.keys); nextIdx++ {
|
||||
next := ra.keys[nextIdx]
|
||||
if previous >= next {
|
||||
return false
|
||||
}
|
||||
previous = next
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// validate checks the referential integrity
|
||||
// ensures len(keys) == len(containers), recurses and checks each container type
|
||||
func (ra *roaringArray64) validate() error {
|
||||
if !ra.checkKeysSorted() {
|
||||
return ErrKeySortOrder
|
||||
}
|
||||
|
||||
if len(ra.keys) != len(ra.containers) {
|
||||
return ErrCardinalityConstraint
|
||||
}
|
||||
|
||||
if len(ra.keys) != len(ra.needCopyOnWrite) {
|
||||
return ErrCardinalityConstraint
|
||||
}
|
||||
|
||||
for _, maps := range ra.containers {
|
||||
err := maps.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maps.IsEmpty() {
|
||||
return errors.New("empty container")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
package roaring64
|
||||
|
||||
import "github.com/RoaringBitmap/roaring"
|
||||
import "github.com/RoaringBitmap/roaring/v2"
|
||||
|
||||
func highbits(x uint64) uint32 {
|
||||
return uint32(x >> 32)
|
||||
Generated
Vendored
+74
-15
@@ -3,10 +3,11 @@ package roaring
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/RoaringBitmap/roaring/internal"
|
||||
"github.com/RoaringBitmap/roaring/v2/internal"
|
||||
)
|
||||
|
||||
type container interface {
|
||||
@@ -30,7 +31,6 @@ type container interface {
|
||||
iadd(x uint16) bool // inplace, returns true if x was new.
|
||||
iaddReturnMinimized(uint16) container // may change return type to minimize storage.
|
||||
|
||||
//addRange(start, final int) container // range is [firstOfRange,lastOfRange) (unused)
|
||||
iaddRange(start, endx int) container // i stands for inplace, range is [firstOfRange,endx)
|
||||
|
||||
iremove(x uint16) bool // inplace, returns true if x was present.
|
||||
@@ -61,7 +61,6 @@ type container interface {
|
||||
lazyOR(r container) container
|
||||
lazyIOR(r container) container
|
||||
getSizeInBytes() int
|
||||
//removeRange(start, final int) container // range is [firstOfRange,lastOfRange) (unused)
|
||||
iremoveRange(start, final int) container // i stands for inplace, range is [firstOfRange,lastOfRange)
|
||||
selectInt(x uint16) int // selectInt returns the xth integer in the container
|
||||
serializedSizeInBytes() int
|
||||
@@ -71,6 +70,14 @@ type container interface {
|
||||
toEfficientContainer() container
|
||||
String() string
|
||||
containerType() contype
|
||||
|
||||
safeMinimum() (uint16, error)
|
||||
safeMaximum() (uint16, error)
|
||||
nextValue(x uint16) int
|
||||
previousValue(x uint16) int
|
||||
nextAbsentValue(x uint16) int
|
||||
previousAbsentValue(x uint16) int
|
||||
validate() error
|
||||
}
|
||||
|
||||
type contype uint8
|
||||
@@ -82,6 +89,11 @@ const (
|
||||
run32Contype
|
||||
)
|
||||
|
||||
var (
|
||||
ErrKeySortOrder = errors.New("keys were out of order")
|
||||
ErrCardinalityConstraint = errors.New("size of arrays was not coherent")
|
||||
)
|
||||
|
||||
// careful: range is [firstOfRange,lastOfRange]
|
||||
func rangeOfOnes(start, last int) container {
|
||||
if start > MaxUint16 {
|
||||
@@ -178,7 +190,6 @@ func (ra *roaringArray) appendCopiesUntil(sa roaringArray, stoppingKey uint16) {
|
||||
} else {
|
||||
// since there is no copy-on-write, we need to clone the container (this is important)
|
||||
ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +215,6 @@ func (ra *roaringArray) appendCopiesAfter(sa roaringArray, beforeStart uint16) {
|
||||
} else {
|
||||
// since there is no copy-on-write, we need to clone the container (this is important)
|
||||
ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,7 +249,6 @@ func (ra *roaringArray) clear() {
|
||||
}
|
||||
|
||||
func (ra *roaringArray) clone() *roaringArray {
|
||||
|
||||
sa := roaringArray{}
|
||||
sa.copyOnWrite = ra.copyOnWrite
|
||||
|
||||
@@ -288,6 +297,8 @@ func (ra *roaringArray) cloneCopyOnWriteContainers() {
|
||||
// return (ra.binarySearch(0, int64(len(ra.keys)), x) >= 0)
|
||||
//}
|
||||
|
||||
// getContainer returns the container with key `x`
|
||||
// if no such container exists `nil` is returned
|
||||
func (ra *roaringArray) getContainer(x uint16) container {
|
||||
i := ra.binarySearch(0, int64(len(ra.keys)), x)
|
||||
if i < 0 {
|
||||
@@ -325,7 +336,6 @@ func (ra *roaringArray) getUnionedWritableContainer(pos int, other container) co
|
||||
return ra.getContainerAtIndex(pos).or(other)
|
||||
}
|
||||
return ra.getContainerAtIndex(pos).ior(other)
|
||||
|
||||
}
|
||||
|
||||
func (ra *roaringArray) getWritableContainerAtIndex(i int) container {
|
||||
@@ -336,7 +346,10 @@ func (ra *roaringArray) getWritableContainerAtIndex(i int) container {
|
||||
return ra.containers[i]
|
||||
}
|
||||
|
||||
// getIndex returns the index of the container with key `x`
|
||||
// if no such container exists a negative value is returned
|
||||
func (ra *roaringArray) getIndex(x uint16) int {
|
||||
// Todo : test
|
||||
// before the binary search, we optimize for frequent cases
|
||||
size := len(ra.keys)
|
||||
if (size == 0) || (ra.keys[size-1] == x) {
|
||||
@@ -396,7 +409,10 @@ func (ra *roaringArray) size() int {
|
||||
return len(ra.keys)
|
||||
}
|
||||
|
||||
// binarySearch returns the index of the key.
|
||||
// negative value returned if not found
|
||||
func (ra *roaringArray) binarySearch(begin, end int64, ikey uint16) int {
|
||||
// TODO: add unit tests
|
||||
low := begin
|
||||
high := end - 1
|
||||
for low+16 <= high {
|
||||
@@ -455,7 +471,6 @@ func (ra *roaringArray) headerSize() uint64 {
|
||||
return 4 + (size+7)/8 + 8*size // - 4 because we pack the size with the cookie
|
||||
}
|
||||
return 4 + 4 + 8*size
|
||||
|
||||
}
|
||||
|
||||
// should be dirt cheap
|
||||
@@ -489,7 +504,7 @@ func (ra *roaringArray) writeTo(w io.Writer) (n int64, err error) {
|
||||
binary.LittleEndian.PutUint16(buf[2:], uint16(len(ra.keys)-1))
|
||||
nw += 2
|
||||
// compute isRun bitmap without temporary allocation
|
||||
var runbitmapslice = buf[nw : nw+isRunSizeInBytes]
|
||||
runbitmapslice := buf[nw : nw+isRunSizeInBytes]
|
||||
for i, c := range ra.containers {
|
||||
switch c.(type) {
|
||||
case *runContainer16:
|
||||
@@ -577,7 +592,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
|
||||
// create is-run-container bitmap
|
||||
isRunBitmapSize := (int(size) + 7) / 8
|
||||
isRunBitmap, err = stream.Next(isRunBitmapSize)
|
||||
|
||||
if err != nil {
|
||||
return stream.GetReadBytes(), fmt.Errorf("malformed bitmap, failed to read is-run bitmap, got: %s", err)
|
||||
}
|
||||
@@ -596,7 +610,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
|
||||
|
||||
// descriptive header
|
||||
buf, err := stream.Next(2 * 2 * int(size))
|
||||
|
||||
if err != nil {
|
||||
return stream.GetReadBytes(), fmt.Errorf("failed to read descriptive header: %s", err)
|
||||
}
|
||||
@@ -637,13 +650,11 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
|
||||
if isRunBitmap != nil && isRunBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||
// run container
|
||||
nr, err := stream.ReadUInt16()
|
||||
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read runtime container size: %s", err)
|
||||
}
|
||||
|
||||
buf, err := stream.Next(int(nr) * 4)
|
||||
|
||||
if err != nil {
|
||||
return stream.GetReadBytes(), fmt.Errorf("failed to read runtime container content: %s", err)
|
||||
}
|
||||
@@ -656,7 +667,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
|
||||
} else if card > arrayDefaultMaxSize {
|
||||
// bitmap container
|
||||
buf, err := stream.Next(arrayDefaultMaxSize * 2)
|
||||
|
||||
if err != nil {
|
||||
return stream.GetReadBytes(), fmt.Errorf("failed to read bitmap container: %s", err)
|
||||
}
|
||||
@@ -670,7 +680,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
|
||||
} else {
|
||||
// array container
|
||||
buf, err := stream.Next(card * 2)
|
||||
|
||||
if err != nil {
|
||||
return stream.GetReadBytes(), fmt.Errorf("failed to read array container: %s", err)
|
||||
}
|
||||
@@ -696,6 +705,15 @@ func (ra *roaringArray) hasRunCompression() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the smallest integer index larger than pos such that array[index].key>=min. If none can
|
||||
* be found, return size. Based on code by O. Kaser.
|
||||
*
|
||||
* @param min minimal value
|
||||
* @param pos index to exceed
|
||||
* @return the smallest index greater than pos such that array[index].key is at least as large as
|
||||
* min, or size if it is not possible.
|
||||
*/
|
||||
func (ra *roaringArray) advanceUntil(min uint16, pos int) int {
|
||||
lower := pos + 1
|
||||
|
||||
@@ -759,3 +777,44 @@ func (ra *roaringArray) needsCopyOnWrite(i int) bool {
|
||||
func (ra *roaringArray) setNeedsCopyOnWrite(i int) {
|
||||
ra.needCopyOnWrite[i] = true
|
||||
}
|
||||
|
||||
func (ra *roaringArray) checkKeysSorted() bool {
|
||||
if len(ra.keys) == 0 || len(ra.keys) == 1 {
|
||||
return true
|
||||
}
|
||||
previous := ra.keys[0]
|
||||
for nextIdx := 1; nextIdx < len(ra.keys); nextIdx++ {
|
||||
next := ra.keys[nextIdx]
|
||||
if previous >= next {
|
||||
return false
|
||||
}
|
||||
previous = next
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// validate checks the referential integrity
|
||||
// ensures len(keys) == len(containers), recurses and checks each container type
|
||||
func (ra *roaringArray) validate() error {
|
||||
if !ra.checkKeysSorted() {
|
||||
return ErrKeySortOrder
|
||||
}
|
||||
|
||||
if len(ra.keys) != len(ra.containers) {
|
||||
return ErrCardinalityConstraint
|
||||
}
|
||||
|
||||
if len(ra.keys) != len(ra.needCopyOnWrite) {
|
||||
return ErrCardinalityConstraint
|
||||
}
|
||||
|
||||
for _, container := range ra.containers {
|
||||
err := container.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+235
-49
@@ -39,9 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// runContainer16 does run-length encoding of sets of
|
||||
@@ -59,6 +59,16 @@ type interval16 struct {
|
||||
length uint16 // length minus 1
|
||||
}
|
||||
|
||||
var (
|
||||
ErrRunIntervalsEmpty = errors.New("run contained no interval")
|
||||
ErrRunNonSorted = errors.New("runs were not sorted")
|
||||
ErrRunIntervalEqual = errors.New("intervals were equal")
|
||||
ErrRunIntervalOverlap = errors.New("intervals overlapped or were continguous")
|
||||
ErrRunIntervalSize = errors.New("too many intervals relative to data")
|
||||
MaxNumIntervals = 2048
|
||||
MaxIntervalsSum = 2048
|
||||
)
|
||||
|
||||
func newInterval16Range(start, last uint16) interval16 {
|
||||
if last < start {
|
||||
panic(fmt.Sprintf("last (%d) cannot be smaller than start (%d)", last, start))
|
||||
@@ -201,7 +211,6 @@ func newRunContainer16FromVals(alreadySorted bool, vals ...uint16) *runContainer
|
||||
// somewhat efficiently. For reference, see the Java
|
||||
// https://github.com/RoaringBitmap/RoaringBitmap/blob/master/src/main/java/org/roaringbitmap/RunContainer.java#L145-L192
|
||||
func newRunContainer16FromBitmapContainer(bc *bitmapContainer) *runContainer16 {
|
||||
|
||||
rc := &runContainer16{}
|
||||
nbrRuns := bc.numberOfRuns()
|
||||
if nbrRuns == 0 {
|
||||
@@ -251,7 +260,6 @@ func newRunContainer16FromBitmapContainer(bc *bitmapContainer) *runContainer16 {
|
||||
curWord = curWordWith1s & (curWordWith1s + 1)
|
||||
// We've lathered and rinsed, so repeat...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// newRunContainer16FromArray populates a new
|
||||
@@ -293,7 +301,6 @@ func newRunContainer16FromArray(arr *arrayContainer) *runContainer16 {
|
||||
// If you have a small number of additions to an already
|
||||
// big runContainer16, calling Add() may be faster.
|
||||
func (rc *runContainer16) set(alreadySorted bool, vals ...uint16) {
|
||||
|
||||
rc2 := newRunContainer16FromVals(alreadySorted, vals...)
|
||||
un := rc.union(rc2)
|
||||
rc.iv = un.iv
|
||||
@@ -374,7 +381,6 @@ func intersectInterval16s(a, b interval16) (res interval16, isEmpty bool) {
|
||||
// union merges two runContainer16s, producing
|
||||
// a new runContainer16 with the union of rc and b.
|
||||
func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
|
||||
|
||||
// rc is also known as 'a' here, but golint insisted we
|
||||
// call it rc for consistency with the rest of the methods.
|
||||
|
||||
@@ -457,7 +463,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
|
||||
break aAdds
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !bDone {
|
||||
@@ -471,7 +476,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
|
||||
break bAdds
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m = append(m, merged)
|
||||
@@ -489,7 +493,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
|
||||
|
||||
// unionCardinality returns the cardinality of the merger of two runContainer16s, the union of rc and b.
|
||||
func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
|
||||
|
||||
// rc is also known as 'a' here, but golint insisted we
|
||||
// call it rc for consistency with the rest of the methods.
|
||||
answer := uint(0)
|
||||
@@ -528,7 +531,7 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
|
||||
}
|
||||
if !mergedUpdated {
|
||||
// we know that merged is disjoint from cura and curb
|
||||
//m = append(m, merged)
|
||||
// m = append(m, merged)
|
||||
answer += uint(merged.last()) - uint(merged.start) + 1
|
||||
mergedUsed = false
|
||||
}
|
||||
@@ -539,11 +542,11 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
|
||||
if !canMerge16(cura, curb) {
|
||||
if cura.start < curb.start {
|
||||
answer += uint(cura.last()) - uint(cura.start) + 1
|
||||
//m = append(m, cura)
|
||||
// m = append(m, cura)
|
||||
na++
|
||||
} else {
|
||||
answer += uint(curb.last()) - uint(curb.start) + 1
|
||||
//m = append(m, curb)
|
||||
// m = append(m, curb)
|
||||
nb++
|
||||
}
|
||||
} else {
|
||||
@@ -574,7 +577,6 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
|
||||
break aAdds
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !bDone {
|
||||
@@ -588,10 +590,9 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
|
||||
break bAdds
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//m = append(m, merged)
|
||||
// m = append(m, merged)
|
||||
answer += uint(merged.last()) - uint(merged.start) + 1
|
||||
}
|
||||
for _, r := range rc.iv[na:] {
|
||||
@@ -615,7 +616,6 @@ func (rc *runContainer16) indexOfIntervalAtOrAfter(key int, startIndex int) int
|
||||
// intersect returns a new runContainer16 holding the
|
||||
// intersection of rc (also known as 'a') and b.
|
||||
func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 {
|
||||
|
||||
a := rc
|
||||
numa := int(len(a.iv))
|
||||
numb := int(len(b.iv))
|
||||
@@ -645,8 +645,7 @@ func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 {
|
||||
toploop:
|
||||
for acuri < numa && bcuri < numb {
|
||||
|
||||
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
|
||||
intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
|
||||
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
|
||||
|
||||
if !isOverlap {
|
||||
switch {
|
||||
@@ -664,7 +663,6 @@ toploop:
|
||||
}
|
||||
bstart = int(b.iv[bcuri].start)
|
||||
}
|
||||
|
||||
} else {
|
||||
// isOverlap
|
||||
output = append(output, intersection)
|
||||
@@ -748,8 +746,7 @@ toploop:
|
||||
for acuri < numa && bcuri < numb {
|
||||
pass++
|
||||
|
||||
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
|
||||
intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
|
||||
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
|
||||
|
||||
if !isOverlap {
|
||||
switch {
|
||||
@@ -767,7 +764,6 @@ toploop:
|
||||
}
|
||||
bstart = int(b.iv[bcuri].start)
|
||||
}
|
||||
|
||||
} else {
|
||||
// isOverlap
|
||||
answer += int(intersection.last()) - int(intersection.start) + 1
|
||||
@@ -941,7 +937,7 @@ func (rc *runContainer16) searchRange(key int, startIndex int, endxIndex int) (w
|
||||
// b) whichInterval16 == -1 if key is before our first
|
||||
// interval16 in rc.iv;
|
||||
//
|
||||
// c) whichInterval16 is set to the minimum index of rc.iv
|
||||
// c) whichInterval16 is set to the maximum index of rc.iv
|
||||
// which comes strictly before the key;
|
||||
// so rc.iv[whichInterval16].last < key,
|
||||
// and if whichInterval16+1 exists, then key < rc.iv[whichInterval16+1].start
|
||||
@@ -1014,10 +1010,10 @@ func newRunContainer16TakeOwnership(iv []interval16) *runContainer16 {
|
||||
return rc
|
||||
}
|
||||
|
||||
const baseRc16Size = int(unsafe.Sizeof(runContainer16{}))
|
||||
const perIntervalRc16Size = int(unsafe.Sizeof(interval16{}))
|
||||
|
||||
const baseDiskRc16Size = int(unsafe.Sizeof(uint16(0)))
|
||||
const (
|
||||
baseRc16Size = 2
|
||||
perIntervalRc16Size = 4
|
||||
)
|
||||
|
||||
// see also runContainer16SerializedSizeInBytes(numRuns int) int
|
||||
|
||||
@@ -1030,7 +1026,7 @@ func (rc *runContainer16) getSizeInBytes() int {
|
||||
// runContainer16SerializedSizeInBytes returns the number of bytes of disk
|
||||
// required to hold numRuns in a runContainer16.
|
||||
func runContainer16SerializedSizeInBytes(numRuns int) int {
|
||||
return perIntervalRc16Size*numRuns + baseDiskRc16Size
|
||||
return perIntervalRc16Size*numRuns + baseRc16Size
|
||||
}
|
||||
|
||||
// Add adds a single value k to the set.
|
||||
@@ -1274,7 +1270,7 @@ func (ri *runIterator16) nextMany(hs uint32, buf []uint32) int {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
ri.curPosInIndex += uint16(moreVals) //moreVals always fits in uint16
|
||||
ri.curPosInIndex += uint16(moreVals) // moreVals always fits in uint16
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1315,7 +1311,7 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
ri.curPosInIndex += uint16(moreVals) //moreVals always fits in uint16
|
||||
ri.curPosInIndex += uint16(moreVals) // moreVals always fits in uint16
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,7 +1320,6 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int {
|
||||
|
||||
// remove removes key from the container.
|
||||
func (rc *runContainer16) removeKey(key uint16) (wasPresent bool) {
|
||||
|
||||
var index int
|
||||
index, wasPresent, _ = rc.search(int(key))
|
||||
if !wasPresent {
|
||||
@@ -1361,7 +1356,7 @@ func (rc *runContainer16) deleteAt(curIndex *int, curPosInIndex *uint16) {
|
||||
*curPosInIndex--
|
||||
// if we leave *curIndex alone, then Next() will work properly even after the delete.
|
||||
default:
|
||||
//middle
|
||||
// middle
|
||||
// split into two, adding an interval16
|
||||
new0 := newInterval16Range(rc.iv[ci].start, rc.iv[ci].start+*curPosInIndex-1)
|
||||
|
||||
@@ -1376,7 +1371,6 @@ func (rc *runContainer16) deleteAt(curIndex *int, curPosInIndex *uint16) {
|
||||
*curIndex++
|
||||
*curPosInIndex = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func have4Overlap16(astart, alast, bstart, blast int) bool {
|
||||
@@ -1503,6 +1497,26 @@ func (iv interval16) isSuperSetOf(b interval16) bool {
|
||||
return iv.start <= b.start && b.last() <= iv.last()
|
||||
}
|
||||
|
||||
func (iv interval16) isNonContiguousDisjoint(b interval16) bool {
|
||||
// cover the zero start case
|
||||
if iv.start == b.start {
|
||||
return false
|
||||
}
|
||||
|
||||
nonContiguous1 := uint32(iv.start) == uint32(b.last())+1 || uint32(iv.last()) == uint32(b.start)+1
|
||||
nonContiguous2 := uint32(b.start) == uint32(iv.last())+1 || uint32(b.last()) == uint32(iv.start)+1
|
||||
if nonContiguous1 || nonContiguous2 {
|
||||
return false
|
||||
}
|
||||
ivl := iv.last()
|
||||
bl := b.last()
|
||||
|
||||
c1 := iv.start <= b.start && b.start <= ivl
|
||||
c2 := b.start <= iv.start && iv.start <= bl
|
||||
|
||||
return !c1 && !c2
|
||||
}
|
||||
|
||||
func (iv interval16) subtractInterval(del interval16) (left []interval16, delcount int) {
|
||||
isect, isEmpty := intersectInterval16s(iv, del)
|
||||
|
||||
@@ -1678,7 +1692,6 @@ func (rc *runContainer16) isubtract(del interval16) {
|
||||
// port of run_container_andnot from CRoaring...
|
||||
// https://github.com/RoaringBitmap/CRoaring/blob/master/src/containers/run.c#L435-L496
|
||||
func (rc *runContainer16) AndNotRunContainer16(b *runContainer16) *runContainer16 {
|
||||
|
||||
if len(b.iv) == 0 || len(rc.iv) == 0 {
|
||||
return rc
|
||||
}
|
||||
@@ -1781,10 +1794,25 @@ func (rc *runContainer16) minimum() uint16 {
|
||||
return rc.iv[0].start // assume not empty
|
||||
}
|
||||
|
||||
func (rc *runContainer16) safeMinimum() (uint16, error) {
|
||||
if len(rc.iv) == 0 {
|
||||
return 0, errors.New("Empty runs")
|
||||
}
|
||||
|
||||
return rc.minimum(), nil
|
||||
}
|
||||
|
||||
func (rc *runContainer16) maximum() uint16 {
|
||||
return rc.iv[len(rc.iv)-1].last() // assume not empty
|
||||
}
|
||||
|
||||
func (rc *runContainer16) safeMaximum() (uint16, error) {
|
||||
if len(rc.iv) == 0 {
|
||||
return 0, errors.New("Empty runs")
|
||||
}
|
||||
return rc.maximum(), nil // assume not empty
|
||||
}
|
||||
|
||||
func (rc *runContainer16) isFull() bool {
|
||||
return (len(rc.iv) == 1) && ((rc.iv[0].start == 0) && (rc.iv[0].last() == MaxUint16))
|
||||
}
|
||||
@@ -1949,7 +1977,6 @@ func (rc *runContainer16) getManyIterator() manyIterable {
|
||||
// add the values in the range [firstOfRange, endx). endx
|
||||
// is still abe to express 2^16 because it is an int not an uint16.
|
||||
func (rc *runContainer16) iaddRange(firstOfRange, endx int) container {
|
||||
|
||||
if firstOfRange > endx {
|
||||
panic(fmt.Sprintf("invalid %v = endx > firstOfRange", endx))
|
||||
}
|
||||
@@ -2002,7 +2029,6 @@ func (rc *runContainer16) not(firstOfRange, endx int) container {
|
||||
// makes 2 more passes through the arrays than should be
|
||||
// strictly necessary. Measure both ways though--this may not matter.
|
||||
func (rc *runContainer16) Not(firstOfRange, endx int) *runContainer16 {
|
||||
|
||||
if firstOfRange > endx {
|
||||
panic(fmt.Sprintf("invalid %v = endx > firstOfRange == %v", endx, firstOfRange))
|
||||
}
|
||||
@@ -2066,12 +2092,12 @@ func (rc *runContainer16) equals(o container) bool {
|
||||
rit := rc.getShortIterator()
|
||||
bit := o.getShortIterator()
|
||||
|
||||
//k := 0
|
||||
// k := 0
|
||||
for rit.hasNext() {
|
||||
if bit.next() != rit.next() {
|
||||
return false
|
||||
}
|
||||
//k++
|
||||
// k++
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -2132,7 +2158,7 @@ func (rc *runContainer16) andBitmapContainerCardinality(bc *bitmapContainer) int
|
||||
for i := range rc.iv {
|
||||
answer += bc.getCardinalityInRange(uint(rc.iv[i].start), uint(rc.iv[i].last())+1)
|
||||
}
|
||||
//bc.computeCardinality()
|
||||
// bc.computeCardinality()
|
||||
return answer
|
||||
}
|
||||
|
||||
@@ -2150,7 +2176,7 @@ func (rc *runContainer16) orArray(ac *arrayContainer) container {
|
||||
}
|
||||
intervals, cardMinusOne := runArrayUnionToRuns(rc, ac)
|
||||
result := newRunContainer16TakeOwnership(intervals)
|
||||
if len(intervals) >= 2048 && cardMinusOne >= arrayDefaultMaxSize {
|
||||
if len(intervals) >= MaxNumIntervals && cardMinusOne >= arrayDefaultMaxSize {
|
||||
return newBitmapContainerFromRun(result)
|
||||
}
|
||||
if len(intervals)*2 > 1+int(cardMinusOne) {
|
||||
@@ -2190,7 +2216,6 @@ func (rc *runContainer16) inplaceUnion(rc2 *runContainer16) container {
|
||||
}
|
||||
|
||||
func (rc *runContainer16) iorBitmapContainer(bc *bitmapContainer) container {
|
||||
|
||||
it := bc.getShortIterator()
|
||||
for it.hasNext() {
|
||||
rc.Add(it.next())
|
||||
@@ -2206,11 +2231,11 @@ func (rc *runContainer16) iorArray(ac *arrayContainer) container {
|
||||
return rc
|
||||
}
|
||||
var cardMinusOne uint16
|
||||
//TODO: perform the union algorithm in-place using rc.iv
|
||||
// TODO: perform the union algorithm in-place using rc.iv
|
||||
// this can be done with methods like the in-place array container union
|
||||
// but maybe lazily moving the remaining elements back.
|
||||
rc.iv, cardMinusOne = runArrayUnionToRuns(rc, ac)
|
||||
if len(rc.iv) >= 2048 && cardMinusOne >= arrayDefaultMaxSize {
|
||||
if len(rc.iv) >= MaxNumIntervals && cardMinusOne >= arrayDefaultMaxSize {
|
||||
return newBitmapContainerFromRun(rc)
|
||||
}
|
||||
if len(rc.iv)*2 > 1+int(cardMinusOne) {
|
||||
@@ -2438,12 +2463,8 @@ func (rc *runContainer16) toBitmapContainer() *bitmapContainer {
|
||||
}
|
||||
|
||||
func (rc *runContainer16) iandNotRunContainer16(x2 *runContainer16) container {
|
||||
rcb := rc.toBitmapContainer()
|
||||
x2b := x2.toBitmapContainer()
|
||||
rcb.iandNotBitmapSurely(x2b)
|
||||
// TODO: check size and optimize the return value
|
||||
// TODO: is inplace modification really required? If not, elide the copy.
|
||||
rc2 := newRunContainer16FromBitmapContainer(rcb)
|
||||
rc2 := rc.AndNotRunContainer16(x2)
|
||||
*rc = *rc2
|
||||
return rc
|
||||
}
|
||||
@@ -2492,7 +2513,7 @@ func (rc *runContainer16) toEfficientContainer() container {
|
||||
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
|
||||
card := rc.getCardinality()
|
||||
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
|
||||
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
return rc
|
||||
}
|
||||
if card <= arrayDefaultMaxSize {
|
||||
@@ -2511,7 +2532,6 @@ func (rc *runContainer16) toArrayContainer() *arrayContainer {
|
||||
}
|
||||
|
||||
func newRunContainer16FromContainer(c container) *runContainer16 {
|
||||
|
||||
switch x := c.(type) {
|
||||
case *runContainer16:
|
||||
return x.Clone()
|
||||
@@ -2622,3 +2642,169 @@ func (rc *runContainer16) addOffset(x uint16) (container, container) {
|
||||
|
||||
return low, high
|
||||
}
|
||||
|
||||
// nextValue returns either the `target` if found or the next larger value.
|
||||
// If the target is in the interior or a run then `target` will be returned
|
||||
// Ex: If our run structure resmembles [[a,c], [d,f]] with a <= target <= c then `target` will be returned.
|
||||
// Ex: If c < target < d then d is returned.
|
||||
// Ex: If target < a then a is returned
|
||||
// if the target > max, this is out of bounds and -1 is returned
|
||||
func (rc *runContainer16) nextValue(target uint16) int {
|
||||
if len(rc.iv) == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
whichIndex, alreadyPresent, _ := rc.search(int(target))
|
||||
|
||||
if alreadyPresent {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
if whichIndex == -1 {
|
||||
return int(rc.iv[0].start)
|
||||
}
|
||||
|
||||
if whichIndex == len(rc.iv)-1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// The if relies on the non-contiguous nature of runs.
|
||||
// If we have two runs [a,b] and another run [c,d]
|
||||
// We can rely on the invariant that b+1 < c
|
||||
// We will return c
|
||||
possibleNext := whichIndex + 1
|
||||
if possibleNext < len(rc.iv) {
|
||||
return int(rc.iv[possibleNext].start)
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// nextAbsentValue returns the next absent value.
|
||||
// By construction the next absent value will be located between gaps in runs
|
||||
//
|
||||
// Ex: if our runs resemble [[a,b],[c,d]] and a <= target <= b then b+1 will not be equal to c, b+1 will be returned
|
||||
// Ex: if target < a then target is returned
|
||||
// Ex: if target > d then target is returned
|
||||
func (rc *runContainer16) nextAbsentValue(target uint16) int {
|
||||
whichIndex, alreadyPresent, _ := rc.search(int(target))
|
||||
|
||||
if !alreadyPresent {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
return int(rc.iv[whichIndex].last()) + 1
|
||||
}
|
||||
|
||||
// previousValue will return the previous present value
|
||||
// If the target is in the interior of a run then `target` will be returned
|
||||
//
|
||||
// Example:
|
||||
// If our run structure resmembles [[a,c], [d,f]] with a <= target <= c then target will be returned.
|
||||
// If c < target < d then c is returned.
|
||||
// if target > f then f is returned
|
||||
// if the target is less than a, this is out of bounds and -1 is returned
|
||||
func (rc *runContainer16) previousValue(target uint16) int {
|
||||
whichIndex, alreadyPresent, _ := rc.search(int(target))
|
||||
|
||||
if len(rc.iv) == 0 {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
if alreadyPresent {
|
||||
return int(target)
|
||||
}
|
||||
if whichIndex == -1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(rc.iv[whichIndex].last())
|
||||
}
|
||||
|
||||
// previousAbsentValue will return the previous absent value
|
||||
// If the target is in the interior of a run then then the start of the range minus 1 will be returned
|
||||
//
|
||||
// Example:
|
||||
// If our run structure resmembles [[x,z], [a,c], [d,f]] with a <= target <= c then a-1 will be returned.
|
||||
// if the target < x then target is returned
|
||||
// if target > f then target is returned
|
||||
func (rc *runContainer16) previousAbsentValue(target uint16) int {
|
||||
whichIndex, alreadyPresent, _ := rc.search(int(target))
|
||||
|
||||
if !alreadyPresent {
|
||||
return int(target)
|
||||
}
|
||||
|
||||
return int(rc.iv[whichIndex].start) - 1
|
||||
}
|
||||
|
||||
// isNonContiguousDisjoint returns an error if the intervals overlap e.g have non-empty intersection
|
||||
func isNonContiguousDisjoint(outer interval16, inner interval16) error {
|
||||
if !outer.isNonContiguousDisjoint(inner) {
|
||||
return ErrRunIntervalOverlap
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate checks the run container referential integrity
|
||||
// Ensures runs are not degenerate, non-contiguous and non-overlapping
|
||||
func (rc *runContainer16) validate() error {
|
||||
if rc.getCardinality() == 0 {
|
||||
return ErrRunIntervalsEmpty
|
||||
}
|
||||
|
||||
intervalsSum := 0
|
||||
for outeridx := range rc.iv {
|
||||
// The length being stored is the actual length - 1.
|
||||
// So we need to add 1 to get the actual length.
|
||||
// It is not possible to have a run with length 0.
|
||||
|
||||
outerInterval := rc.iv[outeridx]
|
||||
|
||||
intervalsSum += outerInterval.runlen()
|
||||
for inneridx := outeridx + 1; inneridx < len(rc.iv); inneridx++ {
|
||||
|
||||
innerInterval := rc.iv[inneridx]
|
||||
|
||||
if outerInterval.equal(innerInterval) {
|
||||
return ErrRunIntervalEqual
|
||||
}
|
||||
|
||||
// only check the start of runs
|
||||
// if the run length overlap the next check will catch that.
|
||||
if outerInterval.start >= innerInterval.start {
|
||||
return ErrRunNonSorted
|
||||
}
|
||||
|
||||
err := isNonContiguousDisjoint(outerInterval, innerInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
if number of distinct values in the container >= 2048 then
|
||||
check that the number of runs is no more than 2047
|
||||
(otherwise you could use a bitset container)
|
||||
else
|
||||
check that the number of runs < (number of distinct values) / 2
|
||||
(otherwise you could use an array container)
|
||||
*/
|
||||
|
||||
sizeAsRunContainer := runContainer16SerializedSizeInBytes(len(rc.iv))
|
||||
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
|
||||
sizeAsArrayContainer := arrayContainerSizeInBytes(intervalsSum)
|
||||
// this is always ok:
|
||||
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
|
||||
return nil
|
||||
}
|
||||
if sizeAsRunContainer >= sizeAsBitmapContainer {
|
||||
return ErrRunIntervalSize
|
||||
}
|
||||
if sizeAsRunContainer >= sizeAsArrayContainer {
|
||||
return ErrRunIntervalSize
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
+9
@@ -299,6 +299,15 @@ func (rb *Bitmap) FrozenView(buf []byte) error {
|
||||
return rb.highlowcontainer.frozenView(buf)
|
||||
}
|
||||
|
||||
func (rb *Bitmap) MustFrozenView(buf []byte) error {
|
||||
if err := rb.FrozenView(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
err := rb.Validate()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
/* Verbatim specification from CRoaring.
|
||||
*
|
||||
* FROZEN SERIALIZATION FORMAT DESCRIPTION
|
||||
Generated
Vendored
Generated
Vendored
+188
-73
@@ -1,26 +1,12 @@
|
||||
package roaring
|
||||
|
||||
func equal(a, b []uint16) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func difference(set1 []uint16, set2 []uint16, buffer []uint16) int {
|
||||
if 0 == len(set2) {
|
||||
if len(set2) == 0 {
|
||||
buffer = buffer[:len(set1)]
|
||||
for k := 0; k < len(set1); k++ {
|
||||
buffer[k] = set1[k]
|
||||
}
|
||||
copy(buffer, set1)
|
||||
return len(set1)
|
||||
}
|
||||
if 0 == len(set1) {
|
||||
if len(set1) == 0 {
|
||||
return 0
|
||||
}
|
||||
pos := 0
|
||||
@@ -66,7 +52,6 @@ func difference(set1 []uint16, set2 []uint16, buffer []uint16) int {
|
||||
}
|
||||
}
|
||||
return pos
|
||||
|
||||
}
|
||||
|
||||
func exclusiveUnion2by2(set1 []uint16, set2 []uint16, buffer []uint16) int {
|
||||
@@ -135,6 +120,7 @@ func exclusiveUnion2by2(set1 []uint16, set2 []uint16, buffer []uint16) int {
|
||||
return pos
|
||||
}
|
||||
|
||||
// union2by2Cardinality computes the cardinality of the union
|
||||
func union2by2Cardinality(set1 []uint16, set2 []uint16) int {
|
||||
pos := 0
|
||||
k1 := 0
|
||||
@@ -186,8 +172,8 @@ func union2by2Cardinality(set1 []uint16, set2 []uint16) int {
|
||||
func intersection2by2(
|
||||
set1 []uint16,
|
||||
set2 []uint16,
|
||||
buffer []uint16) int {
|
||||
|
||||
buffer []uint16,
|
||||
) int {
|
||||
if len(set1)*64 < len(set2) {
|
||||
return onesidedgallopingintersect2by2(set1, set2, buffer)
|
||||
} else if len(set2)*64 < len(set1) {
|
||||
@@ -197,10 +183,11 @@ func intersection2by2(
|
||||
}
|
||||
}
|
||||
|
||||
// intersection2by2Cardinality computes the cardinality of the intersection
|
||||
func intersection2by2Cardinality(
|
||||
set1 []uint16,
|
||||
set2 []uint16) int {
|
||||
|
||||
set2 []uint16,
|
||||
) int {
|
||||
if len(set1)*64 < len(set2) {
|
||||
return onesidedgallopingintersect2by2Cardinality(set1, set2)
|
||||
} else if len(set2)*64 < len(set1) {
|
||||
@@ -210,44 +197,45 @@ func intersection2by2Cardinality(
|
||||
}
|
||||
}
|
||||
|
||||
// intersects2by2 computes whether the two sets intersect
|
||||
func intersects2by2(
|
||||
set1 []uint16,
|
||||
set2 []uint16) bool {
|
||||
set2 []uint16,
|
||||
) bool {
|
||||
// could be optimized if one set is much larger than the other one
|
||||
if (0 == len(set1)) || (0 == len(set2)) {
|
||||
if (len(set1) == 0) || (len(set2) == 0) {
|
||||
return false
|
||||
}
|
||||
k1 := 0
|
||||
k2 := 0
|
||||
s1 := set1[k1]
|
||||
s2 := set2[k2]
|
||||
index1 := 0
|
||||
index2 := 0
|
||||
value1 := set1[index1]
|
||||
value2 := set2[index2]
|
||||
mainwhile:
|
||||
for {
|
||||
|
||||
if s2 < s1 {
|
||||
if value2 < value1 {
|
||||
for {
|
||||
k2++
|
||||
if k2 == len(set2) {
|
||||
index2++
|
||||
if index2 == len(set2) {
|
||||
break mainwhile
|
||||
}
|
||||
s2 = set2[k2]
|
||||
if s2 >= s1 {
|
||||
value2 = set2[index2]
|
||||
if value2 >= value1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if s1 < s2 {
|
||||
if value1 < value2 {
|
||||
for {
|
||||
k1++
|
||||
if k1 == len(set1) {
|
||||
index1++
|
||||
if index1 == len(set1) {
|
||||
break mainwhile
|
||||
}
|
||||
s1 = set1[k1]
|
||||
if s1 >= s2 {
|
||||
value1 = set1[index1]
|
||||
if value1 >= value2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// (set2[k2] == set1[k1])
|
||||
return true
|
||||
@@ -259,9 +247,9 @@ mainwhile:
|
||||
func localintersect2by2(
|
||||
set1 []uint16,
|
||||
set2 []uint16,
|
||||
buffer []uint16) int {
|
||||
|
||||
if (0 == len(set1)) || (0 == len(set2)) {
|
||||
buffer []uint16,
|
||||
) int {
|
||||
if (len(set1) == 0) || (len(set2) == 0) {
|
||||
return 0
|
||||
}
|
||||
k1 := 0
|
||||
@@ -295,7 +283,6 @@ mainwhile:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// (set2[k2] == set1[k1])
|
||||
buffer[pos] = s1
|
||||
@@ -315,57 +302,57 @@ mainwhile:
|
||||
return pos
|
||||
}
|
||||
|
||||
// / localintersect2by2Cardinality computes the cardinality of the intersection
|
||||
func localintersect2by2Cardinality(
|
||||
set1 []uint16,
|
||||
set2 []uint16) int {
|
||||
|
||||
if (0 == len(set1)) || (0 == len(set2)) {
|
||||
set2 []uint16,
|
||||
) int {
|
||||
if (len(set1) == 0) || (len(set2) == 0) {
|
||||
return 0
|
||||
}
|
||||
k1 := 0
|
||||
k2 := 0
|
||||
index1 := 0
|
||||
index2 := 0
|
||||
pos := 0
|
||||
s1 := set1[k1]
|
||||
s2 := set2[k2]
|
||||
value1 := set1[index1]
|
||||
value2 := set2[index2]
|
||||
mainwhile:
|
||||
for {
|
||||
if s2 < s1 {
|
||||
if value2 < value1 {
|
||||
for {
|
||||
k2++
|
||||
if k2 == len(set2) {
|
||||
index2++
|
||||
if index2 == len(set2) {
|
||||
break mainwhile
|
||||
}
|
||||
s2 = set2[k2]
|
||||
if s2 >= s1 {
|
||||
value2 = set2[index2]
|
||||
if value2 >= value1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if s1 < s2 {
|
||||
if value1 < value2 {
|
||||
for {
|
||||
k1++
|
||||
if k1 == len(set1) {
|
||||
index1++
|
||||
if index1 == len(set1) {
|
||||
break mainwhile
|
||||
}
|
||||
s1 = set1[k1]
|
||||
if s1 >= s2 {
|
||||
value1 = set1[index1]
|
||||
if value1 >= value2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// (set2[k2] == set1[k1])
|
||||
pos++
|
||||
k1++
|
||||
if k1 == len(set1) {
|
||||
index1++
|
||||
if index1 == len(set1) {
|
||||
break
|
||||
}
|
||||
s1 = set1[k1]
|
||||
k2++
|
||||
if k2 == len(set2) {
|
||||
value1 = set1[index1]
|
||||
index2++
|
||||
if index2 == len(set2) {
|
||||
break
|
||||
}
|
||||
s2 = set2[k2]
|
||||
value2 = set2[index2]
|
||||
}
|
||||
}
|
||||
return pos
|
||||
@@ -375,7 +362,8 @@ func advanceUntil(
|
||||
array []uint16,
|
||||
pos int,
|
||||
length int,
|
||||
min uint16) int {
|
||||
min uint16,
|
||||
) int {
|
||||
lower := pos + 1
|
||||
|
||||
if lower >= length || array[lower] >= min {
|
||||
@@ -423,14 +411,13 @@ func advanceUntil(
|
||||
}
|
||||
}
|
||||
return upper
|
||||
|
||||
}
|
||||
|
||||
func onesidedgallopingintersect2by2(
|
||||
smallset []uint16,
|
||||
largeset []uint16,
|
||||
buffer []uint16) int {
|
||||
|
||||
buffer []uint16,
|
||||
) int {
|
||||
if 0 == len(smallset) {
|
||||
return 0
|
||||
}
|
||||
@@ -478,8 +465,8 @@ mainwhile:
|
||||
|
||||
func onesidedgallopingintersect2by2Cardinality(
|
||||
smallset []uint16,
|
||||
largeset []uint16) int {
|
||||
|
||||
largeset []uint16,
|
||||
) int {
|
||||
if 0 == len(smallset) {
|
||||
return 0
|
||||
}
|
||||
@@ -548,3 +535,131 @@ func binarySearch(array []uint16, ikey uint16) int {
|
||||
}
|
||||
return -(low + 1)
|
||||
}
|
||||
|
||||
// searchResult provides information about a search request.
|
||||
// The values will depend on the context of the search
|
||||
type searchResult struct {
|
||||
value uint16
|
||||
index int
|
||||
exactMatch bool
|
||||
}
|
||||
|
||||
// notFound returns a bool depending the search context
|
||||
// For cases `previousValue` and `nextValue` if target is present in the slice
|
||||
// this function will return `true` otherwise `false`
|
||||
// For `nextAbsentValue` and `previousAbsentValue` this will only return `False`
|
||||
func (sr *searchResult) notFound() bool {
|
||||
return !sr.exactMatch
|
||||
}
|
||||
|
||||
// outOfBounds indicates whether the target was outside the lower and upper bounds of the container
|
||||
func (sr *searchResult) outOfBounds() bool {
|
||||
return sr.index <= -1
|
||||
}
|
||||
|
||||
// binarySearchUntil is a helper function around binarySearchUntilWithBounds
|
||||
// The user does not have to pass in the lower and upper bound
|
||||
// The lower bound is taken to be `0` and the upper bound `len(array)-1`
|
||||
func binarySearchUntil(array []uint16, target uint16) searchResult {
|
||||
return binarySearchUntilWithBounds(array, target, 0, len(array)-1)
|
||||
}
|
||||
|
||||
// binarySearchUntilWithBounds returns a `searchResult`.
|
||||
// If an exact match is found the `searchResult{target, <index>, true}` will be returned, where `<index>` is
|
||||
// `target`s index in `array`, and `result.notFound()` evaluates to `false`.
|
||||
// If a match is not found, but `target` was in-bounds then the result.index will be the closest smaller value
|
||||
// Example: [ 8,9,11,12] if the target was 10, then `searchResult{9, 1, false}` will be returned.
|
||||
// If `target` was out of bounds `searchResult{0, -1, false}` will be returned.
|
||||
func binarySearchUntilWithBounds(array []uint16, target uint16, lowIndex int, maxIndex int) searchResult {
|
||||
highIndex := maxIndex
|
||||
|
||||
closestIndex := -1
|
||||
|
||||
if target < array[lowIndex] {
|
||||
return searchResult{0, closestIndex, false}
|
||||
}
|
||||
|
||||
if target > array[maxIndex] {
|
||||
return searchResult{0, len(array), false}
|
||||
}
|
||||
|
||||
for lowIndex <= highIndex {
|
||||
middleIndex := (lowIndex + highIndex) / 2
|
||||
middleValue := array[middleIndex]
|
||||
|
||||
if middleValue == target {
|
||||
return searchResult{middleValue, middleIndex, true}
|
||||
}
|
||||
|
||||
if target < middleValue {
|
||||
|
||||
if middleIndex > 0 && target > array[middleIndex-1] {
|
||||
return searchResult{array[middleIndex-1], middleIndex - 1, false}
|
||||
}
|
||||
|
||||
highIndex = middleIndex
|
||||
} else {
|
||||
if middleIndex < maxIndex && target < array[middleIndex+1] {
|
||||
return searchResult{middleValue, middleIndex, false}
|
||||
}
|
||||
lowIndex = middleIndex + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return searchResult{array[closestIndex], closestIndex, false}
|
||||
}
|
||||
|
||||
// binarySearchPast is a wrapper around binarySearchPastWithBounds
|
||||
// The user does not have to pass in the lower and upper bound
|
||||
// The lower bound is taken to be `0` and the upper bound `len(array)-1`
|
||||
func binarySearchPast(array []uint16, target uint16) searchResult {
|
||||
return binarySearchPastWithBounds(array, target, 0, len(array)-1)
|
||||
}
|
||||
|
||||
// binarySearchPastWithBounds looks for the smallest value larger than or equal to `target`
|
||||
// If `target` is out of bounds a `searchResult` indicating out of bounds is returned
|
||||
// `target` does not have to exist in the slice.
|
||||
//
|
||||
// Example:
|
||||
// Suppose the slice is [...10,13...] with `target` equal to 11
|
||||
// The searchResult will have searchResult.value = 13
|
||||
func binarySearchPastWithBounds(array []uint16, target uint16, lowIndex int, maxIndex int) searchResult {
|
||||
highIndex := maxIndex
|
||||
|
||||
closestIndex := -1
|
||||
|
||||
if target < array[lowIndex] {
|
||||
return searchResult{0, closestIndex, false}
|
||||
}
|
||||
|
||||
if target > array[maxIndex] {
|
||||
return searchResult{0, len(array), false}
|
||||
}
|
||||
|
||||
for lowIndex <= highIndex {
|
||||
middleIndex := (lowIndex + highIndex) / 2
|
||||
middleValue := array[middleIndex]
|
||||
|
||||
if middleValue == target {
|
||||
return searchResult{middleValue, middleIndex, true}
|
||||
}
|
||||
|
||||
if target < middleValue {
|
||||
|
||||
if middleIndex > 0 && target > array[middleIndex-1] {
|
||||
return searchResult{array[middleIndex], middleIndex, false}
|
||||
}
|
||||
|
||||
highIndex = middleIndex
|
||||
} else {
|
||||
if middleIndex < maxIndex && target < array[middleIndex+1] {
|
||||
return searchResult{array[middleIndex+1], middleIndex + 1, false}
|
||||
}
|
||||
lowIndex = middleIndex + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return searchResult{array[closestIndex], closestIndex, false}
|
||||
}
|
||||
Generated
Vendored
Generated
Vendored
Generated
Vendored
Generated
Vendored
vendor/github.com/RoaringBitmap/roaring/util.go → vendor/github.com/RoaringBitmap/roaring/v2/util.go
Generated
Vendored
+10
-2
@@ -52,6 +52,7 @@ func fill(arr []uint64, val uint64) {
|
||||
arr[i] = val
|
||||
}
|
||||
}
|
||||
|
||||
func fillRange(arr []uint64, start, end int, val uint64) {
|
||||
for i := start; i < end; i++ {
|
||||
arr[i] = val
|
||||
@@ -112,10 +113,19 @@ func fillArrayXOR(container []uint16, bitmap1, bitmap2 []uint64) {
|
||||
func highbits(x uint32) uint16 {
|
||||
return uint16(x >> 16)
|
||||
}
|
||||
|
||||
func lowbits(x uint32) uint16 {
|
||||
return uint16(x & maxLowBit)
|
||||
}
|
||||
|
||||
func combineLoHi16(lob uint16, hob uint16) uint32 {
|
||||
return combineLoHi32(uint32(lob), uint32(hob))
|
||||
}
|
||||
|
||||
func combineLoHi32(lob uint32, hob uint32) uint32 {
|
||||
return uint32(lob) | (hob << 16)
|
||||
}
|
||||
|
||||
const maxLowBit = 0xFFFF
|
||||
|
||||
func flipBitmapRange(bitmap []uint64, start int, end int) {
|
||||
@@ -146,7 +156,6 @@ func resetBitmapRange(bitmap []uint64, start int, end int) {
|
||||
bitmap[i] = 0
|
||||
}
|
||||
bitmap[endword] &= ^(^uint64(0) >> (uint(-end) % 64))
|
||||
|
||||
}
|
||||
|
||||
func setBitmapRange(bitmap []uint64, start int, end int) {
|
||||
@@ -242,7 +251,6 @@ func selectBitPosition(w uint64, j int) int {
|
||||
}
|
||||
}
|
||||
return seen + int(counter)
|
||||
|
||||
}
|
||||
|
||||
func panicOn(err error) {
|
||||
Reference in New Issue
Block a user