implement intersectionCounts for run containers

This commit is contained in:
Matt Jaffee 2017-05-18 15:40:03 -05:00
parent 014005b5d5
commit 20cec1363b
2 changed files with 146 additions and 2 deletions

View file

@ -1446,12 +1446,24 @@ func intersectionCount(a, b *container) uint64 {
if a.isArray() {
if b.isArray() {
return intersectionCountArrayArray(a, b)
} else if b.isRun() {
return intersectionCountArrayRun(a, b)
} else {
return intersectionCountArrayBitmap(a, b)
}
} else if a.isRun() {
if b.isArray() {
return intersectionCountArrayRun(b, a)
} else if b.isRun() {
return intersectionCountRunRun(a, b)
} else {
return intersectionCountBitmapRun(b, a)
}
} else {
if b.isArray() {
return intersectionCountArrayBitmap(b, a)
} else if b.isRun() {
return intersectionCountBitmapRun(a, b)
} else {
return intersectionCountBitmapBitmap(a, b)
}
@ -1474,6 +1486,61 @@ func intersectionCountArrayArray(a, b *container) (n uint64) {
return n
}
func intersectionCountArrayRun(a, b *container) (n uint64) {
na, nb := len(a.array), len(b.runs)
for i, j := 0, 0; i < na && j < nb; {
va, vb := a.array[i], b.runs[j]
if va < vb.start {
i++
} else if va >= vb.start && va <= vb.last {
i++
n++
} else if va > vb.last {
j++
}
}
return n
}
func intersectionCountRunRun(a, b *container) uint64 {
var n uint32
na, nb := len(a.runs), len(b.runs)
for i, j := 0, 0; i < na && j < nb; {
va, vb := a.runs[i], b.runs[j]
if va.last < vb.start {
// |--va--| |--vb--|
i++
} else if va.start > vb.last {
// |--vb--| |--va--|
j++
} else if va.last > vb.last && va.start >= vb.start {
// |--vb-|-|-va--|
n += 1 + vb.last - va.start
j++
} else if va.last > vb.last && va.start < vb.start {
// |--va|--vb--|--|
n += 1 + vb.last - vb.start
j++
} else if va.last <= vb.last && va.start >= vb.start {
// |--vb|--va--|--|
n += 1 + va.last - va.start
i++
} else if va.last <= vb.last && va.start < vb.start {
// |--va-|-|-vb--|
n += 1 + va.last - vb.start
i++
}
}
return uint64(n)
}
func intersectionCountBitmapRun(a, b *container) (n uint64) {
for _, iv := range b.runs {
n += uint64(a.bitmapCountRange(iv.start, iv.last+1))
}
return n
}
func intersectionCountArrayBitmapOld(a, b *container) (n uint64) {
// Copy array header so we can shrink it.
array := a.array
@ -1878,7 +1945,7 @@ func xorArrayBitmap(a, b *container) *container {
}
if output.count() < ArrayMaxSize {
output.convertToArray()
output.bitmapToArray()
}
return output
@ -1896,7 +1963,7 @@ func xorBitmapBitmap(a, b *container) *container {
}
if output.count() < ArrayMaxSize {
output.convertToArray()
output.bitmapToArray()
}
return output
}

View file

@ -273,3 +273,80 @@ func TestRunMax(t *testing.T) {
t.Fatalf("max for %v should be 0", c.runs)
}
}
func TestIntersectionCountArrayRun(t *testing.T) {
a := &container{array: []uint32{1, 5, 10, 11, 12}}
b := &container{runs: []interval32{{start: 2, last: 10}, {start: 12, last: 13}, {start: 15, last: 16}}}
ret := intersectionCountArrayRun(a, b)
if ret != 3 {
t.Fatalf("count of %v with %v should be 3, but got %v", a.array, b.runs, ret)
}
}
func TestIntersectionCountBitmapRun(t *testing.T) {
a := &container{bitmap: []uint64{1}}
b := &container{runs: []interval32{{start: 63, last: 64}}}
ret := intersectionCountBitmapRun(a, b)
if ret != 1 {
t.Fatalf("count of %v with %v should be 1, but got %v", a.bitmap, b.runs, ret)
}
a = &container{bitmap: []uint64{0xF0000001, 0xFF00000000000000, 0xFF000000000000F0, 0x0F0000}}
b = &container{runs: []interval32{{start: 33, last: 35}, {start: 62, last: 69}, {start: 130, last: 150}, {start: 186, last: 300}}}
ret = intersectionCountBitmapRun(a, b)
if ret != 22 {
t.Fatalf("count of %v with %v should be 22, but got %v", a.bitmap, b.runs, ret)
}
}
func TestIntersectionCountRunRun(t *testing.T) {
a := &container{}
b := &container{}
tests := []struct {
aruns []interval32
bruns []interval32
exp uint64
}{
{
aruns: []interval32{},
bruns: []interval32{{start: 3, last: 8}}, exp: 0},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 3, last: 8}}, exp: 6},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 1, last: 11}}, exp: 9},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 0, last: 2}}, exp: 1},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 1, last: 10}}, exp: 9},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 5, last: 12}}, exp: 6},
{
aruns: []interval32{{start: 2, last: 10}},
bruns: []interval32{{start: 10, last: 99}}, exp: 1},
{
aruns: []interval32{{start: 2, last: 10}, {start: 44, last: 99}},
bruns: []interval32{{start: 12, last: 14}}, exp: 0},
{
aruns: []interval32{{start: 2, last: 10}, {start: 12, last: 13}},
bruns: []interval32{{start: 2, last: 10}, {start: 12, last: 13}}, exp: 11},
{
aruns: []interval32{{start: 8, last: 12}, {start: 15, last: 19}},
bruns: []interval32{{start: 9, last: 9}, {start: 11, last: 17}}, exp: 6},
}
for i, test := range tests {
a.runs = test.aruns
b.runs = test.bruns
ret := intersectionCountRunRun(a, b)
if ret != test.exp {
t.Fatalf("test #%v failed intersecting %v with %v should be %v, but got %v", i, test.aruns, test.bruns, test.exp, ret)
}
}
}