From 3e8030aee427e3de837431a54156919865bf7047 Mon Sep 17 00:00:00 2001 From: Matt Jaffee Date: Fri, 19 May 2017 08:22:56 -0500 Subject: [PATCH] implement intersectRunRun --- roaring/roaring.go | 35 +++++++++++++++++++++++++++++++- roaring/roaring_internal_test.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 1d8ef1223..516a76f5e 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1645,6 +1645,9 @@ func intersectArrayArray(a, b *container) *container { return output } +// intersectArrayRun computes the intersect of an array container and a run +// container. The return is always an array container (since it's guaranteed to +// be low-cardinality) func intersectArrayRun(a, b *container) *container { output := &container{} na, nb := len(a.array), len(b.runs) @@ -1663,9 +1666,39 @@ func intersectArrayRun(a, b *container) *container { return output } +// intersectRunRun computes the intersect of two run containers. The output is +// always a run container, since it can't possible have more runs than either of +// the inputs. (note: it is possible that an array container would be better in +// some cases, but probably not worth complicating the implementation) func intersectRunRun(a, b *container) *container { output := &container{} - // TODO + 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 vb.last < va.start { + // |--vb--| |--va--| + j++ + } else if va.last > vb.last && va.start >= vb.start { + // |--vb-|-|-va--| + output.runs = append(output.runs, interval32{start: va.start, last: vb.last}) + j++ + } else if va.last > vb.last && va.start < vb.start { + // |--va|--vb--|--| + output.runs = append(output.runs, vb) + j++ + } else if va.last <= vb.last && va.start >= vb.start { + // |--vb|--va--|--| + output.runs = append(output.runs, va) + i++ + } else if va.last <= vb.last && va.start < vb.start { + // |--va-|-|-vb--| + output.runs = append(output.runs, interval32{start: vb.start, last: va.last}) + i++ + } + } return output } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 8f771a1fe..c250d57d7 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -390,3 +390,38 @@ func TestIntersectArrayRun(t *testing.T) { } } } + +func TestIntersectRunRun(t *testing.T) { + a := &container{} + b := &container{} + tests := []struct { + aruns []interval32 + bruns []interval32 + exp []interval32 + }{ + { + aruns: []interval32{}, + bruns: []interval32{{start: 5, last: 10}}, + exp: []interval32(nil), + }, + { + aruns: []interval32{{start: 5, last: 12}}, + bruns: []interval32{{start: 5, last: 10}}, + exp: []interval32{{start: 5, last: 10}}, + }, + { + aruns: []interval32{{start: 1, last: 3}, {start: 5, last: 5}, {start: 7, last: 8}, {start: 9, last: 12}}, + bruns: []interval32{{start: 5, last: 10}}, + exp: []interval32{{start: 5, last: 5}, {start: 7, last: 8}, {start: 9, last: 10}}, + }, + } + for i, test := range tests { + a.runs = test.aruns + b.runs = test.bruns + ret := intersectRunRun(a, b) + if !reflect.DeepEqual(ret.runs, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs) + } + } + +}