diff --git a/roaring/roaring.go b/roaring/roaring.go index 4cc1f9751..a8de21d6c 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -4726,116 +4726,186 @@ func unionRunRunInPlace(a, b *Container) *Container { return a } +// unionInterval16InPlace merges two slice of intervals in place (in a). +// The main concept is to go value by value (instead of interval by interval) +// and count `.start` and `.last` points. +// If we get the `state == 0` it means we just built a new interval (`val`), +// and we can set it in `a` at the possition `off` func unionInterval16InPlace(a, b []interval16) ([]interval16, int32) { n := int32(0) - an, bn := len(a), len(b) - i, j, k := 0, 0, 0 + + var ( + // ai - index of a, aii - subindex (0: a[ai].start, 1: a[ai].last). + ai, aii int = 0, 0 + + // bi - index of b, bii - subindex (0: b[bi].start, 1: b[bi].last). + bi, bii int = 0, 0 + + // Offset of a - next available index to set. + off int = 0 + // Value to set/append to a at off + val interval16 + + // Current state - state equals 0 means we are clear (out of intervals) + // When we start a new interval we add +1 when we get out of interval we add -1. + state int + + // subindex (ii) to state mapping + // .start: [0] -> 1 + // .last: [1] -> -1 + iiMap = [2]int{1, -1} + + // If fromB is equal 2 it means that both val.start and val.last come from b, + // so we need to extend a, first + fromB int8 + + // eval functions evaluates global state and value + eval = func(arr [2]uint16, ii int, onlyB bool) { + if state == 0 && ii == 0 { + // we are clear and start a new interval + val.start = arr[ii] + if onlyB { + fromB++ + } + } + + state += iiMap[ii] + + if state == 0 { + // we just got out of interval + // ii == 1 + val.last = arr[ii] + if onlyB { + fromB++ + } + } + } + // eval2 function is a special variant for eval function + // it's only used when two interval endings are equal, e.g.: + // a: ------------------| + // b: -----------| + // the most important part is to change the global for both endings + // before we check if we're getting out of interval and start the new one. + eval2 = func(arr [2]uint16, i1, i2 int) { + if state == 0 && (i1 == 0 || i2 == 0) { + // we are clear and start a new interval + val.start = arr[i1] + + } + + state += iiMap[i1] + state += iiMap[i2] + + if state == 0 { + // (i1 == 1 || i2 == 1) + // we just got out of interval + val.last = arr[i1] + } + } + ) + for { - var ( - v interval16 - local *interval16 - ) + // av, bv reflects a[ai] and b[bi] intervals as an array, + // so we can internally iterate over values (points). + var av, bv [2]uint16 - if i < an && j < bn { - if a[i].start <= b[j].start { - v = a[i] - i++ - } else { - v = b[j] - j++ + if ai < an && bi < bn { + av[0], av[1] = a[ai].start, a[ai].last + bv[0], bv[1] = b[bi].start, b[bi].last + + if av[aii] < bv[bii] { + // a: |------------------- + // b: |------------------- + + eval(av, aii, false) + aii++ + } else if av[aii] == bv[bii] { + // a: |------------------- + // b: |------------------- + // or + // a: ------------------| + // b: |------------------- + // or + // a: ------------------| + // b: |------------| + // ... + + eval2(av, aii, bii) + aii++ + bii++ + } else { // bv[bii] < av[aii] + // a: |------------------- + // b: |------------------- + + eval(bv, bii, true) + bii++ } + } else if ai < an { // only a left + av[0], av[1] = a[ai].start, a[ai].last + eval(av, aii, false) + aii++ + } else if bi < bn { // only b left + bv[0], bv[1] = b[bi].start, b[bi].last + eval(bv, bii, false) + bii++ } else { - if i < an { - v = a[i] - i++ - } else if j < bn { - v = b[j] - j++ - } else { - break + break + } + + if state == 0 { + if fromB == 2 { + // val.start and val.last come from b, so we need to extend a, first + a = append(a, interval16{}) + copy(a[off+1:], a[off:]) + ai++ + an++ } + fromB = 0 + a, off = appendInterval16At(a, val, off) + n += int32(val.last-val.start) + 1 } - reeval: - if k == an { - // append what's left and try to optimize it later - a = append(a, v) - an++ - continue + if aii == 2 { + // move to the next a's interval + aii = 0 + ai++ } - // locally unioned interval - if local == nil { - local = &a[k] - } - - if v.last < local.start { - // [---- v ----][---- local ----] - // prepend 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 ----] - - // check if we can optimize run and combine neighbours - if int32(v.start-local.last) <= 1 { - local.last = v.last - continue - } - - // set already unioned interval and reevaluate v - a[k] = *local - k++ - n += int32(local.last-local.start) + 1 - local = &v - goto reeval - } - - 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 - } - } else if v.start <= local.last { - if v.last <= local.last { - // [---- local ----] - // [-- v --] - - // this assignment overwrites the first available position to set - a[k] - // it's done to "swallow" many nested intervals, 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 bii == 2 { + // move to the next b's interval + bii = 0 + bi++ } } if len(a) > 0 { - // count the latest interval - n += int32(a[k].last-a[k].start) + 1 - a = a[:k+1] + a = a[:off] } return a, n } +// appendInterval16At appends or sets val in a at off position +// The function returns modified a ([]interval16) and new offset (off) +func appendInterval16At(a []interval16, val interval16, off int) ([]interval16, int) { + + if off > 0 && int32(val.start)-int32(a[off-1].last) <= 1 { + a[off-1].last = val.last + return a, off + } + + if off == len(a) { + a = append(a, val) + off++ + return a, off + } + + a[off] = val + off++ + + return a, off +} + func difference(a, b *Container) *Container { if a.N() == 0 || b.N() == maxContainerVal+1 { return nil diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 1f5fe572b..4e0375f76 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -722,467 +722,185 @@ 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}} - bb := make([]interval16, len(b)) - copy(bb, b) + tests := []struct { + name string + a []interval16 + b []interval16 + expected []interval16 + expectedN int32 + }{ + { + name: "firstBitUnset lastBitSet", + a: []interval16{interval16{1, 10}}, + b: []interval16{interval16{10, 10}}, + expected: []interval16{interval16{1, 10}}, + expectedN: 10, + }, + { + name: "single overlap", + a: []interval16{interval16{1, 10}, interval16{21, 28}}, + b: []interval16{interval16{8, 12}}, + expected: []interval16{interval16{1, 12}, interval16{21, 28}}, + expectedN: 20, + }, + { + name: "nested intervals", + 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}}, + expected: []interval16{interval16{1, 20}}, + expectedN: 20, + }, + { + name: "no overlap", + a: []interval16{interval16{3, 4}, interval16{7, 8}}, + b: []interval16{interval16{1, 2}, interval16{5, 6}, interval16{9, 10}}, + expected: []interval16{interval16{1, 10}}, + expectedN: 10, + }, + { + name: "b in a", + a: []interval16{interval16{1, 10}}, + b: []interval16{interval16{5, 7}}, + expected: []interval16{interval16{1, 10}}, + expectedN: 10, + }, + { + name: "a eq b", + a: []interval16{interval16{1, 10}}, + b: []interval16{interval16{1, 10}}, + expected: []interval16{interval16{1, 10}}, + expectedN: 10, + }, + { + name: "a in b", + a: []interval16{interval16{5, 7}}, + b: []interval16{interval16{1, 10}}, + expected: []interval16{interval16{1, 10}}, + expectedN: 10, + }, + { + name: "a ahead b", + a: []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}, + b: []interval16{interval16{10, 11}, interval16{12, 13}, interval16{14, 15}}, + expected: []interval16{interval16{1, 7}, interval16{10, 15}}, + expectedN: 13, + }, + { + name: "b ahead a", + a: []interval16{interval16{10, 11}, interval16{12, 13}, interval16{14, 15}}, + b: []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}, + expected: []interval16{interval16{1, 7}, interval16{10, 15}}, + expectedN: 13, + }, + { + name: "empty a and b", + a: []interval16{}, + b: []interval16{}, + expected: []interval16{}, + expectedN: 0, + }, + { + name: "empty a", + a: []interval16{}, + b: []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}, + expected: []interval16{interval16{1, 7}}, + expectedN: 7, + }, + { + name: "empty b", + a: []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}}, + b: []interval16{}, + expected: []interval16{interval16{1, 7}}, + expectedN: 7, + }, + { + name: "single a", + a: []interval16{interval16{1, 2}}, + b: []interval16{}, + expected: []interval16{interval16{1, 2}}, + expectedN: 2, + }, + { + name: "single b", + a: []interval16{}, + b: []interval16{interval16{1, 2}}, + expected: []interval16{interval16{1, 2}}, + expectedN: 2, + }, + { + name: "single a single b", + a: []interval16{interval16{3, 4}}, + b: []interval16{interval16{1, 2}}, + expected: []interval16{interval16{1, 4}}, + expectedN: 4, + }, + { + name: "oddBitsSet lastBitUnset", + a: []interval16{interval16{1, 1}, interval16{3, 3}, interval16{5, 5}}, + b: []interval16{interval16{0, 4}}, + expected: []interval16{interval16{0, 5}}, + expectedN: 6, + }, + { + name: "all bits", + a: []interval16{interval16{1, 1}, interval16{3, 3}, interval16{5, 5}}, + b: []interval16{interval16{0, 0}, interval16{2, 2}, interval16{4, 4}}, + expected: []interval16{interval16{0, 5}}, + expectedN: 6, + }, + { + name: "short a long b", + a: []interval16{interval16{5, 5}, interval16{7, 7}, interval16{9, 10}, interval16{12, 12}, interval16{15, 17}, interval16{19, 20}}, + b: []interval16{interval16{1, 10}, interval16{12, 12}, interval16{14, 18}}, + expected: []interval16{interval16{1, 10}, interval16{12, 12}, interval16{14, 20}}, + expectedN: 18, + }, + { + name: "common endings", + a: []interval16{interval16{1, 5}, interval16{15, 20}, interval16{25, 35}}, + b: []interval16{interval16{1, 10}, interval16{15, 20}, interval16{30, 35}}, + expected: []interval16{interval16{1, 10}, interval16{15, 20}, interval16{25, 35}}, + expectedN: 27, + }, + { + name: "common endings and overlap", + a: []interval16{interval16{1, 5}, interval16{10, 15}}, + b: []interval16{interval16{5, 10}, interval16{12, 17}}, + expected: []interval16{interval16{1, 17}}, + expectedN: 17, + }, + { + name: "no common endings and overlap", + a: []interval16{interval16{5, 10}, interval16{12, 17}}, + b: []interval16{interval16{0, 11}, interval16{15, 20}}, + expected: []interval16{interval16{0, 20}}, + expectedN: 21, + }, + } - runs, n := unionInterval16InPlace(a, b) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + bb := make([]interval16, len(tc.b)) + copy(bb, tc.b) - 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) + runs, n := unionInterval16InPlace(tc.a, tc.b) + + for i, v := range tc.expected { + if runs[i] != v { + t.Fatalf("runs expected: %+v, got: %+v", tc.expected, runs) + } } - } - - const expectedN = 20 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) + if n != tc.expectedN { + t.Fatalf("N expected: %d, got: %d", tc.expectedN, n) } - } - }) - 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}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 20}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) + for i, v := range bb { + if tc.b[i] != v { + t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, tc.b) + } } - } - - const expectedN = 20 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - 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}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 10}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 10 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("b in a", func(t *testing.T) { - a := []interval16{interval16{1, 10}} - b := []interval16{interval16{5, 7}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 10}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 10 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("a eq b", func(t *testing.T) { - a := []interval16{interval16{1, 10}} - b := []interval16{interval16{1, 10}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 10}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 10 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("a in b", func(t *testing.T) { - a := []interval16{interval16{5, 7}} - b := []interval16{interval16{1, 10}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 10}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 10 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - 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}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{ - interval16{1, 7}, - interval16{10, 15}, - } - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 13 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - 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}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{ - interval16{1, 7}, - interval16{10, 15}, - } - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 13 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("empty a and b", func(t *testing.T) { - a := []interval16{} - b := []interval16{} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 0 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("empty a", func(t *testing.T) { - a := []interval16{} - b := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 7}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 7 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("empty b", func(t *testing.T) { - a := []interval16{interval16{1, 2}, interval16{3, 4}, interval16{5, 7}} - b := []interval16{} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 7}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 7 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("single a", func(t *testing.T) { - a := []interval16{interval16{1, 2}} - b := []interval16{} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 2}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 2 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("single b", func(t *testing.T) { - a := []interval16{} - b := []interval16{interval16{1, 2}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 2}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 2 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("single a single b", func(t *testing.T) { - a := []interval16{interval16{3, 4}} - b := []interval16{interval16{1, 2}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 4}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - - const expectedN = 4 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("oddBitsSet lastBitUnset", func(t *testing.T) { - a := []interval16{interval16{1, 1}, interval16{3, 3}, interval16{5, 5}} - b := []interval16{interval16{0, 4}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{0, 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) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("all bits", func(t *testing.T) { - a := []interval16{interval16{1, 1}, interval16{3, 3}, interval16{5, 5}} - b := []interval16{interval16{0, 0}, interval16{2, 2}, interval16{4, 4}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{0, 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) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) - - t.Run("short a long b", func(t *testing.T) { - a := []interval16{interval16{5, 5}, interval16{7, 7}, interval16{9, 10}, interval16{12, 12}, interval16{15, 17}, interval16{19, 20}} - b := []interval16{interval16{1, 10}, interval16{12, 12}, interval16{14, 18}} - bb := make([]interval16, len(b)) - copy(bb, b) - - runs, n := unionInterval16InPlace(a, b) - - expected := []interval16{interval16{1, 10}, interval16{12, 12}, interval16{14, 20}} - for i, v := range expected { - if runs[i] != v { - t.Fatalf("runs expected: %+v, got: %+v", expected, runs) - } - } - const expectedN = 18 - if n != expectedN { - t.Fatalf("N expected: %d, got: %d", expectedN, n) - } - - for i, v := range bb { - if b[i] != v { - t.Fatalf("b changed - runs expected: %+v, got: %+v", bb, b) - } - } - }) + }) + } } func TestIntersectMixed(t *testing.T) {