UNPKG

fast-array-diff

Version:

Implementation of paper 'An O(ND) Difference Algorithm and Its Variations' on array

237 lines (236 loc) 8.97 kB
var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; function lcs(a, b, compareFunc) { var M = a.length, N = b.length; var MAX = M + N; var v = { 1: 0 }; for (var d = 0; d <= MAX; ++d) { for (var k = -d; k <= d; k += 2) { var x = void 0; if (k === -d || (k !== d && v[k - 1] + 1 < v[k + 1])) { x = v[k + 1]; } else { x = v[k - 1] + 1; } var y = x - k; while (x < M && y < N && compareFunc(a[x], b[y])) { x++; y++; } if (x === M && y === N) { return d; } v[k] = x; } } /* istanbul ignore next */ return -1; // never reach } var Direct; (function (Direct) { Direct[Direct["none"] = 0] = "none"; Direct[Direct["horizontal"] = 1] = "horizontal"; Direct[Direct["vertical"] = 2] = "vertical"; Direct[Direct["diagonal"] = 4] = "diagonal"; Direct[Direct["all"] = 7] = "all"; })(Direct || (Direct = {})); function getSolution(a, aStart, aEnd, b, bStart, bEnd, d, startDirect, endDirect, compareFunc, elementsChanged) { var _a, _b, _c, _d; if (d === 0) { elementsChanged('same', a, aStart, aEnd, b, bStart, bEnd); return; } else if (d === aEnd - aStart + (bEnd - bStart)) { var removeFirst = (startDirect & Direct.horizontal ? 1 : 0) + (endDirect & Direct.vertical ? 1 : 0); var addFirst = (startDirect & Direct.vertical ? 1 : 0) + (endDirect & Direct.horizontal ? 1 : 0); if (removeFirst >= addFirst) { aStart !== aEnd && elementsChanged('remove', a, aStart, aEnd, b, bStart, bStart); bStart !== bEnd && elementsChanged('add', a, aEnd, aEnd, b, bStart, bEnd); } else { bStart !== bEnd && elementsChanged('add', a, aStart, aStart, b, bStart, bEnd); aStart !== aEnd && elementsChanged('remove', a, aStart, aEnd, b, bEnd, bEnd); } return; } var M = aEnd - aStart; var N = bEnd - bStart; var HALF = Math.floor(N / 2); var now = {}; for (var k = -d - 1; k <= d + 1; ++k) { now[k] = { d: Infinity, segments: 0, direct: Direct.none }; } var preview = (_a = {}, _a[-d - 1] = { d: Infinity, segments: 0, direct: Direct.none }, _a[d + 1] = { d: Infinity, segments: 0, direct: Direct.none }, _a); for (var y = 0; y <= HALF; ++y) { _b = __read([preview, now], 2), now = _b[0], preview = _b[1]; var _loop_1 = function (k) { var x = y + k; if (y === 0 && x === 0) { now[k] = { d: 0, segments: 0, direct: startDirect, }; return "continue"; } var currentPoints = [ { direct: Direct.horizontal, d: now[k - 1].d + 1, segments: now[k - 1].segments + (now[k - 1].direct & Direct.horizontal ? 0 : 1), }, { direct: Direct.vertical, d: preview[k + 1].d + 1, segments: preview[k + 1].segments + (preview[k + 1].direct & Direct.vertical ? 0 : 1), }, ]; if (x > 0 && x <= M && y > 0 && y <= N && compareFunc(a[aStart + x - 1], b[bStart + y - 1])) { currentPoints.push({ direct: Direct.diagonal, d: preview[k].d, segments: preview[k].segments + (preview[k].direct & Direct.diagonal ? 0 : 1), }); } var bestValue = currentPoints.reduce(function (best, info) { if (best.d > info.d) { return info; } else if (best.d === info.d && best.segments > info.segments) { return info; } return best; }); currentPoints.forEach(function (info) { if (bestValue.d === info.d && bestValue.segments === info.segments) { bestValue.direct |= info.direct; } }); now[k] = bestValue; }; for (var k = -d; k <= d; ++k) { _loop_1(k); } } var now2 = {}; for (var k = -d - 1; k <= d + 1; ++k) { now2[k] = { d: Infinity, segments: 0, direct: Direct.none }; } var preview2 = (_c = {}, _c[-d - 1] = { d: Infinity, segments: 0, direct: Direct.none }, _c[d + 1] = { d: Infinity, segments: 0, direct: Direct.none }, _c); for (var y = N; y >= HALF; --y) { _d = __read([preview2, now2], 2), now2 = _d[0], preview2 = _d[1]; var _loop_2 = function (k) { var x = y + k; if (y === N && x === M) { now2[k] = { d: 0, segments: 0, direct: endDirect, }; return "continue"; } var currentPoints = [ { direct: Direct.horizontal, d: now2[k + 1].d + 1, segments: now2[k + 1].segments + (now2[k + 1].direct & Direct.horizontal ? 0 : 1), }, { direct: Direct.vertical, d: preview2[k - 1].d + 1, segments: preview2[k - 1].segments + (preview2[k - 1].direct & Direct.vertical ? 0 : 1), }, ]; if (x >= 0 && x < M && y >= 0 && y < N && compareFunc(a[aStart + x], b[bStart + y])) { currentPoints.push({ direct: Direct.diagonal, d: preview2[k].d, segments: preview2[k].segments + (preview2[k].direct & Direct.diagonal ? 0 : 1), }); } var bestValue = currentPoints.reduce(function (best, info) { if (best.d > info.d) { return info; } else if (best.d === info.d && best.segments > info.segments) { return info; } return best; }); currentPoints.forEach(function (info) { if (bestValue.d === info.d && bestValue.segments === info.segments) { bestValue.direct |= info.direct; } }); now2[k] = bestValue; }; for (var k = d; k >= -d; --k) { _loop_2(k); } } var best = { k: -1, d: Infinity, segments: 0, direct: Direct.none, }; for (var k = -d; k <= d; ++k) { var dSum = now[k].d + now2[k].d; if (dSum < best.d) { best.k = k; best.d = dSum; best.segments = now[k].segments + now2[k].segments + (now[k].segments & now2[k].segments ? 0 : 1); best.direct = now2[k].direct; } else if (dSum === best.d) { var segments = now[k].segments + now2[k].segments + (now[k].segments & now2[k].segments ? 0 : 1); if (segments < best.segments) { best.k = k; best.d = dSum; best.segments = segments; best.direct = now2[k].direct; } else if (segments === best.segments && !(best.direct & Direct.diagonal) && now2[k].direct & Direct.diagonal) { best.k = k; best.d = dSum; best.segments = segments; best.direct = now2[k].direct; } } } if (HALF + best.k === 0 && HALF === 0) { HALF++; now[best.k].direct = now2[best.k].direct; now2[best.k].direct = preview2[best.k].direct; } getSolution(a, aStart, aStart + HALF + best.k, b, bStart, bStart + HALF, now[best.k].d, startDirect, now2[best.k].direct, compareFunc, elementsChanged); getSolution(a, aStart + HALF + best.k, aEnd, b, bStart + HALF, bEnd, now2[best.k].d, now[best.k].direct, endDirect, compareFunc, elementsChanged); } export default function bestSubSequence(a, b, compareFunc, elementsChanged) { var d = lcs(a, b, compareFunc); getSolution(a, 0, a.length, b, 0, b.length, d, Direct.diagonal, Direct.all, compareFunc, elementsChanged); }