added roaring min

This commit is contained in:
Yuce Tekol 2019-05-30 15:15:08 +03:00
parent c8a3dc8c18
commit f15cb9e05c
No known key found for this signature in database
GPG key ID: CB59E46D2FB90573
5 changed files with 144 additions and 5 deletions

View file

@ -114,6 +114,13 @@ func (sc *sliceContainers) Clone() Containers {
return other
}
func (sc *sliceContainers) First() (key uint64, c *Container) {
if len(sc.keys) == 0 {
return 0, nil
}
return sc.keys[0], sc.containers[0]
}
func (sc *sliceContainers) Last() (key uint64, c *Container) {
if len(sc.keys) == 0 {
return 0, nil

View file

@ -142,6 +142,13 @@ func (btc *bTreeContainers) Clone() Containers {
return nbtc
}
func (btc *bTreeContainers) First() (key uint64, c *Container) {
if btc.tree.Len() == 0 {
return 0, nil
}
return btc.tree.First()
}
func (btc *bTreeContainers) Last() (key uint64, c *Container) {
if btc.tree.Len() == 0 {
return 0, nil

View file

@ -15,6 +15,7 @@
package roaring
import (
"reflect"
"testing"
)
@ -98,5 +99,46 @@ func testContainersIterator(cs Containers, t *testing.T) {
if itr.Next() {
t.Fatalf("itr should be done, but got true")
}
}
func TestContainers(t *testing.T) {
cs := NewFileBitmap().Containers
first := NewContainerArray([]uint16{1, 2, 3})
last := NewContainerArray([]uint16{1, 2, 3, 4, 5, 6})
cs.Put(3, first)
cs.Put(6, last)
key, container := cs.First()
if key != 3 {
t.Fatalf("cs.First key 3 != %d", key)
}
if !reflect.DeepEqual(first, container) {
t.Fatalf("cs.First container %v != %v", first, container)
}
key, container = cs.Last()
if key != 6 {
t.Fatalf("cs.First key 6 != %d", key)
}
if !reflect.DeepEqual(last, container) {
t.Fatalf("cs.First container %v != %v", last, container)
}
cs = NewFileBitmap().Containers
key, container = cs.First()
if key != 0 {
t.Fatalf("cs.First key 0 != %d", key)
}
if nil != container {
t.Fatalf("cs.First container nil != %v", container)
}
key, container = cs.Last()
if key != 0 {
t.Fatalf("cs.First key 0 != %d", key)
}
if nil != container {
t.Fatalf("cs.First container nil != %v", container)
}
}

View file

@ -95,6 +95,9 @@ type Containers interface {
// Clone does a deep copy of Containers, including cloning all containers contained.
Clone() Containers
// First returns the lowest key and associated container.
First() (key uint64, c *Container)
// Last returns the highest key and associated container.
Last() (key uint64, c *Container)
@ -320,6 +323,18 @@ func (b *Bitmap) remove(v uint64) bool {
return c.remove(lowbits(v))
}
// Min returns the lowest value in the bitmap.
// Second return value is true if containers exist in the bitmap.
func (b *Bitmap) Min() (uint64, bool) {
if b.Containers.Size() == 0 {
return 0, false
}
hb, c := b.Containers.First()
lb, ok := c.min()
return hb<<16 | uint64(lb), ok
}
// Max returns the highest value in the bitmap.
// Returns zero if the bitmap is empty.
func (b *Bitmap) Max() uint64 {
@ -1860,6 +1875,17 @@ func (c *Container) runRemove(v uint16) bool {
return true
}
// min returns the minimum value in the container.
func (c *Container) min() (uint16, bool) {
if c.isArray() {
return c.arrayMin()
} else if c.isRun() {
return c.runMin()
} else {
return c.bitmapMin()
}
}
// max returns the maximum value in the container.
func (c *Container) max() uint16 {
if c.isArray() {
@ -1871,6 +1897,15 @@ func (c *Container) max() uint16 {
}
}
// Second result value is true if array is non-empty.
func (c *Container) arrayMin() (uint16, bool) {
array := c.array()
if len(array) == 0 {
return 0, false
}
return array[0], true
}
func (c *Container) arrayMax() uint16 {
array := c.array()
if len(array) == 0 {
@ -1879,21 +1914,43 @@ func (c *Container) arrayMax() uint16 {
return array[len(array)-1]
}
// Second result value is true if array is non-empty.
func (c *Container) bitmapMin() (uint16, bool) {
bitmap := c.bitmap()
for i := 0; i < len(bitmap); i++ {
// If value is zero then skip.
v := bitmap[i]
if v != 0 {
r := bits.TrailingZeros64(v)
return uint16(r + i*64), true
}
}
return 0, false
}
func (c *Container) bitmapMax() uint16 {
// Search bitmap in reverse order.
bitmap := c.bitmap()
for i := len(bitmap); i > 0; i-- {
for i := len(bitmap) - 1; i > 0; i-- {
// If value is zero then skip.
v := bitmap[i-1]
v := bitmap[i]
if v != 0 {
r := bits.LeadingZeros64(v)
return uint16((i-1)*64 + 63 - r)
return uint16(i*64 + 63 - r)
}
}
return 0
}
// Second result value is true if array is non-empty.
func (c *Container) runMin() (uint16, bool) {
runs := c.runs()
if len(runs) == 0 {
return 0, false
}
return runs[0].start, true
}
func (c *Container) runMax() uint16 {
runs := c.runs()
if len(runs) == 0 {

View file

@ -309,6 +309,32 @@ func TestBitmap_Max(t *testing.T) {
}
}
// Ensure bitmap can return the lowest value.
func TestBitmap_Min(t *testing.T) {
bm := roaring.NewFileBitmap()
for i := uint64(100000); i > 0; i-- {
if _, err := bm.Add(i); err != nil {
t.Fatalf("adding bits: %v", err)
}
v, ok := bm.Min()
if !ok {
t.Fatalf("ok should be true")
}
if v != i {
t.Fatalf("min: got=%d; want=%d", v, i)
}
}
// empty bitmap
bm = roaring.NewFileBitmap()
_, ok := bm.Min()
if ok {
t.Fatalf("ok should be false")
}
}
// Ensure CountRange is correct even if rangekey is prior to initial container.
func TestBitmap_BitmapCountRangeEdgeCase(t *testing.T) {
s := uint64(2009 * pilosa.ShardWidth)