From da6d7e516c30c5cee9b894c7a73eaf693ddfcbb7 Mon Sep 17 00:00:00 2001 From: make-github-pseudonymous-again <5165674+make-github-pseudonymous-again@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:30:09 +0200 Subject: [PATCH] :bicyclist: perf: Scan middle snake only once. Fixes #19. --- src/backwardExtend.js | 74 +++++++++++++++++++++++++++++++++++++++++++ src/backwardStep.js | 27 +++------------- src/forwardStep.js | 9 +++--- src/index.js | 1 + src/oneWay.js | 8 +++-- src/twoWayScan.js | 12 ++++++- 6 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 src/backwardExtend.js diff --git a/src/backwardExtend.js b/src/backwardExtend.js new file mode 100644 index 0000000..7d4cc7b --- /dev/null +++ b/src/backwardExtend.js @@ -0,0 +1,74 @@ +import assert from 'assert'; + +import bound from './bound.js'; +import longestCommonSuffix from './longestCommonSuffix.js'; +/** + * Diagonal backward extension without yield. + * + * @param {number} center + * @param {number} D + * @param {Int32Array} V + * @param {Function} eq + * @param {number} li + * @param {number} lj + * @param {number} ri + * @param {number} rj + * @param {number} Delta + */ +export default function backwardExtend( + center, + D, + V, + eq, + li, + lj, + ri, + rj, + Delta, +) { + assert(ri > rj && li > lj); + // NOTE: We make the bounding box as small as possible. + // This should save roughly half of the computation time compared to + // letting LB = -D and UB = D. + const LB = -bound(D, li - lj); + const UB = bound(D, ri - rj); + assert(LB <= UB); + assert(LB !== D); + assert(UB !== -D); + + console.debug('beg backwardExtend', { + center, + D, + V, + eq, + li, + lj, + ri, + rj, + LB, + UB, + Delta, + }); + + for (let k = LB; k <= UB; k += 2) { + const x = V[center + k]; + const y = x - (k + Delta); + console.debug({k, x, lj, y, rj}); + + if (x >= lj && y >= rj && x <= li && y <= ri) { + V[center + k] = longestCommonSuffix(eq, x, lj, y, rj); + } + } + + console.debug('end backwardExtend', { + center, + D, + V, + eq, + li, + lj, + ri, + rj, + Delta, + }); +} diff --git a/src/backwardStep.js b/src/backwardStep.js index 7ffb404..ba79c94 100644 --- a/src/backwardStep.js +++ b/src/backwardStep.js @@ -1,9 +1,8 @@ import assert from 'assert'; import bound from './bound.js'; -import longestCommonSuffix from './longestCommonSuffix.js'; /** - * Diagonal backward step without yielding. + * Diagonal backward step without yield. * * @param {number} center * @param {number} D @@ -46,25 +45,13 @@ export default function backwardStep(center, D, V, eq, li, lj, ri, rj, Delta) { const k = LB; const xp = V[cx + k]; const yp = V[cy + k]; - const x = k !== -D && xp > yp ? yp : xp - 1; - const y = x - (k + Delta); - console.debug({k, x, lj, y, rj}); - - if (x >= lj && y >= rj && x <= li && y <= ri) { - V[center + k] = longestCommonSuffix(eq, x, lj, y, rj); - } + V[center + k] = k !== -D && xp > yp ? yp : xp - 1; for (let k = LB + 2; k < UB; k += 2) { assert(k !== -D && k !== D); const xp = V[cx + k]; const yp = V[cy + k]; - const x = xp > yp ? yp : xp - 1; - const y = x - (k + Delta); - console.debug({k, x, lj, y, rj}); - - if (x >= lj && y >= rj && x <= li && y <= ri) { - V[center + k] = longestCommonSuffix(eq, x, lj, y, rj); - } + V[center + k] = xp > yp ? yp : xp - 1; } if (UB !== LB) { @@ -72,13 +59,7 @@ export default function backwardStep(center, D, V, eq, li, lj, ri, rj, Delta) { const xp = V[cx + k]; const yp = V[cy + k]; assert(k === D || xp > yp); - const x = yp; - const y = x - (k + Delta); - console.debug({k, x, lj, y, rj}); - - if (x >= lj && y >= rj && x <= li && y <= ri) { - V[center + k] = longestCommonSuffix(eq, x, lj, y, rj); - } + V[center + k] = yp; } console.debug('end backwardStep', { diff --git a/src/forwardStep.js b/src/forwardStep.js index 859e86e..8396392 100644 --- a/src/forwardStep.js +++ b/src/forwardStep.js @@ -1,9 +1,8 @@ import assert from 'assert'; import bound from './bound.js'; -import longestCommonPrefix from './longestCommonPrefix.js'; /** - * Diagonal forward step. + * Diagonal forward step with yield. * * @param {number} center * @param {number} D @@ -53,7 +52,7 @@ export default function* forwardStep(center, D, V, eq, li, lj, ri, rj, Delta) { console.debug({k, x, lj, y, rj}); if (x <= lj && y <= rj && x >= li && y >= ri) { - V[center + k] = longestCommonPrefix(eq, x, lj, y, rj); + V[center + k] = x; yield k; } @@ -66,7 +65,7 @@ export default function* forwardStep(center, D, V, eq, li, lj, ri, rj, Delta) { console.debug({k, x, lj, y, rj}); if (x <= lj && y <= rj && x >= li && y >= ri) { - V[center + k] = longestCommonPrefix(eq, x, lj, y, rj); + V[center + k] = x; yield k; } } @@ -80,7 +79,7 @@ export default function* forwardStep(center, D, V, eq, li, lj, ri, rj, Delta) { console.debug({k, x, lj, y, rj}); if (x <= lj && y <= rj && x >= li && y >= ri) { - V[center + k] = longestCommonPrefix(eq, x, lj, y, rj); + V[center + k] = x; yield k; } } diff --git a/src/index.js b/src/index.js index bd51327..8fd2b82 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +export {default as backwardExtend} from './backwardExtend.js'; export {default as backwardStep} from './backwardStep.js'; export {default as bound} from './bound.js'; export {default as boundAlloc} from './boundAlloc.js'; diff --git a/src/oneWay.js b/src/oneWay.js index 077c978..52750f0 100644 --- a/src/oneWay.js +++ b/src/oneWay.js @@ -2,6 +2,7 @@ import assert from 'assert'; import oneWayAlloc from './oneWayAlloc.js'; import forwardStep from './forwardStep.js'; +import longestCommonPrefix from './longestCommonPrefix.js'; /** * Scan from begin to end. @@ -27,12 +28,15 @@ const oneWay = (MAX, eq, li, lj, ri, rj) => { const Delta0 = li - ri; for (let D = 1; D <= MAX; ++D) { for (const k of forwardStep(center, D, V, eq, li, lj, ri, rj, Delta0)) { - const x = V[center + k]; - const y = x - (k + Delta0); + let x = V[center + k]; + let y = x - (k + Delta0); + x = longestCommonPrefix(eq, x, lj, y, rj); + y = x - (k + Delta0); if (x === lj && y === rj) return { distance: D, }; + V[center + k] = x; } } diff --git a/src/twoWayScan.js b/src/twoWayScan.js index f6ef5c1..ab08603 100644 --- a/src/twoWayScan.js +++ b/src/twoWayScan.js @@ -2,6 +2,9 @@ import assert from 'assert'; import forwardStep from './forwardStep.js'; import backwardStep from './backwardStep.js'; +import backwardExtend from './backwardExtend.js'; +import longestCommonPrefix from './longestCommonPrefix.js'; +import longestCommonSuffix from './longestCommonSuffix.js'; /** * Scan from begin to middle and end to middle. @@ -42,12 +45,15 @@ const twoWayScan = (MAX, V, centerF, centerB, eq, li, lj, ri, rj) => { for (let D = 1; D <= HALF_MAX; ++D) { if (2 * D > MAX + parityDelta) break; for (const k of forwardStep(centerF, D, V, eq, li, lj, ri, rj, Delta0)) { + const x = V[centerF + k]; + const y = x - (k + Delta0); + V[centerF + k] = longestCommonPrefix(eq, x, lj, y, rj); if (k - Delta < -(D - parityDelta)) continue; if (k - Delta > D - parityDelta) continue; if (V[centerF + k] < V[centerB + k - Delta]) continue; // TODO this scans the snake twice return { k: k + Delta0, - xBegin: V[centerB + k - Delta], + xBegin: longestCommonSuffix(eq, x, li, y, ri), xEnd: V[centerF + k], distance: 2 * D - parityDelta, distanceLeft: D, @@ -57,6 +63,10 @@ const twoWayScan = (MAX, V, centerF, centerB, eq, li, lj, ri, rj) => { if (D === HALF_MAX) break; + if (D > parityDelta) { + backwardExtend(centerB, D - parityDelta, V, eq, lj, li, rj, ri, Delta1); + } + backwardStep(centerB, D + 1 - parityDelta, V, eq, lj, li, rj, ri, Delta1); }