| // Myers' Diff Algorithm | 
| // Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js | 
| type EqualFunc<T> = (a: T, b: T) => boolean; | 
|   | 
| type DiffComponent = { | 
|     count: number | 
|     added: boolean | 
|     removed: boolean, | 
|     indices: number[] | 
| } | 
|   | 
| type DiffPath = { | 
|     components: DiffComponent[], | 
|     newPos: number | 
| } | 
|   | 
| // Using O(ND) algorithm | 
| // TODO: Optimize when diff is large. | 
| function diff<T>(oldArr: T[], newArr: T[], equals: EqualFunc<T>): DiffComponent[] { | 
|     if (!equals) { | 
|         equals = function (a, b) { | 
|             return a === b; | 
|         }; | 
|     } | 
|   | 
|     oldArr = oldArr.slice(); | 
|     newArr = newArr.slice(); | 
|     // Allow subclasses to massage the input prior to running | 
|     var newLen = newArr.length; | 
|     var oldLen = oldArr.length; | 
|     var editLength = 1; | 
|     var maxEditLength = newLen + oldLen; | 
|     var bestPath: DiffPath[] = [{ newPos: -1, components: [] }]; | 
|   | 
|     // Seed editLength = 0, i.e. the content starts with the same values | 
|     var oldPos = extractCommon<T>(bestPath[0], newArr, oldArr, 0, equals); | 
|     if (!oldLen // All new created | 
|         || !newLen // Clear | 
|         || (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen)) { | 
|         var indices = []; | 
|         var allCleared = !newLen && oldLen > 0; | 
|         var allCreated = !oldLen && newLen > 0; | 
|         for (let i = 0; i < (allCleared ? oldArr : newArr).length; i++) { | 
|             indices.push(i); | 
|         } | 
|         // Identity per the equality and tokenizer | 
|         return [{ | 
|             indices: indices, | 
|             count: indices.length, | 
|             added: allCreated, | 
|             removed: allCleared | 
|         }]; | 
|     } | 
|   | 
|     // Main worker method. checks all permutations of a given edit length for acceptance. | 
|     function execEditLength() { | 
|         for (let diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { | 
|             var basePath; | 
|             var addPath = bestPath[diagonalPath - 1]; | 
|             var removePath = bestPath[diagonalPath + 1]; | 
|             var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; | 
|             if (addPath) { | 
|                 // No one else is going to attempt to use this value, clear it | 
|                 bestPath[diagonalPath - 1] = undefined; | 
|             } | 
|   | 
|             var canAdd = addPath && addPath.newPos + 1 < newLen; | 
|             var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; | 
|             if (!canAdd && !canRemove) { | 
|                 // If this path is a terminal then prune | 
|                 bestPath[diagonalPath] = undefined; | 
|                 continue; | 
|             } | 
|   | 
|             // Select the diagonal that we want to branch from. We select the prior | 
|             // path whose position in the new string is the farthest from the origin | 
|             // and does not pass the bounds of the diff graph | 
|             if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { | 
|                 basePath = clonePath(removePath); | 
|                 pushComponent(basePath.components, false, true); | 
|             } | 
|             else { | 
|                 basePath = addPath;   // No need to clone, we've pulled it from the list | 
|                 basePath.newPos++; | 
|                 pushComponent(basePath.components, true, false); | 
|             } | 
|   | 
|             oldPos = extractCommon<T>(basePath, newArr, oldArr, diagonalPath, equals); | 
|   | 
|             // If we have hit the end of both strings, then we are done | 
|             if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { | 
|                 return buildValues(basePath.components); | 
|             } | 
|             else { | 
|                 // Otherwise track this path as a potential candidate and continue. | 
|                 bestPath[diagonalPath] = basePath; | 
|             } | 
|         } | 
|   | 
|         editLength++; | 
|     } | 
|   | 
|     while (editLength <= maxEditLength) { | 
|         var ret = execEditLength(); | 
|         if (ret) { | 
|             return ret; | 
|         } | 
|     } | 
| } | 
|   | 
| function extractCommon<T>(basePath: DiffPath, newArr: T[], oldArr: T[], diagonalPath: number, equals: EqualFunc<T>) { | 
|     var newLen = newArr.length; | 
|     var oldLen = oldArr.length; | 
|     var newPos = basePath.newPos; | 
|     var oldPos = newPos - diagonalPath; | 
|     var commonCount = 0; | 
|   | 
|     while (newPos + 1 < newLen && oldPos + 1 < oldLen && equals(newArr[newPos + 1], oldArr[oldPos + 1])) { | 
|         newPos++; | 
|         oldPos++; | 
|         commonCount++; | 
|     } | 
|   | 
|     if (commonCount) { | 
|         basePath.components.push({ | 
|             count: commonCount, | 
|             added: false, | 
|             removed: false, | 
|             indices: [] | 
|         }); | 
|     } | 
|   | 
|     basePath.newPos = newPos; | 
|     return oldPos; | 
| } | 
|   | 
| function pushComponent(components: DiffComponent[], added: boolean, removed: boolean) { | 
|     var last = components[components.length - 1]; | 
|     if (last && last.added === added && last.removed === removed) { | 
|         // We need to clone here as the component clone operation is just | 
|         // as shallow array clone | 
|         components[components.length - 1] = { | 
|             count: last.count + 1, | 
|             added, | 
|             removed, | 
|             indices: [] | 
|         }; | 
|     } | 
|     else { | 
|         components.push({ | 
|             count: 1, | 
|             added, | 
|             removed, | 
|             indices: [] | 
|         }); | 
|     } | 
| } | 
|   | 
| function buildValues(components: DiffComponent[]) { | 
|     var componentPos = 0; | 
|     var componentLen = components.length; | 
|     var newPos = 0; | 
|     var oldPos = 0; | 
|   | 
|     for (; componentPos < componentLen; componentPos++) { | 
|         var component = components[componentPos]; | 
|         if (!component.removed) { | 
|             var indices = []; | 
|             for (let i = newPos; i < newPos + component.count; i++) { | 
|                 indices.push(i); | 
|             } | 
|             component.indices = indices; | 
|             newPos += component.count; | 
|             // Common case | 
|             if (!component.added) { | 
|                 oldPos += component.count; | 
|             } | 
|         } | 
|         else { | 
|             for (let i = oldPos; i < oldPos + component.count; i++) { | 
|                 component.indices.push(i); | 
|             } | 
|             oldPos += component.count; | 
|         } | 
|     } | 
|   | 
|     return components; | 
| } | 
|   | 
| function clonePath(path: DiffPath) { | 
|     return { newPos: path.newPos, components: path.components.slice(0) }; | 
| } | 
|   | 
| export default function arrayDiff<T>(oldArr: T[], newArr: T[], equal?: EqualFunc<T>): DiffComponent[] { | 
|     return diff(oldArr, newArr, equal); | 
| } |