From f40ced47fa25ab5b7850c6e4125cf26d471d12c3 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 21 Jan 2020 16:56:33 -0600 Subject: [PATCH] included previous patterns for in place testing --- roaring/roaring.go | 439 ++++++++++++++++++++++++++++++- roaring/roaring_internal_test.go | 133 ++++++++++ roaring/roaring_test.go | 48 ++++ 3 files changed, 618 insertions(+), 2 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 19a671dbf..ed1b0bbe7 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -25,6 +25,7 @@ import ( "sort" "unsafe" + "github.com/pilosa/pilosa" "github.com/pkg/errors" ) @@ -995,8 +996,6 @@ func (b *Bitmap) unionInPlace(others ...*Bitmap) { // Difference returns the difference of b and other. func (b *Bitmap) Difference(other *Bitmap) *Bitmap { output := NewBitmap() - output.Source = b.Source - iiter, _ := b.Containers.Iterator(0) jiter, _ := other.Containers.Iterator(0) i, j := iiter.Next(), jiter.Next() @@ -2668,6 +2667,7 @@ func (c *Container) arrayRemove(v uint16) (*Container, bool) { } // removing the last item? we can just return the empty container. if c.N() == 1 { + c.n = 0 return nil, true } c = c.Thaw() @@ -2684,6 +2684,7 @@ func (c *Container) bitmapRemove(v uint16) (*Container, bool) { } // removing the last item? we can just return the empty container. if c.N() == 1 { + c.n = 0 return nil, true } c = c.Thaw() @@ -2709,6 +2710,7 @@ func (c *Container) runRemove(v uint16) (*Container, bool) { } // removing the last item? we can just return the empty container. if c.N() == 1 { + c.n = 0 return nil, true } c = c.Thaw() @@ -5451,3 +5453,436 @@ type containerUnionSummaryStats struct { // extremely dense containers. hasMaxRange bool } + +func (b *Bitmap) DifferenceInPlace(others ...*Bitmap) { + var ( + requiredSliceSize = len(others) + // To avoid having to allocate a slice everytime, if the number of bitmaps + // being unioned is small enough we can just use this stack-allocated array. + staticHandledIters = [20]handledIter{} + bitmapIters handledIters + target = b + removeContinerKeys = [pilosa.ShardWidth / (1 << 16)]uint64{} //i need a better guess for this + ) + + if requiredSliceSize <= 20 { + bitmapIters = staticHandledIters[:0] + } else { + bitmapIters = make(handledIters, 0, requiredSliceSize) + } + + for _, other := range others { + otherIter, _ := other.Containers.Iterator(0) + if otherIter.Next() { + bitmapIters = append(bitmapIters, handledIter{ + iter: otherIter, + hasNext: true, + }) + } + } + + targetItr, _ := target.Containers.Iterator(0) + // Go through all the containers and remove the other bits + n := 0 + for targetItr.Next() { + targetKey, curContainer := targetItr.Value() + // Loop until every iters current value has been handled. + + for _, iIter := range bitmapIters { + if !iIter.hasNext { + continue + } + iKey, iContainer := iIter.iter.Value() + if targetKey == iKey { + curContainer.differenceInPlaceO(iContainer) + if curContainer.N() == 0 { //according to comments N = 1-count, so N should == 1 if 0 elements + removeContinerKeys[n] = iKey + n++ + break + } + iIter.hasNext = iIter.iter.Next() + } else if targetKey > iKey { + iIter.hasNext = iIter.iter.Next() + } + } + } + + for i := 0; i < n; i++ { + b.Containers.Remove(removeContinerKeys[i]) + + } + target.Containers.Repair() +} + +func (c *Container) differenceInPlace(other *Container) { + if other.isArray() { + for _, v := range other.array() { + c.remove(v) + } + } else if other.isBitmap() { + for i, word := range other.bitmap() { + for word != 0 { + t := word & -word + c.remove(uint16((i*64 + int(popcount(t-1))))) + word ^= t + } + } + } else if other.isRun() { + for _, r := range other.runs() { + for v := int(r.start); v <= int(r.last); v++ { + c.remove(uint16(v)) + } + } + } +} + +func (c *Container) differenceInPlaceO(other *Container) { + if other.isArray() { + if c.isArray() { + differenceArrayArrayInPlace(c, other) + } else if c.isBitmap() { + differenceBitmapArrayInPlace(c, other) + } else if c.isRun() { + differenceRunArrayInPlace(c, other) + } + } else if other.isBitmap() { + if c.isArray() { + differenceArrayBitmapInPlace(c, other) + } else if c.isBitmap() { + differenceBitmapBitmapInPlace(c, other) + } else if c.isRun() { + differenceRunBitmapInPlace(c, other) + } + } else if other.isRun() { + if c.isArray() { + differenceArrayRunInPlace(c, other) + } else if c.isBitmap() { + differenceBitmapRunInPlace(c, other) + } else if c.isRun() { + differenceRunRunInPlace(c, other) + } + } +} + +func differenceArrayArrayInPlace(output, other *Container) { + statsHit("differenceInPlace/ArrayArray") + aa, ab := output.array(), other.array() + na, nb := len(aa), len(ab) + n := 0 + for i, j := 0, 0; i < na; { + va := aa[i] + if j >= nb { + aa[n] = va + n++ + i++ + continue + } + + vb := ab[j] + if va < vb { + aa[n] = va + n++ + i++ + } else if va > vb { + j++ + } else { + i, j = i+1, j+1 + } + } + aa = aa[:n] + output.setArray(aa) +} +func differenceArrayBitmapInPlace(c, other *Container) { + statsHit("differenceInPlace/ArrayBitmap") + aa := c.array() + n := 0 + bitmap := other.bitmap() + for _, va := range aa { + bmidx := va / 64 + bidx := va % 64 + mask := uint64(1) << bidx + b := bitmap[bmidx] + + if mask&^b > 0 { + aa[n] = va + n++ + } + } + aa = aa[:n] + c.setArray(aa) +} + +func differenceArrayRunInPlace(c, other *Container) { + statsHit("differenceInPlace/ArrayRun") + + i := 0 // array index + j := 0 // run index + aa, rb := c.array(), other.runs() + if len(aa) == 0 || len(rb) == 0 { + return + } + n := 0 + + // handle overlap + for i < len(aa) { + + // keep all array elements before beginning of runs + if aa[i] < rb[j].start { + aa[n] = aa[i] + n++ + i++ + continue + } + + // if array element in run, skip it + if aa[i] >= rb[j].start && aa[i] <= rb[j].last { + i++ + continue + } + + // if array element larger than current run, check next run + if aa[i] > rb[j].last { + j++ + if j == len(rb) { + break + } + } + } + for ; i < len(aa); i++ { + aa[n] = aa[i] + n++ + } + aa = aa[:n] + c.setArray(aa) +} + +func differenceBitmapArrayInPlace(c, other *Container) { + statsHit("differenceInPlace/BitmapArray") + bitmap := c.bitmap() + + n := c.N() + for _, v := range other.array() { + if c.bitmapContains(v) { + bitmap[v/64] &^= (uint64(1) << uint(v%64)) + n-- + } + } + c.setN(n) + if n < ArrayMaxSize { + c.bitmapToArray() // With This Work + } +} + +func differenceBitmapBitmapInPlace(c, other *Container) { + statsHit("differenceInPlace/BitmapBitmap") + // local variables added to prevent BCE checks in loop + // see https://go101.org/article/bounds-check-elimination.html + + var ( + ab = c.bitmap()[:bitmapN] + bb = other.bitmap()[:bitmapN] + n int32 + ) + + for i := 0; i < bitmapN; i++ { + ab[i] = ab[i] & (^bb[i]) + n += int32(popcount(ab[i])) + } + c.setN(n) + if n < ArrayMaxSize { + c.bitmapToArray() // Will this work? + } +} +func differenceBitmapRunInPlace(c, other *Container) { + statsHit("differenceInPlace/BitmapRun") + for _, run := range other.runs() { + c.bitmapZeroRange(uint64(run.start), uint64(run.last)+1) + } +} +func differenceRunArrayInPlace(c, other *Container) { + statsHit("differenceInPlace/RunArray") + ra, ab := c.runs(), other.array() + if len(ab) == 0 { + return + } + runs := make([]interval16, 0, len(ra)) + bidx := 0 + vb := ab[bidx] + +RUNLOOP: + for _, run := range ra { + start := run.start + for vb < run.start { + bidx++ + if bidx >= len(ab) { + break + } + vb = ab[bidx] + } + for vb >= run.start && vb <= run.last { + if vb == start { + if vb == 65535 { // overflow + break RUNLOOP + } + start++ + bidx++ + if bidx >= len(ab) { + break + } + vb = ab[bidx] + continue + } + runs = append(runs, interval16{start: start, last: vb - 1}) + if vb == 65535 { // overflow + break RUNLOOP + } + start = vb + 1 + bidx++ + if bidx >= len(ab) { + break + } + vb = ab[bidx] + } + + if start <= run.last { + runs = append(runs, interval16{start: start, last: run.last}) + } + } + c.setRuns(runs) + c.n = 0 + for _, run := range runs { + c.n += int32(run.last-run.start) + 1 + } + c.optimize() +} +func differenceRunBitmapInPlace(c, other *Container) { + statsHit("differenceInPlace/RunBitmap") + ra := c.runs() + // If a is full, difference is the flip of b. + if len(ra) > 0 && ra[0].start == 0 && ra[0].last == 65535 { + clone := other.Clone() + bitmap := clone.bitmap() + for i, word := range other.bitmap() { + bitmap[i] = ^word + } + c.setTyp(containerBitmap) + c.setMapped(false) + c.setBitmap(bitmap) + c.setN(c.count()) + return + } + runs := make([]interval16, 0, len(ra)) + for _, inputRun := range ra { + run := inputRun + add := true + for bit := inputRun.start; bit <= inputRun.last; bit++ { + if other.bitmapContains(bit) { + if run.start == bit { + if bit == 65535 { //overflow + add = false + } + + run.start++ + } else if bit == run.last { + run.last-- + } else { + run.last = bit - 1 + if run.last >= run.start { + runs = append(runs, run) + } + run.start = bit + 1 + run.last = inputRun.last + } + if run.start > run.last { + break + } + } + + if bit == 65535 { //overflow + break + } + } + if run.start <= run.last { + if add { + runs = append(runs, run) + } + } + } + + c.setRuns(runs) + c.n = 0 + for _, run := range runs { + c.n += int32(run.last-run.start) + 1 + } + if c.N() < ArrayMaxSize && int32(len(runs)) > c.N()/2 { + c.runToArray() + } else if len(runs) > runMaxSize { + c.runToBitmap() + } +} +func differenceRunRunInPlace(c, other *Container) { + statsHit("differenceInPlace/RunRun") + + ra, rb := c.runs(), other.runs() + if len(ra) == 0 || len(rb) == 0 { + return + } + apos := 0 // current a-run index + bpos := 0 // current b-run index + astart := ra[apos].start + alast := ra[apos].last + bstart := rb[bpos].start + blast := rb[bpos].last + alen := len(ra) + blen := len(rb) + + runs := make([]interval16, 0, alen+blen) // TODO allocate max then truncate? or something else + // cardinality upper bound: sum of number of runs + // each B-run could split an A-run in two, up to len(b.runs) times + + for apos < alen && bpos < blen { + switch { + case alast < bstart: + // current A-run entirely precedes current B-run: keep full A-run, advance to next A-run + runs = append(runs, interval16{start: astart, last: alast}) + apos++ + if apos < alen { + astart = ra[apos].start + alast = ra[apos].last + } + case blast < astart: + // current B-run entirely precedes current A-run: advance to next B-run + bpos++ + if bpos < blen { + bstart = rb[bpos].start + blast = rb[bpos].last + } + default: + // overlap + if astart < bstart { + runs = append(runs, interval16{start: astart, last: bstart - 1}) + } + if alast > blast { + astart = blast + 1 + } else { + apos++ + if apos < alen { + astart = ra[apos].start + alast = ra[apos].last + } + } + } + } + if apos < alen { + runs = append(runs, interval16{start: astart, last: alast}) + apos++ + if apos < alen { + runs = append(runs, ra[apos:]...) + } + } + c.setRuns(runs) + c.n = 0 + for _, run := range runs { + c.n += int32(run.last-run.start) + 1 + } +} diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 0ecd73021..48b0a4b0a 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -2709,6 +2709,14 @@ func unionInPlaceWrapper(a, b *Container) *Container { out.UnionInPlace(B) return out.Containers.Get(0) } +func differenceInPlaceWrapper(a, b *Container) *Container { + out := NewBitmap() + out.Containers.Put(0, a.Clone()) + B := NewBitmap() + B.Containers.Put(0, b) + out.DifferenceInPlace(B) + return out.Containers.Get(0) +} func TestContainerCombinations(t *testing.T) { @@ -3283,6 +3291,120 @@ func TestContainerCombinations(t *testing.T) { {flip, "outerBitsSet", "", "innerBitsSet"}, {flip, "oddBitsSet", "", "evenBitsSet"}, {flip, "evenBitsSet", "", "oddBitsSet"}, + + // differenceInPlace + {differenceInPlaceWrapper, "empty", "empty", "empty"}, + {differenceInPlaceWrapper, "empty", "full", "empty"}, + {differenceInPlaceWrapper, "empty", "firstBitSet", "empty"}, + {differenceInPlaceWrapper, "empty", "lastBitSet", "empty"}, + {differenceInPlaceWrapper, "empty", "firstBitUnset", "empty"}, + {differenceInPlaceWrapper, "empty", "lastBitUnset", "empty"}, + {differenceInPlaceWrapper, "empty", "innerBitsSet", "empty"}, + {differenceInPlaceWrapper, "empty", "outerBitsSet", "empty"}, + {differenceInPlaceWrapper, "empty", "oddBitsSet", "empty"}, + {differenceInPlaceWrapper, "empty", "evenBitsSet", "empty"}, + // + {differenceInPlaceWrapper, "full", "empty", "full"}, + {differenceInPlaceWrapper, "full", "full", "empty"}, + {differenceInPlaceWrapper, "full", "firstBitSet", "firstBitUnset"}, + {differenceInPlaceWrapper, "full", "lastBitSet", "lastBitUnset"}, + {differenceInPlaceWrapper, "full", "firstBitUnset", "firstBitSet"}, + {differenceInPlaceWrapper, "full", "lastBitUnset", "lastBitSet"}, + {differenceInPlaceWrapper, "full", "innerBitsSet", "outerBitsSet"}, + {differenceInPlaceWrapper, "full", "outerBitsSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "full", "oddBitsSet", "evenBitsSet"}, + {differenceInPlaceWrapper, "full", "evenBitsSet", "oddBitsSet"}, + // + {differenceInPlaceWrapper, "firstBitSet", "empty", "firstBitSet"}, + {differenceInPlaceWrapper, "firstBitSet", "full", "empty"}, + {differenceInPlaceWrapper, "firstBitSet", "firstBitSet", "empty"}, + {differenceInPlaceWrapper, "firstBitSet", "lastBitSet", "firstBitSet"}, + {differenceInPlaceWrapper, "firstBitSet", "firstBitUnset", "firstBitSet"}, + {differenceInPlaceWrapper, "firstBitSet", "lastBitUnset", "empty"}, + {differenceInPlaceWrapper, "firstBitSet", "innerBitsSet", "firstBitSet"}, + {differenceInPlaceWrapper, "firstBitSet", "outerBitsSet", "empty"}, + {differenceInPlaceWrapper, "firstBitSet", "oddBitsSet", "firstBitSet"}, + {differenceInPlaceWrapper, "firstBitSet", "evenBitsSet", "empty"}, + // + {differenceInPlaceWrapper, "lastBitSet", "empty", "lastBitSet"}, + {differenceInPlaceWrapper, "lastBitSet", "full", "empty"}, + {differenceInPlaceWrapper, "lastBitSet", "firstBitSet", "lastBitSet"}, + {differenceInPlaceWrapper, "lastBitSet", "lastBitSet", "empty"}, + {differenceInPlaceWrapper, "lastBitSet", "firstBitUnset", "empty"}, + {differenceInPlaceWrapper, "lastBitSet", "lastBitUnset", "lastBitSet"}, + {differenceInPlaceWrapper, "lastBitSet", "innerBitsSet", "lastBitSet"}, + {differenceInPlaceWrapper, "lastBitSet", "outerBitsSet", "empty"}, + {differenceInPlaceWrapper, "lastBitSet", "oddBitsSet", "empty"}, + {differenceInPlaceWrapper, "lastBitSet", "evenBitsSet", "lastBitSet"}, + // + {differenceInPlaceWrapper, "firstBitUnset", "empty", "firstBitUnset"}, + {differenceInPlaceWrapper, "firstBitUnset", "full", "empty"}, + {differenceInPlaceWrapper, "firstBitUnset", "firstBitSet", "firstBitUnset"}, + {differenceInPlaceWrapper, "firstBitUnset", "lastBitSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "firstBitUnset", "firstBitUnset", "empty"}, + {differenceInPlaceWrapper, "firstBitUnset", "lastBitUnset", "lastBitSet"}, + {differenceInPlaceWrapper, "firstBitUnset", "innerBitsSet", "lastBitSet"}, + {differenceInPlaceWrapper, "firstBitUnset", "outerBitsSet", "innerBitsSet"}, + //{differenceInPlaceWrapper, "firstBitUnset", "oddBitsSet", ""}, + {differenceInPlaceWrapper, "firstBitUnset", "evenBitsSet", "oddBitsSet"}, + // + {differenceInPlaceWrapper, "lastBitUnset", "empty", "lastBitUnset"}, + {differenceInPlaceWrapper, "lastBitUnset", "full", "empty"}, + {differenceInPlaceWrapper, "lastBitUnset", "firstBitSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "lastBitUnset", "lastBitSet", "lastBitUnset"}, + {differenceInPlaceWrapper, "lastBitUnset", "firstBitUnset", "firstBitSet"}, + {differenceInPlaceWrapper, "lastBitUnset", "lastBitUnset", "empty"}, + {differenceInPlaceWrapper, "lastBitUnset", "innerBitsSet", "firstBitSet"}, + {differenceInPlaceWrapper, "lastBitUnset", "outerBitsSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "lastBitUnset", "oddBitsSet", "evenBitsSet"}, + //{differenceInPlaceWrapper, "lastBitUnset", "evenBitsSet", ""}, + // + {differenceInPlaceWrapper, "innerBitsSet", "empty", "innerBitsSet"}, + {differenceInPlaceWrapper, "innerBitsSet", "full", "empty"}, + {differenceInPlaceWrapper, "innerBitsSet", "firstBitSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "innerBitsSet", "lastBitSet", "innerBitsSet"}, + {differenceInPlaceWrapper, "innerBitsSet", "firstBitUnset", "empty"}, + {differenceInPlaceWrapper, "innerBitsSet", "lastBitUnset", "empty"}, + {differenceInPlaceWrapper, "innerBitsSet", "innerBitsSet", "empty"}, + {differenceInPlaceWrapper, "innerBitsSet", "outerBitsSet", "innerBitsSet"}, + //{differenceInPlaceWrapper, "innerBitsSet", "oddBitsSet", ""}, + //{differenceInPlaceWrapper, "innerBitsSet", "evenBitsSet", ""}, + // + {differenceInPlaceWrapper, "outerBitsSet", "empty", "outerBitsSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "full", "empty"}, + {differenceInPlaceWrapper, "outerBitsSet", "firstBitSet", "lastBitSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "lastBitSet", "firstBitSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "firstBitUnset", "firstBitSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "lastBitUnset", "lastBitSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "innerBitsSet", "outerBitsSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "outerBitsSet", "empty"}, + {differenceInPlaceWrapper, "outerBitsSet", "oddBitsSet", "firstBitSet"}, + {differenceInPlaceWrapper, "outerBitsSet", "evenBitsSet", "lastBitSet"}, + // + {differenceInPlaceWrapper, "oddBitsSet", "empty", "oddBitsSet"}, + {differenceInPlaceWrapper, "oddBitsSet", "full", "empty"}, + {differenceInPlaceWrapper, "oddBitsSet", "firstBitSet", "oddBitsSet"}, + //{differenceInPlaceWrapper, "oddBitsSet", "lastBitSet", ""}, + {differenceInPlaceWrapper, "oddBitsSet", "firstBitUnset", "empty"}, + {differenceInPlaceWrapper, "oddBitsSet", "lastBitUnset", "lastBitSet"}, + {differenceInPlaceWrapper, "oddBitsSet", "innerBitsSet", "lastBitSet"}, + //{difference, "oddBitsSet", "outerBitsSet", ""}, + {differenceInPlaceWrapper, "oddBitsSet", "oddBitsSet", "empty"}, + {differenceInPlaceWrapper, "oddBitsSet", "evenBitsSet", "oddBitsSet"}, + // + {differenceInPlaceWrapper, "evenBitsSet", "empty", "evenBitsSet"}, + {differenceInPlaceWrapper, "evenBitsSet", "full", "empty"}, + //{difference, "evenBitsSet", "firstBitSet", ""}, + {differenceInPlaceWrapper, "evenBitsSet", "lastBitSet", "evenBitsSet"}, + {differenceInPlaceWrapper, "evenBitsSet", "firstBitUnset", "firstBitSet"}, + {differenceInPlaceWrapper, "evenBitsSet", "lastBitUnset", "empty"}, + {differenceInPlaceWrapper, "evenBitsSet", "innerBitsSet", "firstBitSet"}, + //{difference, "evenBitsSet", "outerBitsSet", ""}, + {differenceInPlaceWrapper, "evenBitsSet", "oddBitsSet", "evenBitsSet"}, + {differenceInPlaceWrapper, "evenBitsSet", "evenBitsSet", "empty"}, + /* + */ + } for _, testOp := range testOps { for _, x := range containerTypes { @@ -3896,3 +4018,14 @@ func TestBitmapAny(t *testing.T) { t.Error("shouldn't be any left") } } + +func TestStuff(t *testing.T) { + a := doContainer(containerRun, runFull()) + b := doContainer(containerBitmap, bitmapFull()) + // b := doContainer(containerBitmap, bitmapLastBitSet()) + r := differenceInPlaceWrapper(a, b) + if r.N() != 0 { + t.Error("fail") + } + +} diff --git a/roaring/roaring_test.go b/roaring/roaring_test.go index 836fd8de3..3d1d70734 100644 --- a/roaring/roaring_test.go +++ b/roaring/roaring_test.go @@ -1739,3 +1739,51 @@ func BenchmarkUnionBulk(b *testing.B) { UnionInPlace(data.a1, data.a2, data.b, data.r1, data.r2) } } + +func TestBitmap_differenceInPlace(t *testing.T) { + bm := testBM() + //the array + arraybm := roaring.NewSliceBitmap() + bitmapbm := roaring.NewSliceBitmap() + smallrunbm := roaring.NewSliceBitmap() + largerunbm := roaring.NewSliceBitmap() + + for i := uint64(0); i < 1024; i += 4 { + _, _ = arraybm.Add((1 << 16) + i) + } + arraybm.Optimize() + + for i := uint64(0); i < 16384; i += 2 { + _, _ = bitmapbm.Add((2 << 16) + i) + } + bitmapbm.Optimize() + //small run + for i := uint64(0); i < 1024; i++ { + _, _ = smallrunbm.Add((3 << 16) + i) + } + smallrunbm.Optimize() + for i := uint64(0); i < 65535; i++ { + _, _ = largerunbm.Add((4 << 16) + i) + } + smallrunbm.Optimize() + bm.DifferenceInPlace(arraybm, bitmapbm, smallrunbm, largerunbm) + if bm.Count() != 0 { + t.Fatalf("expected bitmap to be empty, it wasn't. %d", bm.Count()) + } + bm = testBM() + bm.DifferenceInPlace(bitmapbm, smallrunbm, largerunbm) + if bm.Count() != 256 { + t.Fatalf("expected bitmap to have 256, it wasn't. %d", bm.Count()) + } +} + +func BenchmarkDifferencInPlace(b *testing.B) { + data := getBenchData(b) + for n := 0; n < b.N; n++ { + bm := roaring.NewBitmap() + bm. + UnionInPlace(data.a1, data.a2, data.b, data.r1, data.r2) + bm. + DifferenceInPlace(data.a1, data.r2, data.b, data.r1) + } +}