diff --git a/roaring/roaring.go b/roaring/roaring.go index 5cf82a348..702885834 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2252,6 +2252,32 @@ func (c *container) bitmapSetRange(i, j uint64) { } } +// xor's all bits in [i, j] with all true (inclusive) (c must be a bitmap container). +func (c *container) bitmapXorRange(i, j uint64) { + j += 1 + x := i / 64 + y := (j - 1) / 64 + var X uint64 = maxBitmap << (i % 64) + var Y uint64 = maxBitmap >> (64 - (j % 64)) + if x == y { + cnt := popcnt(c.bitmap[x]) + c.bitmap[x] ^= (X & Y) //// flip + c.n += int(popcnt(c.bitmap[x]) - cnt) + } else { + cnt := popcnt(c.bitmap[x]) + c.bitmap[x] ^= X + c.n += int(popcnt(c.bitmap[x]) - cnt) + for i := x + 1; i < y; i++ { + cnt = popcnt(c.bitmap[i]) + c.bitmap[i] ^= maxBitmap + c.n += int(popcnt(c.bitmap[i]) - cnt) + } + cnt = popcnt(c.bitmap[y]) + c.bitmap[y] ^= Y + c.n += int(popcnt(c.bitmap[y]) - cnt) + } +} + // zeroes all bits in [i, j] (inclusive) (c must be a bitmap container) func (c *container) bitmapZeroRange(i, j uint64) { j += 1 @@ -3152,3 +3178,18 @@ func xorRunRun(a, b *container) *container { } return output } + +// xorRunRun computes the exclusive or of a bitmap and a run container. +func xorBitmapRun(a, b *container) *container { + output := a.clone() + for j := 0; j < len(b.runs); j++ { + output.bitmapXorRange(uint64(b.runs[j].start), uint64(b.runs[j].last)) + } + + if output.n < ArrayMaxSize && len(output.runs) > output.n/2 { + output.runToArray() + } else if len(output.runs) > RunMaxSize { + output.runToBitmap() + } + return output +} diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 526d4f194..1745e0c97 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1363,3 +1363,96 @@ func TestXorRunRun(t *testing.T) { } } } + +func TestBitmapFlipRange(t *testing.T) { + c := &container{bitmap: make([]uint64, bitmapN)} + tests := []struct { + bitmap []uint64 + start uint64 + last uint64 + exp []uint64 + expN int + }{ + { + bitmap: []uint64{0x0000000000000000}, + start: 0, + last: 2, + exp: []uint64{0x0000000000000007}, + expN: 3, + }, + { + bitmap: []uint64{0xF1}, + start: 4, + last: 8, + exp: []uint64{0x101}, + expN: 2, + }, + { + bitmap: []uint64{0xAA}, + start: 0, + last: 7, + exp: []uint64{0x55}, + expN: 4, + }, + { + bitmap: []uint64{0x0, 0x0000000000000000, 0x0000000000000000}, + start: 63, + last: 128, + exp: []uint64{0x8000000000000000, 0xFFFFFFFFFFFFFFFF, 0x000000000000001}, + expN: 66, + }, + { + bitmap: []uint64{0x0, 0x00000000000000FF, 0x0000000000000000}, + start: 63, + last: 128, + exp: []uint64{0x8000000000000000, 0xFFFFFFFFFFFFFF00, 0x000000000000001}, + expN: 58, + }, + { + bitmap: []uint64{0x0, 0x0, 0x0}, + start: 129, + last: 131, + exp: []uint64{0x0000000000000000, 0x0000000000000000, 0x00000000000000E}, + expN: 3, + }, + } + + for i, test := range tests { + for i, v := range test.bitmap { + c.bitmap[i] = v + } + c.n = c.countRange(0, 65535) + c.bitmapXorRange(test.start, test.last) + if !reflect.DeepEqual(c.bitmap[:len(test.exp)], test.exp) { + t.Fatalf("test %#v expected %x, got %x", i, test.exp, c.bitmap[:len(test.bitmap)]) + } + if test.expN != c.n { + t.Fatalf("test #%v expected n to be %v, but got %v", i, test.expN, c.n) + } + } +} + +func TestXorBitmapRun(t *testing.T) { + a := &container{} + b := &container{} + tests := []struct { + bitmap []uint64 + runs []interval32 + exp []uint64 + }{ + {bitmap: []uint64{0x0, 0x0, 0x0}, + runs: []interval32{{start: 129, last: 131}}, + exp: []uint64{0x0, 0x0, 0x00000000000000E}, + }, + } + for i, test := range tests { + a.bitmap = test.bitmap + b.runs = test.runs + ret := xorBitmapRun(a, b) + + if !reflect.DeepEqual(ret.bitmap, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.bitmap) + } + } + +}