Return a list of matching block Match triples describing matching subsequences of `a` in `b` starting from the `a_start` position in `a` up to the `a_end` position in `a`. `b2j` is a mapping of b "high" token ids -> list of positions in b, e.g. a posting list. `len_good` i
(a, b, a_start, a_end, b2j, len_good, matchables=frozenset(), *args, **kwargs)
| 105 | |
| 106 | |
| 107 | def match_blocks(a, b, a_start, a_end, b2j, len_good, matchables=frozenset(), *args, **kwargs): |
| 108 | """ |
| 109 | Return a list of matching block Match triples describing matching |
| 110 | subsequences of `a` in `b` starting from the `a_start` position in `a` up to |
| 111 | the `a_end` position in `a`. |
| 112 | |
| 113 | `b2j` is a mapping of b "high" token ids -> list of positions in b, e.g. a |
| 114 | posting list. |
| 115 | |
| 116 | `len_good` is such that token ids smaller than `len_good` are treated as |
| 117 | important, non-junk tokens. |
| 118 | |
| 119 | `matchables` is a set of matchable positions. Positions absent from this set |
| 120 | are ignored. |
| 121 | |
| 122 | Each triple is of the form (i, j, n), and means that a[i:i+n] == b[j:j+n]. |
| 123 | The triples are monotonically increasing in i and in j. It is also |
| 124 | guaranteed that adjacent triples never describe adjacent equal blocks. |
| 125 | Instead adjacent blocks are merged and collapsed in a single block. |
| 126 | """ |
| 127 | |
| 128 | # This non-recursive algorithm is using a list as a queue of blocks. We |
| 129 | # still need to look at and append partial results to matching_blocks in a |
| 130 | # loop. The matches are sorted at the end. |
| 131 | |
| 132 | queue = [(a_start, a_end, 0, len(b))] |
| 133 | queue_append = queue.append |
| 134 | queue_pop = queue.pop |
| 135 | matching_blocks = [] |
| 136 | matching_blocks_append = matching_blocks.append |
| 137 | while queue: |
| 138 | alo, ahi, blo, bhi = queue_pop() |
| 139 | i, j, k = x = find_longest_match( |
| 140 | a, b, alo, ahi, blo, bhi, b2j, len_good, matchables) |
| 141 | # a[alo:i] vs b[blo:j] unknown |
| 142 | # a[i:i+k] same as b[j:j+k] |
| 143 | # a[i+k:ahi] vs b[j+k:bhi] unknown |
| 144 | if k: # if k is 0, there was no matching block as the size is 0 |
| 145 | matching_blocks_append(x) |
| 146 | if alo < i and blo < j: |
| 147 | # there is unprocessed things remaining to the left |
| 148 | queue_append((alo, i, blo, j)) |
| 149 | if i + k < ahi and j + k < bhi: |
| 150 | # there is unprocessed things remaining to the right |
| 151 | queue_append((i + k, ahi, j + k, bhi)) |
| 152 | |
| 153 | matching_blocks.sort() |
| 154 | |
| 155 | # collapse adjacent blocks |
| 156 | i1 = j1 = k1 = 0 |
| 157 | non_adjacent = [] |
| 158 | non_adjacent_append = non_adjacent.append |
| 159 | for i2, j2, k2 in matching_blocks: |
| 160 | # Is this block adjacent to i1, j1, k1? |
| 161 | if i1 + k1 == i2 and j1 + k1 == j2: |
| 162 | # Yes, so collapse them -- this just increases the length of the |
| 163 | # first block by the length of the second, and the first block so |
| 164 | # lengthened remains the block to compare against. |
no test coverage detected