From 250e8f6f5b704ef8fa567a9cd62696c80d97dd99 Mon Sep 17 00:00:00 2001 From: Matt Jaffee Date: Thu, 25 May 2017 16:53:26 -0500 Subject: [PATCH] implement intersectBitmapRun wowza --- roaring/roaring.go | 47 ++++++++++++++- roaring/roaring_internal_test.go | 99 ++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 516a76f5e..81fce64c6 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1702,9 +1702,52 @@ func intersectRunRun(a, b *container) *container { return output } +// intersectBitmapRun returns an array container of the run container's +// cardinality is < 4096. Otherwise it returns a bitmap container. func intersectBitmapRun(a, b *container) *container { - output := &container{} - // TODO + var output *container + if b.n < 4096 { + // output is array container + output = &container{} + for _, iv := range b.runs { + for i := iv.start; i <= iv.last; i++ { + if a.bitmapContains(i) { + output.array = append(output.array, i) + } + } + } + } else { + // right now this iterates through the runs and sets integers in the + // bitmap that are in the runs. alternately, we could zero out ranges in + // the bitmap which are between runs. + output = &container{ + bitmap: make([]uint64, bitmapN), + } + for j := 0; j < len(b.runs); j++ { + vb := b.runs[j] + i := vb.start / 64 // index into a + vastart := 64 * i + valast := vastart + 63 + for valast >= vb.start && vastart <= vb.last { + if vastart >= vb.start && valast <= vb.last { // a within b + output.bitmap[i] = a.bitmap[i] + } else if vb.start >= vastart && vb.last <= valast { // b within a + var mask uint64 = ((1 << (vb.last - vb.start + 1)) - 1) << (vb.start - vastart) + output.bitmap[i] |= a.bitmap[i] & mask + } else if vastart < vb.start { // a overlaps front of b + offset := 64 - (1 + valast - vb.start) + output.bitmap[i] |= (a.bitmap[i] >> offset) << offset + } else if vb.start < vastart { // b overlaps front of a + offset := 64 - (1 + vb.last - vastart) + output.bitmap[i] |= (a.bitmap[i] << offset) >> offset + } + // update loop vars + i++ + vastart = 64 * i + valast = vastart + 63 + } + } + } return output } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 938c64a83..d31b4e237 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -426,3 +426,102 @@ func TestIntersectRunRun(t *testing.T) { } } + +func TestIntersectBitmapRunBitmap(t *testing.T) { + a := &container{bitmap: make([]uint64, bitmapN)} + b := &container{} + tests := []struct { + bitmap []uint64 + runs []interval32 + exp []uint64 + }{ + { + bitmap: []uint64{1}, + runs: []interval32{{start: 0, last: 0}, {start: 2, last: 5}, {start: 62, last: 71}, {start: 77, last: 4096}}, + exp: []uint64{1}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}}, + exp: []uint64{2}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}, {start: 10, last: 12}, {start: 61, last: 77}}, + exp: []uint64{0xe000000000001C02}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}, {start: 61, last: 77}}, + exp: []uint64{0xE000000000000002, 0x00000000000003FFF}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 1, 1, 1, 0xA, 1, 1, 0, 1}, + runs: []interval32{{start: 63, last: 10000}}, + exp: []uint64{0x8000000000000000, 1, 1, 1, 0xA, 1, 1, 0, 1}, + }, + } + for i, test := range tests { + for i, v := range test.bitmap { + a.bitmap[i] = v + } + b.runs = test.runs + b.n = 4097 // ;) + exp := make([]uint64, bitmapN) + for i, v := range test.exp { + exp[i] = v + } + ret := intersectBitmapRun(a, b) + if !reflect.DeepEqual(ret.bitmap, exp) { + t.Fatalf("test #%v expected %v, but got %v", i, exp, ret.bitmap) + } + } + +} + +func TestIntersectBitmapRunArray(t *testing.T) { + a := &container{bitmap: make([]uint64, bitmapN)} + b := &container{} + tests := []struct { + bitmap []uint64 + runs []interval32 + exp []uint32 + }{ + { + bitmap: []uint64{1}, + runs: []interval32{{start: 0, last: 0}, {start: 2, last: 5}, {start: 62, last: 71}, {start: 77, last: 4096}}, + exp: []uint32{0}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}}, + exp: []uint32{1}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}, {start: 10, last: 12}, {start: 61, last: 77}}, + exp: []uint32{1, 10, 11, 12, 61, 62, 63}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + runs: []interval32{{start: 1, last: 1}, {start: 61, last: 68}}, + exp: []uint32{1, 61, 62, 63, 64, 65, 66, 67, 68}, + }, + { + bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 1, 1, 1, 0xA, 1, 1, 0, 1}, + runs: []interval32{{start: 63, last: 10000}}, + exp: []uint32{63, 64, 128, 192, 257, 259, 320, 384, 512}, + }, + } + for i, test := range tests { + for i, v := range test.bitmap { + a.bitmap[i] = v + } + b.runs = test.runs + ret := intersectBitmapRun(a, b) + if !reflect.DeepEqual(ret.array, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.array) + } + } + +}