mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-25 15:53:36 +01:00
1747 lines
44 KiB
Go
1747 lines
44 KiB
Go
package roaring
|
|
|
|
//
|
|
// Copyright (c) 2016 by the roaring authors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
//
|
|
// We derive a few lines of code from the sort.Search
|
|
// function in the golang standard library. That function
|
|
// is Copyright 2009 The Go Authors, and licensed
|
|
// under the following BSD-style license.
|
|
/*
|
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the
|
|
distribution.
|
|
* Neither the name of Google Inc. nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"unsafe"
|
|
)
|
|
|
|
//go:generate msgp -unexported
|
|
|
|
// runContainer16 does run-length encoding of sets of
|
|
// uint16 integers.
|
|
type runContainer16 struct {
|
|
iv []interval16
|
|
card int64
|
|
|
|
// avoid allocation during search
|
|
myOpts searchOptions `msg:"-"`
|
|
}
|
|
|
|
// interval16 is the internal to runContainer16
|
|
// structure that maintains the individual [start, last]
|
|
// closed intervals.
|
|
type interval16 struct {
|
|
start uint16
|
|
length uint16 // length minus 1
|
|
}
|
|
|
|
func newInterval16Range(start, last uint16) interval16 {
|
|
if last < start {
|
|
panic(fmt.Sprintf("last (%d) cannot be smaller than start (%d)", last, start))
|
|
}
|
|
|
|
return interval16{
|
|
start,
|
|
last - start,
|
|
}
|
|
}
|
|
|
|
// runlen returns the count of integers in the interval.
|
|
func (iv interval16) runlen() int64 {
|
|
return int64(iv.length) + 1
|
|
}
|
|
|
|
func (iv interval16) last() uint16 {
|
|
return iv.start + iv.length
|
|
}
|
|
|
|
// String produces a human viewable string of the contents.
|
|
func (iv interval16) String() string {
|
|
return fmt.Sprintf("[%d, %d]", iv.start, iv.length)
|
|
}
|
|
|
|
func ivalString16(iv []interval16) string {
|
|
var s string
|
|
var j int
|
|
var p interval16
|
|
for j, p = range iv {
|
|
s += fmt.Sprintf("%v:[%d, %d], ", j, p.start, p.last())
|
|
}
|
|
return s
|
|
}
|
|
|
|
// String produces a human viewable string of the contents.
|
|
func (rc *runContainer16) String() string {
|
|
if len(rc.iv) == 0 {
|
|
return "runContainer16{}"
|
|
}
|
|
is := ivalString16(rc.iv)
|
|
return `runContainer16{` + is + `}`
|
|
}
|
|
|
|
// uint16Slice is a sort.Sort convenience method
|
|
type uint16Slice []uint16
|
|
|
|
// Len returns the length of p.
|
|
func (p uint16Slice) Len() int { return len(p) }
|
|
|
|
// Less returns p[i] < p[j]
|
|
func (p uint16Slice) Less(i, j int) bool { return p[i] < p[j] }
|
|
|
|
// Swap swaps elements i and j.
|
|
func (p uint16Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
|
|
//msgp:ignore addHelper
|
|
|
|
// addHelper helps build a runContainer16.
|
|
type addHelper16 struct {
|
|
runstart uint16
|
|
runlen uint16
|
|
actuallyAdded uint16
|
|
m []interval16
|
|
rc *runContainer16
|
|
}
|
|
|
|
func (ah *addHelper16) storeIval(runstart, runlen uint16) {
|
|
mi := interval16{start: runstart, length: runlen}
|
|
ah.m = append(ah.m, mi)
|
|
}
|
|
|
|
func (ah *addHelper16) add(cur, prev uint16, i int) {
|
|
if cur == prev+1 {
|
|
ah.runlen++
|
|
ah.actuallyAdded++
|
|
} else {
|
|
if cur < prev {
|
|
panic(fmt.Sprintf("newRunContainer16FromVals sees "+
|
|
"unsorted vals; vals[%v]=cur=%v < prev=%v. Sort your vals"+
|
|
" before calling us with alreadySorted == true.", i, cur, prev))
|
|
}
|
|
if cur == prev {
|
|
// ignore duplicates
|
|
} else {
|
|
ah.actuallyAdded++
|
|
ah.storeIval(ah.runstart, ah.runlen)
|
|
ah.runstart = cur
|
|
ah.runlen = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
// newRunContainerRange makes a new container made of just the specified closed interval [rangestart,rangelast]
|
|
func newRunContainer16Range(rangestart uint16, rangelast uint16) *runContainer16 {
|
|
rc := &runContainer16{}
|
|
rc.iv = append(rc.iv, newInterval16Range(rangestart, rangelast))
|
|
return rc
|
|
}
|
|
|
|
// newRunContainer16FromVals makes a new container from vals.
|
|
//
|
|
// For efficiency, vals should be sorted in ascending order.
|
|
// Ideally vals should not contain duplicates, but we detect and
|
|
// ignore them. If vals is already sorted in ascending order, then
|
|
// pass alreadySorted = true. Otherwise, for !alreadySorted,
|
|
// we will sort vals before creating a runContainer16 of them.
|
|
// We sort the original vals, so this will change what the
|
|
// caller sees in vals as a side effect.
|
|
func newRunContainer16FromVals(alreadySorted bool, vals ...uint16) *runContainer16 {
|
|
// keep this in sync with newRunContainer16FromArray below
|
|
|
|
rc := &runContainer16{}
|
|
ah := addHelper16{rc: rc}
|
|
|
|
if !alreadySorted {
|
|
sort.Sort(uint16Slice(vals))
|
|
}
|
|
n := len(vals)
|
|
var cur, prev uint16
|
|
switch {
|
|
case n == 0:
|
|
// nothing more
|
|
case n == 1:
|
|
ah.m = append(ah.m, newInterval16Range(vals[0], vals[0]))
|
|
ah.actuallyAdded++
|
|
default:
|
|
ah.runstart = vals[0]
|
|
ah.actuallyAdded++
|
|
for i := 1; i < n; i++ {
|
|
prev = vals[i-1]
|
|
cur = vals[i]
|
|
ah.add(cur, prev, i)
|
|
}
|
|
ah.storeIval(ah.runstart, ah.runlen)
|
|
}
|
|
rc.iv = ah.m
|
|
rc.card = int64(ah.actuallyAdded)
|
|
return rc
|
|
}
|
|
|
|
// newRunContainer16FromBitmapContainer makes a new run container from bc,
|
|
// 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 {
|
|
return rc
|
|
}
|
|
rc.iv = make([]interval16, nbrRuns)
|
|
|
|
longCtr := 0 // index of current long in bitmap
|
|
curWord := bc.bitmap[0] // its value
|
|
runCount := 0
|
|
for {
|
|
// potentially multiword advance to first 1 bit
|
|
for curWord == 0 && longCtr < len(bc.bitmap)-1 {
|
|
longCtr++
|
|
curWord = bc.bitmap[longCtr]
|
|
}
|
|
|
|
if curWord == 0 {
|
|
// wrap up, no more runs
|
|
return rc
|
|
}
|
|
localRunStart := countTrailingZeros(curWord)
|
|
runStart := localRunStart + 64*longCtr
|
|
// stuff 1s into number's LSBs
|
|
curWordWith1s := curWord | (curWord - 1)
|
|
|
|
// find the next 0, potentially in a later word
|
|
runEnd := 0
|
|
for curWordWith1s == maxWord && longCtr < len(bc.bitmap)-1 {
|
|
longCtr++
|
|
curWordWith1s = bc.bitmap[longCtr]
|
|
}
|
|
|
|
if curWordWith1s == maxWord {
|
|
// a final unterminated run of 1s
|
|
runEnd = wordSizeInBits + longCtr*64
|
|
rc.iv[runCount].start = uint16(runStart)
|
|
rc.iv[runCount].length = uint16(runEnd) - uint16(runStart) - 1
|
|
return rc
|
|
}
|
|
localRunEnd := countTrailingZeros(^curWordWith1s)
|
|
runEnd = localRunEnd + longCtr*64
|
|
rc.iv[runCount].start = uint16(runStart)
|
|
rc.iv[runCount].length = uint16(runEnd) - 1 - uint16(runStart)
|
|
runCount++
|
|
// now, zero out everything right of runEnd.
|
|
curWord = curWordWith1s & (curWordWith1s + 1)
|
|
// We've lathered and rinsed, so repeat...
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// newRunContainer16FromArray populates a new
|
|
// runContainer16 from the contents of arr.
|
|
//
|
|
func newRunContainer16FromArray(arr *arrayContainer) *runContainer16 {
|
|
// keep this in sync with newRunContainer16FromVals above
|
|
|
|
rc := &runContainer16{}
|
|
ah := addHelper16{rc: rc}
|
|
|
|
n := arr.getCardinality()
|
|
var cur, prev uint16
|
|
switch {
|
|
case n == 0:
|
|
// nothing more
|
|
case n == 1:
|
|
ah.m = append(ah.m, newInterval16Range(arr.content[0], arr.content[0]))
|
|
ah.actuallyAdded++
|
|
default:
|
|
ah.runstart = arr.content[0]
|
|
ah.actuallyAdded++
|
|
for i := 1; i < n; i++ {
|
|
prev = arr.content[i-1]
|
|
cur = arr.content[i]
|
|
ah.add(cur, prev, i)
|
|
}
|
|
ah.storeIval(ah.runstart, ah.runlen)
|
|
}
|
|
rc.iv = ah.m
|
|
rc.card = int64(ah.actuallyAdded)
|
|
return rc
|
|
}
|
|
|
|
// set adds the integers in vals to the set. Vals
|
|
// must be sorted in increasing order; if not, you should set
|
|
// alreadySorted to false, and we will sort them in place for you.
|
|
// (Be aware of this side effect -- it will affect the callers
|
|
// view of vals).
|
|
//
|
|
// 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
|
|
rc.card = 0
|
|
}
|
|
|
|
// canMerge returns true iff the intervals
|
|
// a and b either overlap or they are
|
|
// contiguous and so can be merged into
|
|
// a single interval.
|
|
func canMerge16(a, b interval16) bool {
|
|
if int64(a.last())+1 < int64(b.start) {
|
|
return false
|
|
}
|
|
return int64(b.last())+1 >= int64(a.start)
|
|
}
|
|
|
|
// haveOverlap differs from canMerge in that
|
|
// it tells you if the intersection of a
|
|
// and b would contain an element (otherwise
|
|
// it would be the empty set, and we return
|
|
// false).
|
|
func haveOverlap16(a, b interval16) bool {
|
|
if int64(a.last())+1 <= int64(b.start) {
|
|
return false
|
|
}
|
|
return int64(b.last())+1 > int64(a.start)
|
|
}
|
|
|
|
// mergeInterval16s joins a and b into a
|
|
// new interval, and panics if it cannot.
|
|
func mergeInterval16s(a, b interval16) (res interval16) {
|
|
if !canMerge16(a, b) {
|
|
panic(fmt.Sprintf("cannot merge %#v and %#v", a, b))
|
|
}
|
|
|
|
if b.start < a.start {
|
|
res.start = b.start
|
|
} else {
|
|
res.start = a.start
|
|
}
|
|
|
|
if b.last() > a.last() {
|
|
res.length = b.last() - res.start
|
|
} else {
|
|
res.length = a.last() - res.start
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// intersectInterval16s returns the intersection
|
|
// of a and b. The isEmpty flag will be true if
|
|
// a and b were disjoint.
|
|
func intersectInterval16s(a, b interval16) (res interval16, isEmpty bool) {
|
|
if !haveOverlap16(a, b) {
|
|
isEmpty = true
|
|
return
|
|
}
|
|
if b.start > a.start {
|
|
res.start = b.start
|
|
} else {
|
|
res.start = a.start
|
|
}
|
|
|
|
bEnd := b.last()
|
|
aEnd := a.last()
|
|
var resEnd uint16
|
|
|
|
if bEnd < aEnd {
|
|
resEnd = bEnd
|
|
} else {
|
|
resEnd = aEnd
|
|
}
|
|
res.length = resEnd - res.start
|
|
return
|
|
}
|
|
|
|
// 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.
|
|
|
|
var m []interval16
|
|
|
|
alim := int64(len(rc.iv))
|
|
blim := int64(len(b.iv))
|
|
|
|
var na int64 // next from a
|
|
var nb int64 // next from b
|
|
|
|
// merged holds the current merge output, which might
|
|
// get additional merges before being appended to m.
|
|
var merged interval16
|
|
var mergedUsed bool // is merged being used at the moment?
|
|
|
|
var cura interval16 // currently considering this interval16 from a
|
|
var curb interval16 // currently considering this interval16 from b
|
|
|
|
pass := 0
|
|
for na < alim && nb < blim {
|
|
pass++
|
|
cura = rc.iv[na]
|
|
curb = b.iv[nb]
|
|
|
|
if mergedUsed {
|
|
mergedUpdated := false
|
|
if canMerge16(cura, merged) {
|
|
merged = mergeInterval16s(cura, merged)
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
mergedUpdated = true
|
|
}
|
|
if canMerge16(curb, merged) {
|
|
merged = mergeInterval16s(curb, merged)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
mergedUpdated = true
|
|
}
|
|
if !mergedUpdated {
|
|
// we know that merged is disjoint from cura and curb
|
|
m = append(m, merged)
|
|
mergedUsed = false
|
|
}
|
|
continue
|
|
|
|
} else {
|
|
// !mergedUsed
|
|
if !canMerge16(cura, curb) {
|
|
if cura.start < curb.start {
|
|
m = append(m, cura)
|
|
na++
|
|
} else {
|
|
m = append(m, curb)
|
|
nb++
|
|
}
|
|
} else {
|
|
merged = mergeInterval16s(cura, curb)
|
|
mergedUsed = true
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
}
|
|
}
|
|
}
|
|
var aDone, bDone bool
|
|
if na >= alim {
|
|
aDone = true
|
|
}
|
|
if nb >= blim {
|
|
bDone = true
|
|
}
|
|
// finish by merging anything remaining into merged we can:
|
|
if mergedUsed {
|
|
if !aDone {
|
|
aAdds:
|
|
for na < alim {
|
|
cura = rc.iv[na]
|
|
if canMerge16(cura, merged) {
|
|
merged = mergeInterval16s(cura, merged)
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
} else {
|
|
break aAdds
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if !bDone {
|
|
bAdds:
|
|
for nb < blim {
|
|
curb = b.iv[nb]
|
|
if canMerge16(curb, merged) {
|
|
merged = mergeInterval16s(curb, merged)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
} else {
|
|
break bAdds
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
m = append(m, merged)
|
|
}
|
|
if na < alim {
|
|
m = append(m, rc.iv[na:]...)
|
|
}
|
|
if nb < blim {
|
|
m = append(m, b.iv[nb:]...)
|
|
}
|
|
|
|
res := &runContainer16{iv: m}
|
|
return res
|
|
}
|
|
|
|
// unionCardinality returns the cardinality of the merger of two runContainer16s, the union of rc and b.
|
|
func (rc *runContainer16) unionCardinality(b *runContainer16) uint64 {
|
|
|
|
// rc is also known as 'a' here, but golint insisted we
|
|
// call it rc for consistency with the rest of the methods.
|
|
answer := uint64(0)
|
|
|
|
alim := int64(len(rc.iv))
|
|
blim := int64(len(b.iv))
|
|
|
|
var na int64 // next from a
|
|
var nb int64 // next from b
|
|
|
|
// merged holds the current merge output, which might
|
|
// get additional merges before being appended to m.
|
|
var merged interval16
|
|
var mergedUsed bool // is merged being used at the moment?
|
|
|
|
var cura interval16 // currently considering this interval16 from a
|
|
var curb interval16 // currently considering this interval16 from b
|
|
|
|
pass := 0
|
|
for na < alim && nb < blim {
|
|
pass++
|
|
cura = rc.iv[na]
|
|
curb = b.iv[nb]
|
|
|
|
if mergedUsed {
|
|
mergedUpdated := false
|
|
if canMerge16(cura, merged) {
|
|
merged = mergeInterval16s(cura, merged)
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
mergedUpdated = true
|
|
}
|
|
if canMerge16(curb, merged) {
|
|
merged = mergeInterval16s(curb, merged)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
mergedUpdated = true
|
|
}
|
|
if !mergedUpdated {
|
|
// we know that merged is disjoint from cura and curb
|
|
//m = append(m, merged)
|
|
answer += uint64(merged.last()) - uint64(merged.start) + 1
|
|
mergedUsed = false
|
|
}
|
|
continue
|
|
|
|
} else {
|
|
// !mergedUsed
|
|
if !canMerge16(cura, curb) {
|
|
if cura.start < curb.start {
|
|
answer += uint64(cura.last()) - uint64(cura.start) + 1
|
|
//m = append(m, cura)
|
|
na++
|
|
} else {
|
|
answer += uint64(curb.last()) - uint64(curb.start) + 1
|
|
//m = append(m, curb)
|
|
nb++
|
|
}
|
|
} else {
|
|
merged = mergeInterval16s(cura, curb)
|
|
mergedUsed = true
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
}
|
|
}
|
|
}
|
|
var aDone, bDone bool
|
|
if na >= alim {
|
|
aDone = true
|
|
}
|
|
if nb >= blim {
|
|
bDone = true
|
|
}
|
|
// finish by merging anything remaining into merged we can:
|
|
if mergedUsed {
|
|
if !aDone {
|
|
aAdds:
|
|
for na < alim {
|
|
cura = rc.iv[na]
|
|
if canMerge16(cura, merged) {
|
|
merged = mergeInterval16s(cura, merged)
|
|
na = rc.indexOfIntervalAtOrAfter(int64(merged.last())+1, na+1)
|
|
} else {
|
|
break aAdds
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if !bDone {
|
|
bAdds:
|
|
for nb < blim {
|
|
curb = b.iv[nb]
|
|
if canMerge16(curb, merged) {
|
|
merged = mergeInterval16s(curb, merged)
|
|
nb = b.indexOfIntervalAtOrAfter(int64(merged.last())+1, nb+1)
|
|
} else {
|
|
break bAdds
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//m = append(m, merged)
|
|
answer += uint64(merged.last()) - uint64(merged.start) + 1
|
|
}
|
|
for _, r := range rc.iv[na:] {
|
|
answer += uint64(r.last()) - uint64(r.start) + 1
|
|
}
|
|
for _, r := range b.iv[nb:] {
|
|
answer += uint64(r.last()) - uint64(r.start) + 1
|
|
}
|
|
return answer
|
|
}
|
|
|
|
// indexOfIntervalAtOrAfter is a helper for union.
|
|
func (rc *runContainer16) indexOfIntervalAtOrAfter(key int64, startIndex int64) int64 {
|
|
rc.myOpts.startIndex = startIndex
|
|
rc.myOpts.endxIndex = 0
|
|
|
|
w, already, _ := rc.search(key, &rc.myOpts)
|
|
if already {
|
|
return w
|
|
}
|
|
return w + 1
|
|
}
|
|
|
|
// 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 := int64(len(a.iv))
|
|
numb := int64(len(b.iv))
|
|
res := &runContainer16{}
|
|
if numa == 0 || numb == 0 {
|
|
return res
|
|
}
|
|
|
|
if numa == 1 && numb == 1 {
|
|
if !haveOverlap16(a.iv[0], b.iv[0]) {
|
|
return res
|
|
}
|
|
}
|
|
|
|
var output []interval16
|
|
|
|
var acuri int64
|
|
var bcuri int64
|
|
|
|
astart := int64(a.iv[acuri].start)
|
|
bstart := int64(b.iv[bcuri].start)
|
|
|
|
var intersection interval16
|
|
var leftoverstart int64
|
|
var isOverlap, isLeftoverA, isLeftoverB bool
|
|
var done bool
|
|
toploop:
|
|
for acuri < numa && bcuri < numb {
|
|
|
|
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
|
|
intersectWithLeftover16(astart, int64(a.iv[acuri].last()), bstart, int64(b.iv[bcuri].last()))
|
|
|
|
if !isOverlap {
|
|
switch {
|
|
case astart < bstart:
|
|
acuri, done = a.findNextIntervalThatIntersectsStartingFrom(acuri+1, bstart)
|
|
if done {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
|
|
case astart > bstart:
|
|
bcuri, done = b.findNextIntervalThatIntersectsStartingFrom(bcuri+1, astart)
|
|
if done {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
|
|
//default:
|
|
// panic("impossible that astart == bstart, since !isOverlap")
|
|
}
|
|
|
|
} else {
|
|
// isOverlap
|
|
output = append(output, intersection)
|
|
switch {
|
|
case isLeftoverA:
|
|
// note that we change astart without advancing acuri,
|
|
// since we need to capture any 2ndary intersections with a.iv[acuri]
|
|
astart = leftoverstart
|
|
bcuri++
|
|
if bcuri >= numb {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
case isLeftoverB:
|
|
// note that we change bstart without advancing bcuri,
|
|
// since we need to capture any 2ndary intersections with b.iv[bcuri]
|
|
bstart = leftoverstart
|
|
acuri++
|
|
if acuri >= numa {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
default:
|
|
// neither had leftover, both completely consumed
|
|
// optionally, assert for sanity:
|
|
//if a.iv[acuri].endx != b.iv[bcuri].endx {
|
|
// panic("huh? should only be possible that endx agree now!")
|
|
//}
|
|
|
|
// advance to next a interval
|
|
acuri++
|
|
if acuri >= numa {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
|
|
// advance to next b interval
|
|
bcuri++
|
|
if bcuri >= numb {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
}
|
|
}
|
|
} // end for toploop
|
|
|
|
if len(output) == 0 {
|
|
return res
|
|
}
|
|
|
|
res.iv = output
|
|
return res
|
|
}
|
|
|
|
// intersectCardinality returns the cardinality of the
|
|
// intersection of rc (also known as 'a') and b.
|
|
func (rc *runContainer16) intersectCardinality(b *runContainer16) int64 {
|
|
answer := int64(0)
|
|
|
|
a := rc
|
|
numa := int64(len(a.iv))
|
|
numb := int64(len(b.iv))
|
|
if numa == 0 || numb == 0 {
|
|
return 0
|
|
}
|
|
|
|
if numa == 1 && numb == 1 {
|
|
if !haveOverlap16(a.iv[0], b.iv[0]) {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
var acuri int64
|
|
var bcuri int64
|
|
|
|
astart := int64(a.iv[acuri].start)
|
|
bstart := int64(b.iv[bcuri].start)
|
|
|
|
var intersection interval16
|
|
var leftoverstart int64
|
|
var isOverlap, isLeftoverA, isLeftoverB bool
|
|
var done bool
|
|
pass := 0
|
|
toploop:
|
|
for acuri < numa && bcuri < numb {
|
|
pass++
|
|
|
|
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
|
|
intersectWithLeftover16(astart, int64(a.iv[acuri].last()), bstart, int64(b.iv[bcuri].last()))
|
|
|
|
if !isOverlap {
|
|
switch {
|
|
case astart < bstart:
|
|
acuri, done = a.findNextIntervalThatIntersectsStartingFrom(acuri+1, bstart)
|
|
if done {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
|
|
case astart > bstart:
|
|
bcuri, done = b.findNextIntervalThatIntersectsStartingFrom(bcuri+1, astart)
|
|
if done {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
|
|
//default:
|
|
// panic("impossible that astart == bstart, since !isOverlap")
|
|
}
|
|
|
|
} else {
|
|
// isOverlap
|
|
answer += int64(intersection.last()) - int64(intersection.start) + 1
|
|
switch {
|
|
case isLeftoverA:
|
|
// note that we change astart without advancing acuri,
|
|
// since we need to capture any 2ndary intersections with a.iv[acuri]
|
|
astart = leftoverstart
|
|
bcuri++
|
|
if bcuri >= numb {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
case isLeftoverB:
|
|
// note that we change bstart without advancing bcuri,
|
|
// since we need to capture any 2ndary intersections with b.iv[bcuri]
|
|
bstart = leftoverstart
|
|
acuri++
|
|
if acuri >= numa {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
default:
|
|
// neither had leftover, both completely consumed
|
|
// optionally, assert for sanity:
|
|
//if a.iv[acuri].endx != b.iv[bcuri].endx {
|
|
// panic("huh? should only be possible that endx agree now!")
|
|
//}
|
|
|
|
// advance to next a interval
|
|
acuri++
|
|
if acuri >= numa {
|
|
break toploop
|
|
}
|
|
astart = int64(a.iv[acuri].start)
|
|
|
|
// advance to next b interval
|
|
bcuri++
|
|
if bcuri >= numb {
|
|
break toploop
|
|
}
|
|
bstart = int64(b.iv[bcuri].start)
|
|
}
|
|
}
|
|
} // end for toploop
|
|
|
|
return answer
|
|
}
|
|
|
|
// get returns true iff key is in the container.
|
|
func (rc *runContainer16) contains(key uint16) bool {
|
|
_, in, _ := rc.search(int64(key), nil)
|
|
return in
|
|
}
|
|
|
|
// numIntervals returns the count of intervals in the container.
|
|
func (rc *runContainer16) numIntervals() int {
|
|
return len(rc.iv)
|
|
}
|
|
|
|
// search returns alreadyPresent to indicate if the
|
|
// key is already in one of our interval16s.
|
|
//
|
|
// If key is alreadyPresent, then whichInterval16 tells
|
|
// you where.
|
|
//
|
|
// If key is not already present, then whichInterval16 is
|
|
// set as follows:
|
|
//
|
|
// a) whichInterval16 == len(rc.iv)-1 if key is beyond our
|
|
// last interval16 in rc.iv;
|
|
//
|
|
// b) whichInterval16 == -1 if key is before our first
|
|
// interval16 in rc.iv;
|
|
//
|
|
// c) whichInterval16 is set to the minimum 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
|
|
// (Note that whichInterval16+1 won't exist when
|
|
// whichInterval16 is the last interval.)
|
|
//
|
|
// runContainer16.search always returns whichInterval16 < len(rc.iv).
|
|
//
|
|
// If not nil, opts can be used to further restrict
|
|
// the search space.
|
|
//
|
|
func (rc *runContainer16) search(key int64, opts *searchOptions) (whichInterval16 int64, alreadyPresent bool, numCompares int) {
|
|
n := int64(len(rc.iv))
|
|
if n == 0 {
|
|
return -1, false, 0
|
|
}
|
|
|
|
startIndex := int64(0)
|
|
endxIndex := n
|
|
if opts != nil {
|
|
startIndex = opts.startIndex
|
|
|
|
// let endxIndex == 0 mean no effect
|
|
if opts.endxIndex > 0 {
|
|
endxIndex = opts.endxIndex
|
|
}
|
|
}
|
|
|
|
// sort.Search returns the smallest index i
|
|
// in [0, n) at which f(i) is true, assuming that on the range [0, n),
|
|
// f(i) == true implies f(i+1) == true.
|
|
// If there is no such index, Search returns n.
|
|
|
|
// For correctness, this began as verbatim snippet from
|
|
// sort.Search in the Go standard lib.
|
|
// We inline our comparison function for speed, and
|
|
// annotate with numCompares
|
|
// to observe and test that extra bounds are utilized.
|
|
i, j := startIndex, endxIndex
|
|
for i < j {
|
|
h := i + (j-i)/2 // avoid overflow when computing h as the bisector
|
|
// i <= h < j
|
|
numCompares++
|
|
if !(key < int64(rc.iv[h].start)) {
|
|
i = h + 1
|
|
} else {
|
|
j = h
|
|
}
|
|
}
|
|
below := i
|
|
// end std lib snippet.
|
|
|
|
// The above is a simple in-lining and annotation of:
|
|
/* below := sort.Search(n,
|
|
func(i int) bool {
|
|
return key < rc.iv[i].start
|
|
})
|
|
*/
|
|
whichInterval16 = below - 1
|
|
|
|
if below == n {
|
|
// all falses => key is >= start of all interval16s
|
|
// ... so does it belong to the last interval16?
|
|
if key < int64(rc.iv[n-1].last())+1 {
|
|
// yes, it belongs to the last interval16
|
|
alreadyPresent = true
|
|
return
|
|
}
|
|
// no, it is beyond the last interval16.
|
|
// leave alreadyPreset = false
|
|
return
|
|
}
|
|
|
|
// INVAR: key is below rc.iv[below]
|
|
if below == 0 {
|
|
// key is before the first first interval16.
|
|
// leave alreadyPresent = false
|
|
return
|
|
}
|
|
|
|
// INVAR: key is >= rc.iv[below-1].start and
|
|
// key is < rc.iv[below].start
|
|
|
|
// is key in below-1 interval16?
|
|
if key >= int64(rc.iv[below-1].start) && key < int64(rc.iv[below-1].last())+1 {
|
|
// yes, it is. key is in below-1 interval16.
|
|
alreadyPresent = true
|
|
return
|
|
}
|
|
|
|
// INVAR: key >= rc.iv[below-1].endx && key < rc.iv[below].start
|
|
// leave alreadyPresent = false
|
|
return
|
|
}
|
|
|
|
// cardinality returns the count of the integers stored in the
|
|
// runContainer16.
|
|
func (rc *runContainer16) cardinality() int64 {
|
|
if len(rc.iv) == 0 {
|
|
rc.card = 0
|
|
return 0
|
|
}
|
|
if rc.card > 0 {
|
|
return rc.card // already cached
|
|
}
|
|
// have to compute it
|
|
var n int64
|
|
for _, p := range rc.iv {
|
|
n += p.runlen()
|
|
}
|
|
rc.card = n // cache it
|
|
return n
|
|
}
|
|
|
|
// AsSlice decompresses the contents into a []uint16 slice.
|
|
func (rc *runContainer16) AsSlice() []uint16 {
|
|
s := make([]uint16, rc.cardinality())
|
|
j := 0
|
|
for _, p := range rc.iv {
|
|
for i := p.start; i <= p.last(); i++ {
|
|
s[j] = i
|
|
j++
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
// newRunContainer16 creates an empty run container.
|
|
func newRunContainer16() *runContainer16 {
|
|
return &runContainer16{}
|
|
}
|
|
|
|
// newRunContainer16CopyIv creates a run container, initializing
|
|
// with a copy of the supplied iv slice.
|
|
//
|
|
func newRunContainer16CopyIv(iv []interval16) *runContainer16 {
|
|
rc := &runContainer16{
|
|
iv: make([]interval16, len(iv)),
|
|
}
|
|
copy(rc.iv, iv)
|
|
return rc
|
|
}
|
|
|
|
func (rc *runContainer16) Clone() *runContainer16 {
|
|
rc2 := newRunContainer16CopyIv(rc.iv)
|
|
return rc2
|
|
}
|
|
|
|
// newRunContainer16TakeOwnership returns a new runContainer16
|
|
// backed by the provided iv slice, which we will
|
|
// assume exclusive control over from now on.
|
|
//
|
|
func newRunContainer16TakeOwnership(iv []interval16) *runContainer16 {
|
|
rc := &runContainer16{
|
|
iv: iv,
|
|
}
|
|
return rc
|
|
}
|
|
|
|
const baseRc16Size = int(unsafe.Sizeof(runContainer16{}))
|
|
const perIntervalRc16Size = int(unsafe.Sizeof(interval16{}))
|
|
|
|
const baseDiskRc16Size = int(unsafe.Sizeof(uint16(0)))
|
|
|
|
// see also runContainer16SerializedSizeInBytes(numRuns int) int
|
|
|
|
// getSizeInBytes returns the number of bytes of memory
|
|
// required by this runContainer16.
|
|
func (rc *runContainer16) getSizeInBytes() int {
|
|
return perIntervalRc16Size*len(rc.iv) + baseRc16Size
|
|
}
|
|
|
|
// runContainer16SerializedSizeInBytes returns the number of bytes of disk
|
|
// required to hold numRuns in a runContainer16.
|
|
func runContainer16SerializedSizeInBytes(numRuns int) int {
|
|
return perIntervalRc16Size*numRuns + baseDiskRc16Size
|
|
}
|
|
|
|
// Add adds a single value k to the set.
|
|
func (rc *runContainer16) Add(k uint16) (wasNew bool) {
|
|
// TODO comment from runContainer16.java:
|
|
// it might be better and simpler to do return
|
|
// toBitmapOrArrayContainer(getCardinality()).add(k)
|
|
// but note that some unit tests use this method to build up test
|
|
// runcontainers without calling runOptimize
|
|
|
|
k64 := int64(k)
|
|
|
|
index, present, _ := rc.search(k64, nil)
|
|
if present {
|
|
return // already there
|
|
}
|
|
wasNew = true
|
|
|
|
// increment card if it is cached already
|
|
if rc.card > 0 {
|
|
rc.card++
|
|
}
|
|
n := int64(len(rc.iv))
|
|
if index == -1 {
|
|
// we may need to extend the first run
|
|
if n > 0 {
|
|
if rc.iv[0].start == k+1 {
|
|
rc.iv[0].start = k
|
|
rc.iv[0].length++
|
|
return
|
|
}
|
|
}
|
|
// nope, k stands alone, starting the new first interval16.
|
|
rc.iv = append([]interval16{newInterval16Range(k, k)}, rc.iv...)
|
|
return
|
|
}
|
|
|
|
// are we off the end? handle both index == n and index == n-1:
|
|
if index >= n-1 {
|
|
if int64(rc.iv[n-1].last())+1 == k64 {
|
|
rc.iv[n-1].length++
|
|
return
|
|
}
|
|
rc.iv = append(rc.iv, newInterval16Range(k, k))
|
|
return
|
|
}
|
|
|
|
// INVAR: index and index+1 both exist, and k goes between them.
|
|
//
|
|
// Now: add k into the middle,
|
|
// possibly fusing with index or index+1 interval16
|
|
// and possibly resulting in fusing of two interval16s
|
|
// that had a one integer gap.
|
|
|
|
left := index
|
|
right := index + 1
|
|
|
|
// are we fusing left and right by adding k?
|
|
if int64(rc.iv[left].last())+1 == k64 && int64(rc.iv[right].start) == k64+1 {
|
|
// fuse into left
|
|
rc.iv[left].length = rc.iv[right].last() - rc.iv[left].start
|
|
// remove redundant right
|
|
rc.iv = append(rc.iv[:left+1], rc.iv[right+1:]...)
|
|
return
|
|
}
|
|
|
|
// are we an addition to left?
|
|
if int64(rc.iv[left].last())+1 == k64 {
|
|
// yes
|
|
rc.iv[left].length++
|
|
return
|
|
}
|
|
|
|
// are we an addition to right?
|
|
if int64(rc.iv[right].start) == k64+1 {
|
|
// yes
|
|
rc.iv[right].start = k
|
|
rc.iv[right].length++
|
|
return
|
|
}
|
|
|
|
// k makes a standalone new interval16, inserted in the middle
|
|
tail := append([]interval16{newInterval16Range(k, k)}, rc.iv[right:]...)
|
|
rc.iv = append(rc.iv[:left+1], tail...)
|
|
return
|
|
}
|
|
|
|
//msgp:ignore runIterator
|
|
|
|
// runIterator16 advice: you must call Next() at least once
|
|
// before calling Cur(); and you should call HasNext()
|
|
// before calling Next() to insure there are contents.
|
|
type runIterator16 struct {
|
|
rc *runContainer16
|
|
curIndex int64
|
|
curPosInIndex uint16
|
|
curSeq int64
|
|
}
|
|
|
|
// newRunIterator16 returns a new empty run container.
|
|
func (rc *runContainer16) newRunIterator16() *runIterator16 {
|
|
return &runIterator16{rc: rc, curIndex: -1}
|
|
}
|
|
|
|
// HasNext returns false if calling Next will panic. It
|
|
// returns true when there is at least one more value
|
|
// available in the iteration sequence.
|
|
func (ri *runIterator16) hasNext() bool {
|
|
if len(ri.rc.iv) == 0 {
|
|
return false
|
|
}
|
|
if ri.curIndex == -1 {
|
|
return true
|
|
}
|
|
return ri.curSeq+1 < ri.rc.cardinality()
|
|
}
|
|
|
|
// cur returns the current value pointed to by the iterator.
|
|
func (ri *runIterator16) cur() uint16 {
|
|
return ri.rc.iv[ri.curIndex].start + ri.curPosInIndex
|
|
}
|
|
|
|
// Next returns the next value in the iteration sequence.
|
|
func (ri *runIterator16) next() uint16 {
|
|
if !ri.hasNext() {
|
|
panic("no Next available")
|
|
}
|
|
if ri.curIndex >= int64(len(ri.rc.iv)) {
|
|
panic("runIterator.Next() going beyond what is available")
|
|
}
|
|
if ri.curIndex == -1 {
|
|
// first time is special
|
|
ri.curIndex = 0
|
|
} else {
|
|
ri.curPosInIndex++
|
|
if int64(ri.rc.iv[ri.curIndex].start)+int64(ri.curPosInIndex) == int64(ri.rc.iv[ri.curIndex].last())+1 {
|
|
ri.curPosInIndex = 0
|
|
ri.curIndex++
|
|
}
|
|
ri.curSeq++
|
|
}
|
|
return ri.cur()
|
|
}
|
|
|
|
// remove removes the element that the iterator
|
|
// is on from the run container. You can use
|
|
// Cur if you want to double check what is about
|
|
// to be deleted.
|
|
func (ri *runIterator16) remove() uint16 {
|
|
n := ri.rc.cardinality()
|
|
if n == 0 {
|
|
panic("runIterator.Remove called on empty runContainer16")
|
|
}
|
|
cur := ri.cur()
|
|
|
|
ri.rc.deleteAt(&ri.curIndex, &ri.curPosInIndex, &ri.curSeq)
|
|
return cur
|
|
}
|
|
|
|
type manyRunIterator16 struct {
|
|
rc *runContainer16
|
|
curIndex int64
|
|
curPosInIndex uint16
|
|
curSeq int64
|
|
}
|
|
|
|
func (rc *runContainer16) newManyRunIterator16() *manyRunIterator16 {
|
|
return &manyRunIterator16{rc: rc, curIndex: -1}
|
|
}
|
|
|
|
func (ri *manyRunIterator16) hasNext() bool {
|
|
if len(ri.rc.iv) == 0 {
|
|
return false
|
|
}
|
|
if ri.curIndex == -1 {
|
|
return true
|
|
}
|
|
return ri.curSeq+1 < ri.rc.cardinality()
|
|
}
|
|
|
|
// hs are the high bits to include to avoid needing to reiterate over the buffer in NextMany
|
|
func (ri *manyRunIterator16) nextMany(hs uint32, buf []uint32) int {
|
|
n := 0
|
|
if !ri.hasNext() {
|
|
return n
|
|
}
|
|
// start and end are inclusive
|
|
for n < len(buf) {
|
|
if ri.curIndex == -1 || int(ri.rc.iv[ri.curIndex].length-ri.curPosInIndex) <= 0 {
|
|
ri.curPosInIndex = 0
|
|
ri.curIndex++
|
|
if ri.curIndex == int64(len(ri.rc.iv)) {
|
|
break
|
|
}
|
|
buf[n] = uint32(ri.rc.iv[ri.curIndex].start) | hs
|
|
if ri.curIndex != 0 {
|
|
ri.curSeq += 1
|
|
}
|
|
n += 1
|
|
// not strictly necessarily due to len(buf)-n min check, but saves some work
|
|
continue
|
|
}
|
|
// add as many as you can from this seq
|
|
moreVals := minOfInt(int(ri.rc.iv[ri.curIndex].length-ri.curPosInIndex), len(buf)-n)
|
|
|
|
base := uint32(ri.rc.iv[ri.curIndex].start+ri.curPosInIndex+1) | hs
|
|
|
|
// allows BCE
|
|
buf2 := buf[n : n+moreVals]
|
|
for i := range buf2 {
|
|
buf2[i] = base + uint32(i)
|
|
}
|
|
|
|
// update values
|
|
ri.curPosInIndex += uint16(moreVals) //moreVals always fits in uint16
|
|
ri.curSeq += int64(moreVals)
|
|
n += moreVals
|
|
}
|
|
return n
|
|
}
|
|
|
|
// remove removes key from the container.
|
|
func (rc *runContainer16) removeKey(key uint16) (wasPresent bool) {
|
|
|
|
var index int64
|
|
var curSeq int64
|
|
index, wasPresent, _ = rc.search(int64(key), nil)
|
|
if !wasPresent {
|
|
return // already removed, nothing to do.
|
|
}
|
|
pos := key - rc.iv[index].start
|
|
rc.deleteAt(&index, &pos, &curSeq)
|
|
return
|
|
}
|
|
|
|
// internal helper functions
|
|
|
|
func (rc *runContainer16) deleteAt(curIndex *int64, curPosInIndex *uint16, curSeq *int64) {
|
|
rc.card--
|
|
*curSeq--
|
|
ci := *curIndex
|
|
pos := *curPosInIndex
|
|
|
|
// are we first, last, or in the middle of our interval16?
|
|
switch {
|
|
case pos == 0:
|
|
if int64(rc.iv[ci].length) == 0 {
|
|
// our interval disappears
|
|
rc.iv = append(rc.iv[:ci], rc.iv[ci+1:]...)
|
|
// curIndex stays the same, since the delete did
|
|
// the advance for us.
|
|
*curPosInIndex = 0
|
|
} else {
|
|
rc.iv[ci].start++ // no longer overflowable
|
|
rc.iv[ci].length--
|
|
}
|
|
case pos == rc.iv[ci].length:
|
|
// length
|
|
rc.iv[ci].length--
|
|
// our interval16 cannot disappear, else we would have been pos == 0, case first above.
|
|
*curPosInIndex--
|
|
// if we leave *curIndex alone, then Next() will work properly even after the delete.
|
|
default:
|
|
//middle
|
|
// split into two, adding an interval16
|
|
new0 := newInterval16Range(rc.iv[ci].start, rc.iv[ci].start+*curPosInIndex-1)
|
|
|
|
new1start := int64(rc.iv[ci].start+*curPosInIndex) + 1
|
|
if new1start > int64(MaxUint16) {
|
|
panic("overflow?!?!")
|
|
}
|
|
new1 := newInterval16Range(uint16(new1start), rc.iv[ci].last())
|
|
tail := append([]interval16{new0, new1}, rc.iv[ci+1:]...)
|
|
rc.iv = append(rc.iv[:ci], tail...)
|
|
// update curIndex and curPosInIndex
|
|
*curIndex++
|
|
*curPosInIndex = 0
|
|
}
|
|
|
|
}
|
|
|
|
func have4Overlap16(astart, alast, bstart, blast int64) bool {
|
|
if alast+1 <= bstart {
|
|
return false
|
|
}
|
|
return blast+1 > astart
|
|
}
|
|
|
|
func intersectWithLeftover16(astart, alast, bstart, blast int64) (isOverlap, isLeftoverA, isLeftoverB bool, leftoverstart int64, intersection interval16) {
|
|
if !have4Overlap16(astart, alast, bstart, blast) {
|
|
return
|
|
}
|
|
isOverlap = true
|
|
|
|
// do the intersection:
|
|
if bstart > astart {
|
|
intersection.start = uint16(bstart)
|
|
} else {
|
|
intersection.start = uint16(astart)
|
|
}
|
|
|
|
switch {
|
|
case blast < alast:
|
|
isLeftoverA = true
|
|
leftoverstart = blast + 1
|
|
intersection.length = uint16(blast) - intersection.start
|
|
case alast < blast:
|
|
isLeftoverB = true
|
|
leftoverstart = alast + 1
|
|
intersection.length = uint16(alast) - intersection.start
|
|
default:
|
|
// alast == blast
|
|
intersection.length = uint16(alast) - intersection.start
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (rc *runContainer16) findNextIntervalThatIntersectsStartingFrom(startIndex int64, key int64) (index int64, done bool) {
|
|
|
|
rc.myOpts.startIndex = startIndex
|
|
rc.myOpts.endxIndex = 0
|
|
|
|
w, _, _ := rc.search(key, &rc.myOpts)
|
|
// rc.search always returns w < len(rc.iv)
|
|
if w < startIndex {
|
|
// not found and comes before lower bound startIndex,
|
|
// so just use the lower bound.
|
|
if startIndex == int64(len(rc.iv)) {
|
|
// also this bump up means that we are done
|
|
return startIndex, true
|
|
}
|
|
return startIndex, false
|
|
}
|
|
|
|
return w, false
|
|
}
|
|
|
|
func sliceToString16(m []interval16) string {
|
|
s := ""
|
|
for i := range m {
|
|
s += fmt.Sprintf("%v: %s, ", i, m[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
// selectInt16 returns the j-th value in the container.
|
|
// We panic of j is out of bounds.
|
|
func (rc *runContainer16) selectInt16(j uint16) int {
|
|
n := rc.cardinality()
|
|
if int64(j) > n {
|
|
panic(fmt.Sprintf("Cannot select %v since Cardinality is %v", j, n))
|
|
}
|
|
|
|
var offset int64
|
|
for k := range rc.iv {
|
|
nextOffset := offset + rc.iv[k].runlen() + 1
|
|
if nextOffset > int64(j) {
|
|
return int(int64(rc.iv[k].start) + (int64(j) - offset))
|
|
}
|
|
offset = nextOffset
|
|
}
|
|
panic(fmt.Sprintf("Cannot select %v since Cardinality is %v", j, n))
|
|
}
|
|
|
|
// helper for invert
|
|
func (rc *runContainer16) invertlastInterval(origin uint16, lastIdx int) []interval16 {
|
|
cur := rc.iv[lastIdx]
|
|
if cur.last() == MaxUint16 {
|
|
if cur.start == origin {
|
|
return nil // empty container
|
|
}
|
|
return []interval16{newInterval16Range(origin, cur.start-1)}
|
|
}
|
|
if cur.start == origin {
|
|
return []interval16{newInterval16Range(cur.last()+1, MaxUint16)}
|
|
}
|
|
// invert splits
|
|
return []interval16{
|
|
newInterval16Range(origin, cur.start-1),
|
|
newInterval16Range(cur.last()+1, MaxUint16),
|
|
}
|
|
}
|
|
|
|
// invert returns a new container (not inplace), that is
|
|
// the inversion of rc. For each bit b in rc, the
|
|
// returned value has !b
|
|
func (rc *runContainer16) invert() *runContainer16 {
|
|
ni := len(rc.iv)
|
|
var m []interval16
|
|
switch ni {
|
|
case 0:
|
|
return &runContainer16{iv: []interval16{newInterval16Range(0, MaxUint16)}}
|
|
case 1:
|
|
return &runContainer16{iv: rc.invertlastInterval(0, 0)}
|
|
}
|
|
var invstart int64
|
|
ult := ni - 1
|
|
for i, cur := range rc.iv {
|
|
if i == ult {
|
|
// invertlastInteval will add both intervals (b) and (c) in
|
|
// diagram below.
|
|
m = append(m, rc.invertlastInterval(uint16(invstart), i)...)
|
|
break
|
|
}
|
|
// INVAR: i and cur are not the last interval, there is a next at i+1
|
|
//
|
|
// ........[cur.start, cur.last] ...... [next.start, next.last]....
|
|
// ^ ^ ^
|
|
// (a) (b) (c)
|
|
//
|
|
// Now: we add interval (a); but if (a) is empty, for cur.start==0, we skip it.
|
|
if cur.start > 0 {
|
|
m = append(m, newInterval16Range(uint16(invstart), cur.start-1))
|
|
}
|
|
invstart = int64(cur.last() + 1)
|
|
}
|
|
return &runContainer16{iv: m}
|
|
}
|
|
|
|
func (iv interval16) equal(b interval16) bool {
|
|
return iv.start == b.start && iv.length == b.length
|
|
}
|
|
|
|
func (iv interval16) isSuperSetOf(b interval16) bool {
|
|
return iv.start <= b.start && b.last() <= iv.last()
|
|
}
|
|
|
|
func (iv interval16) subtractInterval(del interval16) (left []interval16, delcount int64) {
|
|
isect, isEmpty := intersectInterval16s(iv, del)
|
|
|
|
if isEmpty {
|
|
return nil, 0
|
|
}
|
|
if del.isSuperSetOf(iv) {
|
|
return nil, iv.runlen()
|
|
}
|
|
|
|
switch {
|
|
case isect.start > iv.start && isect.last() < iv.last():
|
|
new0 := newInterval16Range(iv.start, isect.start-1)
|
|
new1 := newInterval16Range(isect.last()+1, iv.last())
|
|
return []interval16{new0, new1}, isect.runlen()
|
|
case isect.start == iv.start:
|
|
return []interval16{newInterval16Range(isect.last()+1, iv.last())}, isect.runlen()
|
|
default:
|
|
return []interval16{newInterval16Range(iv.start, isect.start-1)}, isect.runlen()
|
|
}
|
|
}
|
|
|
|
func (rc *runContainer16) isubtract(del interval16) {
|
|
origiv := make([]interval16, len(rc.iv))
|
|
copy(origiv, rc.iv)
|
|
n := int64(len(rc.iv))
|
|
if n == 0 {
|
|
return // already done.
|
|
}
|
|
|
|
_, isEmpty := intersectInterval16s(newInterval16Range(rc.iv[0].start, rc.iv[n-1].last()), del)
|
|
if isEmpty {
|
|
return // done
|
|
}
|
|
|
|
// INVAR there is some intersection between rc and del
|
|
istart, startAlready, _ := rc.search(int64(del.start), nil)
|
|
ilast, lastAlready, _ := rc.search(int64(del.last()), nil)
|
|
rc.card = -1
|
|
if istart == -1 {
|
|
if ilast == n-1 && !lastAlready {
|
|
rc.iv = nil
|
|
return
|
|
}
|
|
}
|
|
// some intervals will remain
|
|
switch {
|
|
case startAlready && lastAlready:
|
|
res0, _ := rc.iv[istart].subtractInterval(del)
|
|
|
|
// would overwrite values in iv b/c res0 can have len 2. so
|
|
// write to origiv instead.
|
|
lost := 1 + ilast - istart
|
|
changeSize := int64(len(res0)) - lost
|
|
newSize := int64(len(rc.iv)) + changeSize
|
|
|
|
// rc.iv = append(pre, caboose...)
|
|
// return
|
|
|
|
if ilast != istart {
|
|
res1, _ := rc.iv[ilast].subtractInterval(del)
|
|
res0 = append(res0, res1...)
|
|
changeSize = int64(len(res0)) - lost
|
|
newSize = int64(len(rc.iv)) + changeSize
|
|
}
|
|
switch {
|
|
case changeSize < 0:
|
|
// shrink
|
|
copy(rc.iv[istart+int64(len(res0)):], rc.iv[ilast+1:])
|
|
copy(rc.iv[istart:istart+int64(len(res0))], res0)
|
|
rc.iv = rc.iv[:newSize]
|
|
return
|
|
case changeSize == 0:
|
|
// stay the same
|
|
copy(rc.iv[istart:istart+int64(len(res0))], res0)
|
|
return
|
|
default:
|
|
// changeSize > 0 is only possible when ilast == istart.
|
|
// Hence we now know: changeSize == 1 and len(res0) == 2
|
|
rc.iv = append(rc.iv, interval16{})
|
|
// len(rc.iv) is correct now, no need to rc.iv = rc.iv[:newSize]
|
|
|
|
// copy the tail into place
|
|
copy(rc.iv[ilast+2:], rc.iv[ilast+1:])
|
|
// copy the new item(s) into place
|
|
copy(rc.iv[istart:istart+2], res0)
|
|
return
|
|
}
|
|
|
|
case !startAlready && !lastAlready:
|
|
// we get to discard whole intervals
|
|
|
|
// from the search() definition:
|
|
|
|
// if del.start is not present, then istart is
|
|
// set as follows:
|
|
//
|
|
// a) istart == n-1 if del.start is beyond our
|
|
// last interval16 in rc.iv;
|
|
//
|
|
// b) istart == -1 if del.start is before our first
|
|
// interval16 in rc.iv;
|
|
//
|
|
// c) istart is set to the minimum index of rc.iv
|
|
// which comes strictly before the del.start;
|
|
// so del.start > rc.iv[istart].last,
|
|
// and if istart+1 exists, then del.start < rc.iv[istart+1].startx
|
|
|
|
// if del.last is not present, then ilast is
|
|
// set as follows:
|
|
//
|
|
// a) ilast == n-1 if del.last is beyond our
|
|
// last interval16 in rc.iv;
|
|
//
|
|
// b) ilast == -1 if del.last is before our first
|
|
// interval16 in rc.iv;
|
|
//
|
|
// c) ilast is set to the minimum index of rc.iv
|
|
// which comes strictly before the del.last;
|
|
// so del.last > rc.iv[ilast].last,
|
|
// and if ilast+1 exists, then del.last < rc.iv[ilast+1].start
|
|
|
|
// INVAR: istart >= 0
|
|
pre := rc.iv[:istart+1]
|
|
if ilast == n-1 {
|
|
rc.iv = pre
|
|
return
|
|
}
|
|
// INVAR: ilast < n-1
|
|
lost := ilast - istart
|
|
changeSize := -lost
|
|
newSize := int64(len(rc.iv)) + changeSize
|
|
if changeSize != 0 {
|
|
copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:])
|
|
}
|
|
rc.iv = rc.iv[:newSize]
|
|
return
|
|
|
|
case startAlready && !lastAlready:
|
|
// we can only shrink or stay the same size
|
|
// i.e. we either eliminate the whole interval,
|
|
// or just cut off the right side.
|
|
res0, _ := rc.iv[istart].subtractInterval(del)
|
|
if len(res0) > 0 {
|
|
// len(res) must be 1
|
|
rc.iv[istart] = res0[0]
|
|
}
|
|
lost := 1 + (ilast - istart)
|
|
changeSize := int64(len(res0)) - lost
|
|
newSize := int64(len(rc.iv)) + changeSize
|
|
if changeSize != 0 {
|
|
copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:])
|
|
}
|
|
rc.iv = rc.iv[:newSize]
|
|
return
|
|
|
|
case !startAlready && lastAlready:
|
|
// we can only shrink or stay the same size
|
|
res1, _ := rc.iv[ilast].subtractInterval(del)
|
|
lost := ilast - istart
|
|
changeSize := int64(len(res1)) - lost
|
|
newSize := int64(len(rc.iv)) + changeSize
|
|
if changeSize != 0 {
|
|
// move the tail first to make room for res1
|
|
copy(rc.iv[ilast+1+changeSize:], rc.iv[ilast+1:])
|
|
}
|
|
copy(rc.iv[istart+1:], res1)
|
|
rc.iv = rc.iv[:newSize]
|
|
return
|
|
}
|
|
}
|
|
|
|
// compute rc minus b, and return the result as a new value (not inplace).
|
|
// 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
|
|
}
|
|
|
|
dst := newRunContainer16()
|
|
apos := 0
|
|
bpos := 0
|
|
|
|
a := rc
|
|
|
|
astart := a.iv[apos].start
|
|
alast := a.iv[apos].last()
|
|
bstart := b.iv[bpos].start
|
|
blast := b.iv[bpos].last()
|
|
|
|
alen := len(a.iv)
|
|
blen := len(b.iv)
|
|
|
|
for apos < alen && bpos < blen {
|
|
switch {
|
|
case alast < bstart:
|
|
// output the first run
|
|
dst.iv = append(dst.iv, newInterval16Range(astart, alast))
|
|
apos++
|
|
if apos < alen {
|
|
astart = a.iv[apos].start
|
|
alast = a.iv[apos].last()
|
|
}
|
|
case blast < astart:
|
|
// exit the second run
|
|
bpos++
|
|
if bpos < blen {
|
|
bstart = b.iv[bpos].start
|
|
blast = b.iv[bpos].last()
|
|
}
|
|
default:
|
|
// a: [ ]
|
|
// b: [ ]
|
|
// alast >= bstart
|
|
// blast >= astart
|
|
if astart < bstart {
|
|
dst.iv = append(dst.iv, newInterval16Range(astart, bstart-1))
|
|
}
|
|
if alast > blast {
|
|
astart = blast + 1
|
|
} else {
|
|
apos++
|
|
if apos < alen {
|
|
astart = a.iv[apos].start
|
|
alast = a.iv[apos].last()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if apos < alen {
|
|
dst.iv = append(dst.iv, newInterval16Range(astart, alast))
|
|
apos++
|
|
if apos < alen {
|
|
dst.iv = append(dst.iv, a.iv[apos:]...)
|
|
}
|
|
}
|
|
|
|
return dst
|
|
}
|
|
|
|
func (rc *runContainer16) numberOfRuns() (nr int) {
|
|
return len(rc.iv)
|
|
}
|
|
|
|
func (rc *runContainer16) containerType() contype {
|
|
return run16Contype
|
|
}
|
|
|
|
func (rc *runContainer16) equals16(srb *runContainer16) bool {
|
|
//p("both rc16")
|
|
// Check if the containers are the same object.
|
|
if rc == srb {
|
|
//p("same object")
|
|
return true
|
|
}
|
|
|
|
if len(srb.iv) != len(rc.iv) {
|
|
//p("iv len differ")
|
|
return false
|
|
}
|
|
|
|
for i, v := range rc.iv {
|
|
if v != srb.iv[i] {
|
|
//p("differ at iv i=%v, srb.iv[i]=%v, rc.iv[i]=%v", i, srb.iv[i], rc.iv[i])
|
|
return false
|
|
}
|
|
}
|
|
//p("all intervals same, returning true")
|
|
return true
|
|
}
|