diff --git a/roaring/roaring.go b/roaring/roaring.go index ab58f293c..240fdbc33 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -101,7 +101,6 @@ func (b *Bitmap) Add(a ...uint64) (changed bool, err error) { return changed, nil } - func (b *Bitmap) add(v uint64) bool { hb := highbits(v) i := search64(b.keys, hb) @@ -706,6 +705,34 @@ func (b *Bitmap) Check() error { return a } +//Perform a logical negate of the bits in the range [start,end]. +func (b *Bitmap) Flip(start, end uint64) *Bitmap { + result := NewBitmap() + itr := b.Iterator() + v, eof := itr.Next() + //copy over previous bits. + for v < start && !eof { + result.add(v) + v, eof = itr.Next() + } + //flip bits in range . + for i := start; i <= end; i++ { + if eof { + result.add(i) + } else if v == i { + v, eof = itr.Next() + } else { + result.add(i) + } + } + //add remaining. + for !eof { + result.add(v) + v, eof = itr.Next() + } + return result +} + // BitmapInfo represents a point-in-time snapshot of bitmap stats. type BitmapInfo struct { OpN int diff --git a/roaring/roaring_test.go b/roaring/roaring_test.go index fb3517495..bd1ef5954 100644 --- a/roaring/roaring_test.go +++ b/roaring/roaring_test.go @@ -15,8 +15,8 @@ package roaring_test import ( - "bytes" "fmt" + "bytes" "math" "math/rand" "reflect" @@ -199,6 +199,69 @@ func TestBitmap_Xor_BitmapBitmap(t *testing.T) { } } +// Ensure bitmap contents alternate. +func TestBitmap_Flip_Empty(t *testing.T) { + bm := roaring.NewBitmap() + results := bm.Flip(0, 10) + if n := results.Count(); n != 11 { + t.Fatalf("unexpected n: %d", n) + } + results = results.Flip(0, 10) + if n := results.Count(); n != 0 { + t.Fatalf("unexpected n: %d", n) + } +} + +// Test Subrange Flip should not affect bits outside of Range +func TestBitmap_Flip_Array(t *testing.T) { + bm := roaring.NewBitmap(0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024) + results := bm.Flip(0, 4) + if !reflect.DeepEqual(results.Slice(), []uint64{8, 16, 32, 64, 128, 256, 512, 1024}) { + t.Fatalf("unexpected %v ", results.Slice()) + } + results = results.Flip(0, 4) + if !reflect.DeepEqual(results.Slice(), []uint64{0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024}) { + t.Fatalf("unexpected %v ", results.Slice()) + } + +} + +// Ensure Flip works with underlying Bitmap container. +func TestBitmap_Flip_Bitmap(t *testing.T) { + bm := roaring.NewBitmap() + size := uint64(10000) + for i := uint64(0); i < size; i += 2 { + bm.Add(i) + } + results := bm.Flip(0, size-1) + if n := results.Count(); n != size/2 { + t.Fatalf("unexpected n: %d", n) + } + results = results.Flip(0, size-1) //flipping back should be the same + if n := results.Count(); n != size/2 { + t.Fatalf("unexpected n: %d", n) + } +} + +// Verify Flip works correctly with in different regions of bitmap, beginning, middle, and end. +func TestBitmap_Flip_After(t *testing.T) { + bm := roaring.NewBitmap(0, 2, 4, 8) + results := bm.Flip(9, 10) + + if !reflect.DeepEqual(results.Slice(), []uint64{0, 2, 4, 8, 9, 10}) { + t.Fatalf("unexpected %v ", results.Slice()) + } + results = results.Flip(0, 1) + if !reflect.DeepEqual(results.Slice(), []uint64{1, 2, 4, 8, 9, 10}) { + t.Fatalf("unexpected %v ", results.Slice()) + } + results = results.Flip(4, 8) + if !reflect.DeepEqual(results.Slice(), []uint64{1, 2, 5, 6, 7, 9, 10}) { + t.Fatalf("unexpected %v ", results.Slice()) + } + +} + // Ensure bitmap can return the number of intersecting bits in two bitmaps. func TestBitmap_IntersectionCount_ArrayArray(t *testing.T) { bm0 := roaring.NewBitmap(0, 1000001, 1000002, 1000003)