diff --git a/roaring/roaring.go b/roaring/roaring.go index f24e13fdd..bae6b60bb 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1363,12 +1363,65 @@ func (c *container) arrayToBitmap() { c.mapped = false } +// runToBitmap converts from RLE format to bitmap format func (c *container) runToBitmap() { - // TODO + c.bitmap = make([]uint64, bitmapN) + for _, r := range c.runs { + // TODO is there a faster way ?!?!! + for v := r.start; v <= r.last; v++ { + c.bitmap[int(v)/64] |= (uint64(1) << uint(v%64)) + } + } + c.runs = nil + c.mapped = false } +// bitmapToRun converts from bitmap format to RLE format func (c *container) bitmapToRun() { - // TODO + numRuns := c.n // TODO compute properly + c.runs = make([]interval32, 0, numRuns) + // TODO return early if no runs + + current := c.bitmap[0] + var i, start, last uint32 + for { + // skip while empty + for current == 0 && i < bitmapN-1 { + i++ + current = c.bitmap[i] + } + + if current == 0 { + break + } + currentStart := uint32(trailingZeroN(current)) + start = 64 * i + currentStart + + // pad LSBs with 1s + current = current | (current-1) + + // find next 0 + for current == 0xFFFFFFFFFFFFFFFF && i < bitmapN-1 { + i++ + current = c.bitmap[i] + } + + last = 0 + if current == 0xFFFFFFFFFFFFFFFF { + last = 64*i + 64 // TODO verify + c.runs = append(c.runs, interval32{start, last-1}) + break + } + currentLast := uint32(trailingZeroN(^current)) + last = 64 * i + currentLast + c.runs = append(c.runs, interval32{start, last-1}) + + // pad LSBs with 0s + current = current & (current+1) + } + + c.bitmap = nil + c.mapped = false } // arrayToRun converts from array format to RLE format diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 1d58c9b01..e6643e5f5 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -731,6 +731,75 @@ func TestBitmapSetRange(t *testing.T) { } } +func TestRunToBitmap(t *testing.T) { + a := &container{} + tests := []struct { + runs []interval32 + exp []uint64 + }{ + { + runs: []interval32{{start: 0, last: 0}}, + exp: []uint64{1}, + }, + { + runs: []interval32{{start: 0, last: 4}}, + exp: []uint64{31}, + }, + { + runs: []interval32{{start: 2, last: 2}, {start: 5, last: 7}, {start: 13, last: 14}, {start: 17, last: 17}}, + exp: []uint64{155876}, + }, + } + + for i, test := range tests { + exp := make([]uint64, bitmapN) + for i, v := range test.exp { + exp[i] = v + } + + a.runs = test.runs + a.runToBitmap() + if !reflect.DeepEqual(a.bitmap, exp) { + t.Logf("test #%v expected %v, but got (%d) %v", i, exp, a.bitmap) + } + } +} + +func TestBitmapToRun(t *testing.T) { + a := &container{} + tests := []struct { + bitmap []uint64 + exp []interval32 + }{ + { + bitmap: []uint64{1}, + exp: []interval32{{start: 0, last: 0}}, + }, + { + bitmap: []uint64{31}, + exp: []interval32{{start: 0, last: 4}}, + }, + { + bitmap: []uint64{155876}, + exp: []interval32{{start: 2, last: 2}, {start: 5, last: 7}, {start: 13, last: 14}, {start: 17, last: 17}}, + }, + } + + for i, test := range tests { + a.bitmap = make([]uint64, bitmapN) + n := 0 + for i, v := range test.bitmap { + a.bitmap[i] = v + n += int(popcount(v)) + } + a.n = n + a.bitmapToRun() + if !reflect.DeepEqual(a.runs, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, a.runs) + } + } +} + func TestArrayToRun(t *testing.T) { a := &container{} tests := []struct {