diff --git a/roaring/roaring.go b/roaring/roaring.go index ed3863dfc..76aa9a6b3 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1715,7 +1715,7 @@ func intersectRunRun(a, b *container) *container { i++ } } - if n < 4096 && len(output.runs) > n/2 { + if output.n < 4096 && len(output.runs) > output.n/2 { output.runToArray() } else if len(output.runs) > RunMaxSize { output.runToBitmap() @@ -1737,6 +1737,7 @@ func intersectBitmapRun(a, b *container) *container { } } } + output.n = len(output.array) } 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 @@ -1752,15 +1753,22 @@ func intersectBitmapRun(a, b *container) *container { for valast >= vb.start && vastart <= vb.last { if vastart >= vb.start && valast <= vb.last { // a within b output.bitmap[i] = a.bitmap[i] + output.n += int(popcnt(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 + bits := a.bitmap[i] & mask + output.bitmap[i] |= bits + output.n += int(popcnt(bits)) } else if vastart < vb.start { // a overlaps front of b offset := 64 - (1 + valast - vb.start) - output.bitmap[i] |= (a.bitmap[i] >> offset) << offset + bits := (a.bitmap[i] >> offset) << offset + output.bitmap[i] |= bits + output.n += int(popcnt(bits)) } else if vb.start < vastart { // b overlaps front of a offset := 64 - (1 + vb.last - vastart) - output.bitmap[i] |= (a.bitmap[i] << offset) >> offset + bits := (a.bitmap[i] << offset) >> offset + output.bitmap[i] |= bits + output.n += int(popcnt(bits)) } // update loop vars i++ @@ -1768,6 +1776,9 @@ func intersectBitmapRun(a, b *container) *container { valast = vastart + 63 } } + if output.n < 4096 { + output.bitmapToArray() + } } return output } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index a106d3d8b..efba7d85c 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -479,31 +479,37 @@ func TestIntersectBitmapRunBitmap(t *testing.T) { bitmap []uint64 runs []interval32 exp []uint64 + expN int }{ { bitmap: []uint64{1}, runs: []interval32{{start: 0, last: 0}, {start: 2, last: 5}, {start: 62, last: 71}, {start: 77, last: 4096}}, exp: []uint64{1}, + expN: 1, }, { bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, runs: []interval32{{start: 1, last: 1}}, exp: []uint64{2}, + expN: 1, }, { bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, runs: []interval32{{start: 1, last: 1}, {start: 10, last: 12}, {start: 61, last: 77}}, exp: []uint64{0xe000000000001C02}, + expN: 7, }, { bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, runs: []interval32{{start: 1, last: 1}, {start: 61, last: 77}}, exp: []uint64{0xE000000000000002, 0x00000000000003FFF}, + expN: 18, }, { 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}, + expN: 9, }, } for i, test := range tests { @@ -517,9 +523,15 @@ func TestIntersectBitmapRunBitmap(t *testing.T) { exp[i] = v } ret := intersectBitmapRun(a, b) + if ret.isArray() { + ret.arrayToBitmap() + } if !reflect.DeepEqual(ret.bitmap, exp) { t.Fatalf("test #%v expected %v, but got %v", i, exp, ret.bitmap) } + if ret.n != test.expN { + t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n) + } } } @@ -531,31 +543,37 @@ func TestIntersectBitmapRunArray(t *testing.T) { bitmap []uint64 runs []interval32 exp []uint32 + expN int }{ { bitmap: []uint64{1}, runs: []interval32{{start: 0, last: 0}, {start: 2, last: 5}, {start: 62, last: 71}, {start: 77, last: 4096}}, exp: []uint32{0}, + expN: 1, }, { bitmap: []uint64{0xFFFFFFFFFFFFFFFF}, runs: []interval32{{start: 1, last: 1}}, exp: []uint32{1}, + expN: 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}, + expN: 7, }, { 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}, + expN: 9, }, { 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}, + expN: 9, }, } for i, test := range tests { @@ -567,6 +585,9 @@ func TestIntersectBitmapRunArray(t *testing.T) { if !reflect.DeepEqual(ret.array, test.exp) { t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.array) } + if ret.n != test.expN { + t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, ret.n) + } } }