added xor support to roaring

This commit is contained in:
Todd Gruben 2017-05-22 16:51:53 -05:00
parent a7fcace1e9
commit 2182534cc7
2 changed files with 161 additions and 0 deletions

View file

@ -1563,6 +1563,127 @@ func differenceBitmapBitmap(a, b *container) *container {
}
return output
}
func (b *Bitmap) Xor(other *Bitmap) *Bitmap {
output := &Bitmap{}
ki, ci := b.keys, b.containers
kj, cj := other.keys, other.containers
for {
var key uint64
var container *container
ni, nj := len(ki), len(kj)
if ni == 0 && nj == 0 { // eof(i,j)
break
} else if ni == 0 || (nj != 0 && ki[0] > kj[0]) { // eof(i) or i > j
key, container = kj[0], cj[0].clone()
kj, cj = kj[1:], cj[1:]
} else if nj == 0 || (ki[0] < kj[0]) { // eof(j) or i < j
key, container = ki[0], ci[0].clone()
ki, ci = ki[1:], ci[1:]
} else { // i == j
key, container = ki[0], xor(ci[0], cj[0])
ki, ci = ki[1:], ci[1:]
kj, cj = kj[1:], cj[1:]
}
output.keys = append(output.keys, key)
output.containers = append(output.containers, container)
}
return output
}
func xor(a, b *container) *container {
if a.isArray() {
if b.isArray() {
return xorArrayArray(a, b)
} else {
return xorArrayBitmap(a, b)
}
} else {
if b.isArray() {
return xorArrayBitmap(b, a)
} else {
return xorBitmapBitmap(a, b)
}
}
}
func xorArrayArray(a, b *container) *container {
output := &container{}
na, nb := len(a.array), len(b.array)
for i, j := 0, 0; ; {
if i >= na && j >= nb {
break
} else if i < na && j >= nb {
output.add(a.array[i])
i++
continue
} else if i >= na && j < nb {
output.add(b.array[j])
j++
continue
}
va, vb := a.array[i], b.array[j]
if va < vb {
output.add(va)
i++
} else if va > vb {
output.add(vb)
j++
} else { //==
i++
j++
}
}
return output
}
func xorArrayBitmap(a, b *container) *container {
output := &container{}
itr := newBufIterator(newBitmapIterator(b.bitmap))
for i := 0; ; {
vb, eof := itr.next()
if i >= len(a.array) && eof {
break
} else if i >= len(a.array) {
output.add(vb)
continue
} else if eof {
output.add(a.array[i])
i++
continue
}
va := a.array[i]
if va < vb {
output.add(va)
i++
itr.unread()
} else if va > vb {
output.add(vb)
} else {
i++
}
}
return output
}
func xorBitmapBitmap(a, b *container) *container {
output := &container{
bitmap: make([]uint64, bitmapN),
}
for i := 0; i < bitmapN; i++ {
v := a.bitmap[i] ^ b.bitmap[i]
output.bitmap[i] = v
output.n += int(popcnt(v))
}
return output
}
// opType represents a type of operation.
type opType uint8

View file

@ -130,6 +130,46 @@ func TestBitmap_Union(t *testing.T) {
}
}
func TestBitmap_Xor_ArrayArray(t *testing.T) {
bm0 := roaring.NewBitmap(0, 1000001, 1000002, 1000003)
bm1 := roaring.NewBitmap(0, 50000, 1000001, 1000002)
result := bm0.Xor(bm1)
if n := result.Count(); n != 2 {
t.Fatalf("unexpected n: %d", n)
}
}
func TestBitmap_Xor_ArrayBitmap(t *testing.T) {
bm0 := roaring.NewBitmap(1, 70, 200, 4097, 4098)
bm1 := roaring.NewBitmap()
for i := uint64(0); i < 10000; i += 2 {
bm1.Add(i)
}
result := bm0.Xor(bm1)
if n := result.Count(); n != 4999 {
t.Fatalf("unexpected n: %d", n)
}
}
func TestBitmap_Xor_BitmapBitmap(t *testing.T) {
bm0 := roaring.NewBitmap()
bm1 := roaring.NewBitmap()
for i := uint64(0); i < 10000; i += 2 {
bm1.Add(i)
}
for i := uint64(1); i < 10000; i += 2 {
bm0.Add(i)
}
result := bm0.Xor(bm1)
if n := result.Count(); n != 10000 {
t.Fatalf("unexpected n: %d", n)
}
}
// 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)