Add unionRunRunInPlace

This commit is contained in:
Kuba Podgórski 2020-06-06 01:02:23 +02:00
parent 89ac3f4737
commit 2b0cd2f234
2 changed files with 402 additions and 2 deletions

View file

@ -3134,8 +3134,7 @@ func (c *Container) unionInPlace(other *Container) *Container {
c = c.runToBitmap()
return unionBitmapArrayInPlace(c, other)
case containerRun:
c = c.runToBitmap()
return unionBitmapRunInPlace(c, other)
return unionRunRunInPlace(c, other)
}
}
if roaringParanoia {
@ -4715,6 +4714,145 @@ func unionBitmapBitmapInPlace(a, b *Container) *Container {
return a
}
// unions run b into run a, mutating a in place.
func unionRunRunInPlace(a, b *Container) *Container {
statsHit("unionInPlace/RunRun")
a = a.Thaw()
runs, n := unionInterval16InPlace(a.runs(), b.runs())
a.setN(n)
a.setRuns(runs)
return a
}
func unionInterval16InPlace(a, b []interval16) ([]interval16, int32) {
n := int32(0)
an, bn := len(a), len(b)
i, j, k := 0, 0, 0
for {
// select the next interval - v
var (
v *interval16
local *interval16
visa bool // v is from a
)
if i < an && j < bn {
if a[i].start <= b[j].start {
v, visa = &a[i], true
i++
} else if j < bn {
v = &b[j]
j++
}
} else {
if i < an {
v, visa = &a[i], true
i++
} else if j < bn {
v = &b[j]
j++
}
}
// no more intervals - break
if v == nil {
if len(a) > 0 {
// count the last interval
n += int32(a[k].last-a[k].start) + 1
}
break
}
next:
if k == an {
// append what's left
a = append(a, *v)
an++
continue
}
// current, locally unioned interval
if local == nil {
local = &a[k]
}
if v.last < local.start {
// [---- v ----][---- local ----]
// insert v into a
a = append(a, interval16{})
copy(a[i+1:], a[i:])
a[i] = *v
an++
i++
continue
}
if v.start > local.last {
// [---- local ----][---- v ----]
// set already unioned interval and go next
// if int32(v.start-local.last) <= 1 {
// local.last = v.last
// continue
// }
a[k] = *local
n += int32(local.last-local.start) + 1
k++
// if we set unioned interval we can go to the next interval
if visa {
// v is from a, so we can skip "digested intervals"
// and jump already to v
local = v
} else {
// ...otherwise, move to the next one,
// so let assign local in next iteration
local = nil
}
goto next
}
if v.start < local.start {
if v.last >= local.start && v.last <= local.last {
// [---- local ----]
// [---- v ----]
local.start = v.start
} else if v.last > local.last {
// [- local -]
// [---- v ----]
*local = *v
}
continue
}
if v.start >= local.start && v.start <= local.last {
if v.last <= local.last {
// [---- local ----]
// [-- v --]
// this assignment looks silly, but if we extended local
// and "digested" many nested intervals, we can squash a, e.g.:
// [a1] [a2] [a3] [a4]
// [------ b ------]
// will give us:
// [ ----- a1 -----] [a4]
a[k] = *local
} else if v.last > local.last {
// [---- local ----]
// [---- v ----]
local.last = v.last
}
}
}
if len(a) > 0 {
a = a[:k+1]
}
return a, n
}
func difference(a, b *Container) *Container {
if a.N() == 0 || b.N() == maxContainerVal+1 {
return nil

View file

@ -721,6 +721,268 @@ func TestUnionMixed(t *testing.T) {
})
}
func TestUnionInterval16InPlace(t *testing.T) {
t.Run("single overlap", func(t *testing.T) {
a := []interval16{interval16{1, 10}, interval16{21, 28}}
b := []interval16{interval16{8, 12}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 20
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 12}, interval16{21, 28}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("nested intervals", func(t *testing.T) {
a := []interval16{interval16{3, 13}, interval16{17, 20}}
b := []interval16{interval16{1, 4}, interval16{6, 7}, interval16{8, 9}, interval16{10, 11}, interval16{14, 17}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 20
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 13}, interval16{14, 20}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("no overlap", func(t *testing.T) {
a := []interval16{interval16{3, 4}, interval16{7, 8}}
b := []interval16{interval16{1, 2}, interval16{5, 6}, interval16{9, 10}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 10
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 6}, interval16{7, 8}, interval16{9, 10}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("b in a", func(t *testing.T) {
a := []interval16{interval16{1, 10}}
b := []interval16{interval16{5, 7}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 10
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 10}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("a eq b", func(t *testing.T) {
a := []interval16{interval16{1, 10}}
b := []interval16{interval16{1, 10}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 10
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 10}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("a in b", func(t *testing.T) {
a := []interval16{interval16{5, 7}}
b := []interval16{interval16{1, 10}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 10
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 10}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("a ahead b", func(t *testing.T) {
a := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
b := []interval16{interval16{10, 11}, interval16{12, 13}, interval16{14, 15}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 13
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{
interval16{1, 2},
interval16{3, 4},
interval16{5, 7},
interval16{10, 11},
interval16{12, 13},
interval16{14, 15},
}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("b ahead a", func(t *testing.T) {
a := []interval16{interval16{10, 11}, interval16{12, 13}, interval16{14, 15}}
b := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 13
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{
interval16{1, 2},
interval16{3, 4},
interval16{5, 7},
interval16{10, 11},
interval16{12, 13},
interval16{14, 15},
}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("empty a and b", func(t *testing.T) {
a := []interval16{}
b := []interval16{}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 0
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("empty a", func(t *testing.T) {
a := []interval16{}
b := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 7
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("empty b", func(t *testing.T) {
a := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
b := []interval16{}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 7
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("single a", func(t *testing.T) {
a := []interval16{interval16{1, 2}}
b := []interval16{}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 2
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 2}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("single b", func(t *testing.T) {
a := []interval16{}
b := []interval16{interval16{1, 2}}
runs, n := unionInterval16InPlace(a, b)
const expectedN = 2
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
expected := []interval16{interval16{1, 2}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
})
t.Run("oddBitsSet lastBitUnset", func(t *testing.T) {
a := []interval16{interval16{1, 1}, interval16{3, 3}, interval16{5, 5}}
b := []interval16{interval16{0, 4}}
runs, n := unionInterval16InPlace(a, b)
expected := []interval16{interval16{0, 4}, interval16{5, 5}}
for i, v := range expected {
if runs[i] != v {
t.Fatalf("runs expected: %+v, got: %+v", expected, runs)
}
}
const expectedN = 6
if n != expectedN {
t.Fatalf("N expected: %d, got: %d", expectedN, n)
}
})
}
func TestIntersectMixed(t *testing.T) {
a := NewContainerRun([]interval16{{start: 5, last: 10}})
b := NewContainerArray([]uint16{1, 4, 5, 7, 10, 11, 12})