|
- 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
-
- // runContainer32 does run-length encoding of sets of
- // uint32 integers.
- type runContainer32 struct {
- iv []interval32
- card int64
-
- // avoid allocation during search
- myOpts searchOptions `msg:"-"`
- }
-
- // interval32 is the internal to runContainer32
- // structure that maintains the individual [Start, last]
- // closed intervals.
- type interval32 struct {
- start uint32
- last uint32
- }
-
- // runlen returns the count of integers in the interval.
- func (iv interval32) runlen() int64 {
- return 1 + int64(iv.last) - int64(iv.start)
- }
-
- // String produces a human viewable string of the contents.
- func (iv interval32) String() string {
- return fmt.Sprintf("[%d, %d]", iv.start, iv.last)
- }
-
- func ivalString32(iv []interval32) string {
- var s string
- var j int
- var p interval32
- 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 *runContainer32) String() string {
- if len(rc.iv) == 0 {
- return "runContainer32{}"
- }
- is := ivalString32(rc.iv)
- return `runContainer32{` + is + `}`
- }
-
- // uint32Slice is a sort.Sort convenience method
- type uint32Slice []uint32
-
- // Len returns the length of p.
- func (p uint32Slice) Len() int { return len(p) }
-
- // Less returns p[i] < p[j]
- func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] }
-
- // Swap swaps elements i and j.
- func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-
- //msgp:ignore addHelper
-
- // addHelper helps build a runContainer32.
- type addHelper32 struct {
- runstart uint32
- runlen uint32
- actuallyAdded uint32
- m []interval32
- rc *runContainer32
- }
-
- func (ah *addHelper32) storeIval(runstart, runlen uint32) {
- mi := interval32{start: runstart, last: runstart + runlen}
- ah.m = append(ah.m, mi)
- }
-
- func (ah *addHelper32) add(cur, prev uint32, i int) {
- if cur == prev+1 {
- ah.runlen++
- ah.actuallyAdded++
- } else {
- if cur < prev {
- panic(fmt.Sprintf("newRunContainer32FromVals 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 newRunContainer32Range(rangestart uint32, rangelast uint32) *runContainer32 {
- rc := &runContainer32{}
- rc.iv = append(rc.iv, interval32{start: rangestart, last: rangelast})
- return rc
- }
-
- // newRunContainer32FromVals 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 runContainer32 of them.
- // We sort the original vals, so this will change what the
- // caller sees in vals as a side effect.
- func newRunContainer32FromVals(alreadySorted bool, vals ...uint32) *runContainer32 {
- // keep this in sync with newRunContainer32FromArray below
-
- rc := &runContainer32{}
- ah := addHelper32{rc: rc}
-
- if !alreadySorted {
- sort.Sort(uint32Slice(vals))
- }
- n := len(vals)
- var cur, prev uint32
- switch {
- case n == 0:
- // nothing more
- case n == 1:
- ah.m = append(ah.m, interval32{start: vals[0], last: 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
- }
-
- // newRunContainer32FromBitmapContainer 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 newRunContainer32FromBitmapContainer(bc *bitmapContainer) *runContainer32 {
-
- rc := &runContainer32{}
- nbrRuns := bc.numberOfRuns()
- if nbrRuns == 0 {
- return rc
- }
- rc.iv = make([]interval32, 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 = uint32(runStart)
- rc.iv[runCount].last = uint32(runEnd) - 1
- return rc
- }
- localRunEnd := countTrailingZeros(^curWordWith1s)
- runEnd = localRunEnd + longCtr*64
- rc.iv[runCount].start = uint32(runStart)
- rc.iv[runCount].last = uint32(runEnd) - 1
- runCount++
- // now, zero out everything right of runEnd.
- curWord = curWordWith1s & (curWordWith1s + 1)
- // We've lathered and rinsed, so repeat...
- }
-
- }
-
- //
- // newRunContainer32FromArray populates a new
- // runContainer32 from the contents of arr.
- //
- func newRunContainer32FromArray(arr *arrayContainer) *runContainer32 {
- // keep this in sync with newRunContainer32FromVals above
-
- rc := &runContainer32{}
- ah := addHelper32{rc: rc}
-
- n := arr.getCardinality()
- var cur, prev uint32
- switch {
- case n == 0:
- // nothing more
- case n == 1:
- ah.m = append(ah.m, interval32{start: uint32(arr.content[0]), last: uint32(arr.content[0])})
- ah.actuallyAdded++
- default:
- ah.runstart = uint32(arr.content[0])
- ah.actuallyAdded++
- for i := 1; i < n; i++ {
- prev = uint32(arr.content[i-1])
- cur = uint32(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 runContainer32, calling Add() may be faster.
- func (rc *runContainer32) set(alreadySorted bool, vals ...uint32) {
-
- rc2 := newRunContainer32FromVals(alreadySorted, vals...)
- un := rc.union(rc2)
- rc.iv = un.iv
- rc.card = 0
- }
-
- // canMerge returns true if the intervals
- // a and b either overlap or they are
- // contiguous and so can be merged into
- // a single interval.
- func canMerge32(a, b interval32) 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 haveOverlap32(a, b interval32) bool {
- if int64(a.last)+1 <= int64(b.start) {
- return false
- }
- return int64(b.last)+1 > int64(a.start)
- }
-
- // mergeInterval32s joins a and b into a
- // new interval, and panics if it cannot.
- func mergeInterval32s(a, b interval32) (res interval32) {
- if !canMerge32(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.last = b.last
- } else {
- res.last = a.last
- }
- return
- }
-
- // intersectInterval32s returns the intersection
- // of a and b. The isEmpty flag will be true if
- // a and b were disjoint.
- func intersectInterval32s(a, b interval32) (res interval32, isEmpty bool) {
- if !haveOverlap32(a, b) {
- isEmpty = true
- return
- }
- if b.start > a.start {
- res.start = b.start
- } else {
- res.start = a.start
- }
- if b.last < a.last {
- res.last = b.last
- } else {
- res.last = a.last
- }
- return
- }
-
- // union merges two runContainer32s, producing
- // a new runContainer32 with the union of rc and b.
- func (rc *runContainer32) union(b *runContainer32) *runContainer32 {
-
- // rc is also known as 'a' here, but golint insisted we
- // call it rc for consistency with the rest of the methods.
-
- var m []interval32
-
- 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 interval32
- var mergedUsed bool // is merged being used at the moment?
-
- var cura interval32 // currently considering this interval32 from a
- var curb interval32 // currently considering this interval32 from b
-
- pass := 0
- for na < alim && nb < blim {
- pass++
- cura = rc.iv[na]
- curb = b.iv[nb]
-
- if mergedUsed {
- mergedUpdated := false
- if canMerge32(cura, merged) {
- merged = mergeInterval32s(cura, merged)
- na = rc.indexOfIntervalAtOrAfter(int64(merged.last)+1, na+1)
- mergedUpdated = true
- }
- if canMerge32(curb, merged) {
- merged = mergeInterval32s(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 !canMerge32(cura, curb) {
- if cura.start < curb.start {
- m = append(m, cura)
- na++
- } else {
- m = append(m, curb)
- nb++
- }
- } else {
- merged = mergeInterval32s(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 canMerge32(cura, merged) {
- merged = mergeInterval32s(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 canMerge32(curb, merged) {
- merged = mergeInterval32s(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 := &runContainer32{iv: m}
- return res
- }
-
- // unionCardinality returns the cardinality of the merger of two runContainer32s, the union of rc and b.
- func (rc *runContainer32) unionCardinality(b *runContainer32) 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 interval32
- var mergedUsed bool // is merged being used at the moment?
-
- var cura interval32 // currently considering this interval32 from a
- var curb interval32 // currently considering this interval32 from b
-
- pass := 0
- for na < alim && nb < blim {
- pass++
- cura = rc.iv[na]
- curb = b.iv[nb]
-
- if mergedUsed {
- mergedUpdated := false
- if canMerge32(cura, merged) {
- merged = mergeInterval32s(cura, merged)
- na = rc.indexOfIntervalAtOrAfter(int64(merged.last)+1, na+1)
- mergedUpdated = true
- }
- if canMerge32(curb, merged) {
- merged = mergeInterval32s(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 !canMerge32(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 = mergeInterval32s(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 canMerge32(cura, merged) {
- merged = mergeInterval32s(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 canMerge32(curb, merged) {
- merged = mergeInterval32s(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 *runContainer32) 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 runContainer32 holding the
- // intersection of rc (also known as 'a') and b.
- func (rc *runContainer32) intersect(b *runContainer32) *runContainer32 {
-
- a := rc
- numa := int64(len(a.iv))
- numb := int64(len(b.iv))
- res := &runContainer32{}
- if numa == 0 || numb == 0 {
- return res
- }
-
- if numa == 1 && numb == 1 {
- if !haveOverlap32(a.iv[0], b.iv[0]) {
- return res
- }
- }
-
- var output []interval32
-
- var acuri int64
- var bcuri int64
-
- astart := int64(a.iv[acuri].start)
- bstart := int64(b.iv[bcuri].start)
-
- var intersection interval32
- 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 = intersectWithLeftover32(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 *runContainer32) intersectCardinality(b *runContainer32) 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 !haveOverlap32(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 interval32
- 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 = intersectWithLeftover32(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 if key is in the container.
- func (rc *runContainer32) contains(key uint32) bool {
- _, in, _ := rc.search(int64(key), nil)
- return in
- }
-
- // numIntervals returns the count of intervals in the container.
- func (rc *runContainer32) numIntervals() int {
- return len(rc.iv)
- }
-
- // search returns alreadyPresent to indicate if the
- // key is already in one of our interval32s.
- //
- // If key is alreadyPresent, then whichInterval32 tells
- // you where.
- //
- // If key is not already present, then whichInterval32 is
- // set as follows:
- //
- // a) whichInterval32 == len(rc.iv)-1 if key is beyond our
- // last interval32 in rc.iv;
- //
- // b) whichInterval32 == -1 if key is before our first
- // interval32 in rc.iv;
- //
- // c) whichInterval32 is set to the minimum index of rc.iv
- // which comes strictly before the key;
- // so rc.iv[whichInterval32].last < key,
- // and if whichInterval32+1 exists, then key < rc.iv[whichInterval32+1].start
- // (Note that whichInterval32+1 won't exist when
- // whichInterval32 is the last interval.)
- //
- // runContainer32.search always returns whichInterval32 < len(rc.iv).
- //
- // If not nil, opts can be used to further restrict
- // the search space.
- //
- func (rc *runContainer32) search(key int64, opts *searchOptions) (whichInterval32 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
- })
- */
- whichInterval32 = below - 1
-
- if below == n {
- // all falses => key is >= start of all interval32s
- // ... so does it belong to the last interval32?
- if key < int64(rc.iv[n-1].last)+1 {
- // yes, it belongs to the last interval32
- alreadyPresent = true
- return
- }
- // no, it is beyond the last interval32.
- // leave alreadyPreset = false
- return
- }
-
- // INVAR: key is below rc.iv[below]
- if below == 0 {
- // key is before the first first interval32.
- // leave alreadyPresent = false
- return
- }
-
- // INVAR: key is >= rc.iv[below-1].start and
- // key is < rc.iv[below].start
-
- // is key in below-1 interval32?
- if key >= int64(rc.iv[below-1].start) && key < int64(rc.iv[below-1].last)+1 {
- // yes, it is. key is in below-1 interval32.
- 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
- // runContainer32.
- func (rc *runContainer32) 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 []uint32 slice.
- func (rc *runContainer32) AsSlice() []uint32 {
- s := make([]uint32, rc.cardinality())
- j := 0
- for _, p := range rc.iv {
- for i := p.start; i <= p.last; i++ {
- s[j] = i
- j++
- }
- }
- return s
- }
-
- // newRunContainer32 creates an empty run container.
- func newRunContainer32() *runContainer32 {
- return &runContainer32{}
- }
-
- // newRunContainer32CopyIv creates a run container, initializing
- // with a copy of the supplied iv slice.
- //
- func newRunContainer32CopyIv(iv []interval32) *runContainer32 {
- rc := &runContainer32{
- iv: make([]interval32, len(iv)),
- }
- copy(rc.iv, iv)
- return rc
- }
-
- func (rc *runContainer32) Clone() *runContainer32 {
- rc2 := newRunContainer32CopyIv(rc.iv)
- return rc2
- }
-
- // newRunContainer32TakeOwnership returns a new runContainer32
- // backed by the provided iv slice, which we will
- // assume exclusive control over from now on.
- //
- func newRunContainer32TakeOwnership(iv []interval32) *runContainer32 {
- rc := &runContainer32{
- iv: iv,
- }
- return rc
- }
-
- const baseRc32Size = int(unsafe.Sizeof(runContainer32{}))
- const perIntervalRc32Size = int(unsafe.Sizeof(interval32{}))
-
- const baseDiskRc32Size = int(unsafe.Sizeof(uint32(0)))
-
- // see also runContainer32SerializedSizeInBytes(numRuns int) int
-
- // getSizeInBytes returns the number of bytes of memory
- // required by this runContainer32.
- func (rc *runContainer32) getSizeInBytes() int {
- return perIntervalRc32Size*len(rc.iv) + baseRc32Size
- }
-
- // runContainer32SerializedSizeInBytes returns the number of bytes of disk
- // required to hold numRuns in a runContainer32.
- func runContainer32SerializedSizeInBytes(numRuns int) int {
- return perIntervalRc32Size*numRuns + baseDiskRc32Size
- }
-
- // Add adds a single value k to the set.
- func (rc *runContainer32) Add(k uint32) (wasNew bool) {
- // TODO comment from runContainer32.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
- return
- }
- }
- // nope, k stands alone, starting the new first interval32.
- rc.iv = append([]interval32{{start: k, last: 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].last++
- return
- }
- rc.iv = append(rc.iv, interval32{start: k, last: 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 interval32
- // and possibly resulting in fusing of two interval32s
- // 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].last = rc.iv[right].last
- // 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].last++
- return
- }
-
- // are we an addition to right?
- if int64(rc.iv[right].start) == k64+1 {
- // yes
- rc.iv[right].start = k
- return
- }
-
- // k makes a standalone new interval32, inserted in the middle
- tail := append([]interval32{{start: k, last: k}}, rc.iv[right:]...)
- rc.iv = append(rc.iv[:left+1], tail...)
- return
- }
-
- //msgp:ignore runIterator
-
- // runIterator32 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 runIterator32 struct {
- rc *runContainer32
- curIndex int64
- curPosInIndex uint32
- curSeq int64
- }
-
- // newRunIterator32 returns a new empty run container.
- func (rc *runContainer32) newRunIterator32() *runIterator32 {
- return &runIterator32{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 *runIterator32) 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 *runIterator32) cur() uint32 {
- return ri.rc.iv[ri.curIndex].start + ri.curPosInIndex
- }
-
- // Next returns the next value in the iteration sequence.
- func (ri *runIterator32) next() uint32 {
- 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 *runIterator32) remove() uint32 {
- n := ri.rc.cardinality()
- if n == 0 {
- panic("runIterator.Remove called on empty runContainer32")
- }
- cur := ri.cur()
-
- ri.rc.deleteAt(&ri.curIndex, &ri.curPosInIndex, &ri.curSeq)
- return cur
- }
-
- // remove removes key from the container.
- func (rc *runContainer32) removeKey(key uint32) (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 *runContainer32) deleteAt(curIndex *int64, curPosInIndex *uint32, curSeq *int64) {
- rc.card--
- (*curSeq)--
- ci := *curIndex
- pos := *curPosInIndex
-
- // are we first, last, or in the middle of our interval32?
- switch {
- case pos == 0:
- if int64(rc.iv[ci].start) == int64(rc.iv[ci].last) {
- // 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
- }
- case int64(pos) == rc.iv[ci].runlen()-1:
- // last
- rc.iv[ci].last--
- // our interval32 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 interval32
- new0 := interval32{
- start: rc.iv[ci].start,
- last: rc.iv[ci].start + *curPosInIndex - 1}
-
- new1start := int64(rc.iv[ci].start) + int64(*curPosInIndex) + 1
- if new1start > int64(MaxUint32) {
- panic("overflow?!?!")
- }
- new1 := interval32{
- start: uint32(new1start),
- last: rc.iv[ci].last}
- tail := append([]interval32{new0, new1}, rc.iv[ci+1:]...)
- rc.iv = append(rc.iv[:ci], tail...)
- // update curIndex and curPosInIndex
- (*curIndex)++
- *curPosInIndex = 0
- }
-
- }
-
- func have4Overlap32(astart, alast, bstart, blast int64) bool {
- if alast+1 <= bstart {
- return false
- }
- return blast+1 > astart
- }
-
- func intersectWithLeftover32(astart, alast, bstart, blast int64) (isOverlap, isLeftoverA, isLeftoverB bool, leftoverstart int64, intersection interval32) {
- if !have4Overlap32(astart, alast, bstart, blast) {
- return
- }
- isOverlap = true
-
- // do the intersection:
- if bstart > astart {
- intersection.start = uint32(bstart)
- } else {
- intersection.start = uint32(astart)
- }
- switch {
- case blast < alast:
- isLeftoverA = true
- leftoverstart = blast + 1
- intersection.last = uint32(blast)
- case alast < blast:
- isLeftoverB = true
- leftoverstart = alast + 1
- intersection.last = uint32(alast)
- default:
- // alast == blast
- intersection.last = uint32(alast)
- }
-
- return
- }
-
- func (rc *runContainer32) 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 sliceToString32(m []interval32) string {
- s := ""
- for i := range m {
- s += fmt.Sprintf("%v: %s, ", i, m[i])
- }
- return s
- }
-
- // selectInt32 returns the j-th value in the container.
- // We panic of j is out of bounds.
- func (rc *runContainer32) selectInt32(j uint32) 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 *runContainer32) invertlastInterval(origin uint32, lastIdx int) []interval32 {
- cur := rc.iv[lastIdx]
- if cur.last == MaxUint32 {
- if cur.start == origin {
- return nil // empty container
- }
- return []interval32{{start: origin, last: cur.start - 1}}
- }
- if cur.start == origin {
- return []interval32{{start: cur.last + 1, last: MaxUint32}}
- }
- // invert splits
- return []interval32{
- {start: origin, last: cur.start - 1},
- {start: cur.last + 1, last: MaxUint32},
- }
- }
-
- // 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 *runContainer32) invert() *runContainer32 {
- ni := len(rc.iv)
- var m []interval32
- switch ni {
- case 0:
- return &runContainer32{iv: []interval32{{0, MaxUint32}}}
- case 1:
- return &runContainer32{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(uint32(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, interval32{start: uint32(invstart), last: cur.start - 1})
- }
- invstart = int64(cur.last + 1)
- }
- return &runContainer32{iv: m}
- }
-
- func (iv interval32) equal(b interval32) bool {
- if iv.start == b.start {
- return iv.last == b.last
- }
- return false
- }
-
- func (iv interval32) isSuperSetOf(b interval32) bool {
- return iv.start <= b.start && b.last <= iv.last
- }
-
- func (iv interval32) subtractInterval(del interval32) (left []interval32, delcount int64) {
- isect, isEmpty := intersectInterval32s(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 := interval32{start: iv.start, last: isect.start - 1}
- new1 := interval32{start: isect.last + 1, last: iv.last}
- return []interval32{new0, new1}, isect.runlen()
- case isect.start == iv.start:
- return []interval32{{start: isect.last + 1, last: iv.last}}, isect.runlen()
- default:
- return []interval32{{start: iv.start, last: isect.start - 1}}, isect.runlen()
- }
- }
-
- func (rc *runContainer32) isubtract(del interval32) {
- origiv := make([]interval32, len(rc.iv))
- copy(origiv, rc.iv)
- n := int64(len(rc.iv))
- if n == 0 {
- return // already done.
- }
-
- _, isEmpty := intersectInterval32s(
- interval32{
- start: rc.iv[0].start,
- last: 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, interval32{})
- // 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 interval32 in rc.iv;
- //
- // b) istart == -1 if del.start is before our first
- // interval32 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 interval32 in rc.iv;
- //
- // b) ilast == -1 if del.last is before our first
- // interval32 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 *runContainer32) AndNotRunContainer32(b *runContainer32) *runContainer32 {
-
- if len(b.iv) == 0 || len(rc.iv) == 0 {
- return rc
- }
-
- dst := newRunContainer32()
- 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, interval32{start: astart, last: 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, interval32{start: astart, last: 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, interval32{start: astart, last: alast})
- apos++
- if apos < alen {
- dst.iv = append(dst.iv, a.iv[apos:]...)
- }
- }
-
- return dst
- }
-
- func (rc *runContainer32) numberOfRuns() (nr int) {
- return len(rc.iv)
- }
-
- func (rc *runContainer32) containerType() contype {
- return run32Contype
- }
-
- func (rc *runContainer32) equals32(srb *runContainer32) bool {
- //p("both rc32")
- // 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
- }
|