UNPKG

@xterm/addon-search

Version:

An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables searching the buffer. This addon requires xterm.js v4+.

4 lines 314 kB
{ "version": 3, "sources": ["../../../src/vs/base/common/errors.ts", "../../../src/vs/base/common/arraysFind.ts", "../../../src/vs/base/common/arrays.ts", "../../../src/vs/base/common/collections.ts", "../../../src/vs/base/common/map.ts", "../../../src/vs/base/common/functional.ts", "../../../src/vs/base/common/iterator.ts", "../../../src/vs/base/common/lifecycle.ts", "../../../src/vs/base/common/linkedList.ts", "../../../src/vs/base/common/stopwatch.ts", "../../../src/vs/base/common/event.ts", "../../../src/vs/base/common/cancellation.ts", "../../../src/vs/base/common/platform.ts", "../../../src/vs/base/common/symbols.ts", "../../../src/vs/base/common/async.ts", "../src/SearchLineCache.ts", "../src/SearchState.ts", "../src/SearchEngine.ts", "../src/DecorationManager.ts", "../src/SearchResultTracker.ts", "../src/SearchAddon.ts"], "sourcesContent": ["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nexport interface ErrorListenerCallback {\n\t(error: any): void;\n}\n\nexport interface ErrorListenerUnbind {\n\t(): void;\n}\n\n// Avoid circular dependency on EventEmitter by implementing a subset of the interface.\nexport class ErrorHandler {\n\tprivate unexpectedErrorHandler: (e: any) => void;\n\tprivate listeners: ErrorListenerCallback[];\n\n\tconstructor() {\n\n\t\tthis.listeners = [];\n\n\t\tthis.unexpectedErrorHandler = function (e: any) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (e.stack) {\n\t\t\t\t\tif (ErrorNoTelemetry.isErrorNoTelemetry(e)) {\n\t\t\t\t\t\tthrow new ErrorNoTelemetry(e.message + '\\n\\n' + e.stack);\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new Error(e.message + '\\n\\n' + e.stack);\n\t\t\t\t}\n\n\t\t\t\tthrow e;\n\t\t\t}, 0);\n\t\t};\n\t}\n\n\taddListener(listener: ErrorListenerCallback): ErrorListenerUnbind {\n\t\tthis.listeners.push(listener);\n\n\t\treturn () => {\n\t\t\tthis._removeListener(listener);\n\t\t};\n\t}\n\n\tprivate emit(e: any): void {\n\t\tthis.listeners.forEach((listener) => {\n\t\t\tlistener(e);\n\t\t});\n\t}\n\n\tprivate _removeListener(listener: ErrorListenerCallback): void {\n\t\tthis.listeners.splice(this.listeners.indexOf(listener), 1);\n\t}\n\n\tsetUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {\n\t\tthis.unexpectedErrorHandler = newUnexpectedErrorHandler;\n\t}\n\n\tgetUnexpectedErrorHandler(): (e: any) => void {\n\t\treturn this.unexpectedErrorHandler;\n\t}\n\n\tonUnexpectedError(e: any): void {\n\t\tthis.unexpectedErrorHandler(e);\n\t\tthis.emit(e);\n\t}\n\n\t// For external errors, we don't want the listeners to be called\n\tonUnexpectedExternalError(e: any): void {\n\t\tthis.unexpectedErrorHandler(e);\n\t}\n}\n\nexport const errorHandler = new ErrorHandler();\n\n/** @skipMangle */\nexport function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {\n\terrorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);\n}\n\n/**\n * Returns if the error is a SIGPIPE error. SIGPIPE errors should generally be\n * logged at most once, to avoid a loop.\n *\n * @see https://github.com/microsoft/vscode-remote-release/issues/6481\n */\nexport function isSigPipeError(e: unknown): e is Error {\n\tif (!e || typeof e !== 'object') {\n\t\treturn false;\n\t}\n\n\tconst cast = e as Record<string, string | undefined>;\n\treturn cast.code === 'EPIPE' && cast.syscall?.toUpperCase() === 'WRITE';\n}\n\nexport function onUnexpectedError(e: any): undefined {\n\t// ignore errors from cancelled promises\n\tif (!isCancellationError(e)) {\n\t\terrorHandler.onUnexpectedError(e);\n\t}\n\treturn undefined;\n}\n\nexport function onUnexpectedExternalError(e: any): undefined {\n\t// ignore errors from cancelled promises\n\tif (!isCancellationError(e)) {\n\t\terrorHandler.onUnexpectedExternalError(e);\n\t}\n\treturn undefined;\n}\n\nexport interface SerializedError {\n\treadonly $isError: true;\n\treadonly name: string;\n\treadonly message: string;\n\treadonly stack: string;\n\treadonly noTelemetry: boolean;\n}\n\nexport function transformErrorForSerialization(error: Error): SerializedError;\nexport function transformErrorForSerialization(error: any): any;\nexport function transformErrorForSerialization(error: any): any {\n\tif (error instanceof Error) {\n\t\tconst { name, message } = error;\n\t\tconst stack: string = (<any>error).stacktrace || (<any>error).stack;\n\t\treturn {\n\t\t\t$isError: true,\n\t\t\tname,\n\t\t\tmessage,\n\t\t\tstack,\n\t\t\tnoTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error)\n\t\t};\n\t}\n\n\t// return as is\n\treturn error;\n}\n\nexport function transformErrorFromSerialization(data: SerializedError): Error {\n\tlet error: Error;\n\tif (data.noTelemetry) {\n\t\terror = new ErrorNoTelemetry();\n\t} else {\n\t\terror = new Error();\n\t\terror.name = data.name;\n\t}\n\terror.message = data.message;\n\terror.stack = data.stack;\n\treturn error;\n}\n\n// see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces\nexport interface V8CallSite {\n\tgetThis(): unknown;\n\tgetTypeName(): string | null;\n\tgetFunction(): Function | undefined;\n\tgetFunctionName(): string | null;\n\tgetMethodName(): string | null;\n\tgetFileName(): string | null;\n\tgetLineNumber(): number | null;\n\tgetColumnNumber(): number | null;\n\tgetEvalOrigin(): string | undefined;\n\tisToplevel(): boolean;\n\tisEval(): boolean;\n\tisNative(): boolean;\n\tisConstructor(): boolean;\n\ttoString(): string;\n}\n\nconst canceledName = 'Canceled';\n\n/**\n * Checks if the given error is a promise in canceled state\n */\nexport function isCancellationError(error: any): boolean {\n\tif (error instanceof CancellationError) {\n\t\treturn true;\n\t}\n\treturn error instanceof Error && error.name === canceledName && error.message === canceledName;\n}\n\n// !!!IMPORTANT!!!\n// Do NOT change this class because it is also used as an API-type.\nexport class CancellationError extends Error {\n\tconstructor() {\n\t\tsuper(canceledName);\n\t\tthis.name = this.message;\n\t}\n}\n\n/**\n * @deprecated use {@link CancellationError `new CancellationError()`} instead\n */\nexport function canceled(): Error {\n\tconst error = new Error(canceledName);\n\terror.name = error.message;\n\treturn error;\n}\n\nexport function illegalArgument(name?: string): Error {\n\tif (name) {\n\t\treturn new Error(`Illegal argument: ${name}`);\n\t} else {\n\t\treturn new Error('Illegal argument');\n\t}\n}\n\nexport function illegalState(name?: string): Error {\n\tif (name) {\n\t\treturn new Error(`Illegal state: ${name}`);\n\t} else {\n\t\treturn new Error('Illegal state');\n\t}\n}\n\nexport class ReadonlyError extends TypeError {\n\tconstructor(name?: string) {\n\t\tsuper(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');\n\t}\n}\n\nexport function getErrorMessage(err: any): string {\n\tif (!err) {\n\t\treturn 'Error';\n\t}\n\n\tif (err.message) {\n\t\treturn err.message;\n\t}\n\n\tif (err.stack) {\n\t\treturn err.stack.split('\\n')[0];\n\t}\n\n\treturn String(err);\n}\n\nexport class NotImplementedError extends Error {\n\tconstructor(message?: string) {\n\t\tsuper('NotImplemented');\n\t\tif (message) {\n\t\t\tthis.message = message;\n\t\t}\n\t}\n}\n\nexport class NotSupportedError extends Error {\n\tconstructor(message?: string) {\n\t\tsuper('NotSupported');\n\t\tif (message) {\n\t\t\tthis.message = message;\n\t\t}\n\t}\n}\n\nexport class ExpectedError extends Error {\n\treadonly isExpected = true;\n}\n\n/**\n * Error that when thrown won't be logged in telemetry as an unhandled error.\n */\nexport class ErrorNoTelemetry extends Error {\n\toverride readonly name: string;\n\n\tconstructor(msg?: string) {\n\t\tsuper(msg);\n\t\tthis.name = 'CodeExpectedError';\n\t}\n\n\tpublic static fromError(err: Error): ErrorNoTelemetry {\n\t\tif (err instanceof ErrorNoTelemetry) {\n\t\t\treturn err;\n\t\t}\n\n\t\tconst result = new ErrorNoTelemetry();\n\t\tresult.message = err.message;\n\t\tresult.stack = err.stack;\n\t\treturn result;\n\t}\n\n\tpublic static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry {\n\t\treturn err.name === 'CodeExpectedError';\n\t}\n}\n\n/**\n * This error indicates a bug.\n * Do not throw this for invalid user input.\n * Only catch this error to recover gracefully from bugs.\n */\nexport class BugIndicatingError extends Error {\n\tconstructor(message?: string) {\n\t\tsuper(message || 'An unexpected bug occurred.');\n\t\tObject.setPrototypeOf(this, BugIndicatingError.prototype);\n\n\t\t// Because we know for sure only buggy code throws this,\n\t\t// we definitely want to break here and fix the bug.\n\t\t// eslint-disable-next-line no-debugger\n\t\t// debugger;\n\t}\n}\n", "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { Comparator } from './arrays';\n\nexport function findLast<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {\n\tconst idx = findLastIdx(array, predicate);\n\tif (idx === -1) {\n\t\treturn undefined;\n\t}\n\treturn array[idx];\n}\n\nexport function findLastIdx<T>(array: readonly T[], predicate: (item: T) => boolean, fromIndex = array.length - 1): number {\n\tfor (let i = fromIndex; i >= 0; i--) {\n\t\tconst element = array[i];\n\n\t\tif (predicate(element)) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n/**\n * Finds the last item where predicate is true using binary search.\n * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!\n *\n * @returns `undefined` if no item matches, otherwise the last item that matches the predicate.\n */\nexport function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {\n\tconst idx = findLastIdxMonotonous(array, predicate);\n\treturn idx === -1 ? undefined : array[idx];\n}\n\n/**\n * Finds the last item where predicate is true using binary search.\n * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!\n *\n * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.\n */\nexport function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {\n\tlet i = startIdx;\n\tlet j = endIdxEx;\n\twhile (i < j) {\n\t\tconst k = Math.floor((i + j) / 2);\n\t\tif (predicate(array[k])) {\n\t\t\ti = k + 1;\n\t\t} else {\n\t\t\tj = k;\n\t\t}\n\t}\n\treturn i - 1;\n}\n\n/**\n * Finds the first item where predicate is true using binary search.\n * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!\n *\n * @returns `undefined` if no item matches, otherwise the first item that matches the predicate.\n */\nexport function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {\n\tconst idx = findFirstIdxMonotonousOrArrLen(array, predicate);\n\treturn idx === array.length ? undefined : array[idx];\n}\n\n/**\n * Finds the first item where predicate is true using binary search.\n * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!\n *\n * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.\n */\nexport function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {\n\tlet i = startIdx;\n\tlet j = endIdxEx;\n\twhile (i < j) {\n\t\tconst k = Math.floor((i + j) / 2);\n\t\tif (predicate(array[k])) {\n\t\t\tj = k;\n\t\t} else {\n\t\t\ti = k + 1;\n\t\t}\n\t}\n\treturn i;\n}\n\nexport function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {\n\tconst idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);\n\treturn idx === array.length ? -1 : idx;\n}\n\n/**\n * Use this when\n * * You have a sorted array\n * * You query this array with a monotonous predicate to find the last item that has a certain property.\n * * You query this array multiple times with monotonous predicates that get weaker and weaker.\n */\nexport class MonotonousArray<T> {\n\tpublic static assertInvariants = false;\n\n\tprivate _findLastMonotonousLastIdx = 0;\n\tprivate _prevFindLastPredicate: ((item: T) => boolean) | undefined;\n\n\tconstructor(private readonly _array: readonly T[]) {\n\t}\n\n\t/**\n\t * The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!\n\t * For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.\n\t */\n\tfindLastMonotonous(predicate: (item: T) => boolean): T | undefined {\n\t\tif (MonotonousArray.assertInvariants) {\n\t\t\tif (this._prevFindLastPredicate) {\n\t\t\t\tfor (const item of this._array) {\n\t\t\t\t\tif (this._prevFindLastPredicate(item) && !predicate(item)) {\n\t\t\t\t\t\tthrow new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._prevFindLastPredicate = predicate;\n\t\t}\n\n\t\tconst idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx);\n\t\tthis._findLastMonotonousLastIdx = idx + 1;\n\t\treturn idx === -1 ? undefined : this._array[idx];\n\t}\n}\n\n/**\n * Returns the first item that is equal to or greater than every other item.\n*/\nexport function findFirstMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {\n\tif (array.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tlet max = array[0];\n\tfor (let i = 1; i < array.length; i++) {\n\t\tconst item = array[i];\n\t\tif (comparator(item, max) > 0) {\n\t\t\tmax = item;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Returns the last item that is equal to or greater than every other item.\n*/\nexport function findLastMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {\n\tif (array.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tlet max = array[0];\n\tfor (let i = 1; i < array.length; i++) {\n\t\tconst item = array[i];\n\t\tif (comparator(item, max) >= 0) {\n\t\t\tmax = item;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Returns the first item that is equal to or less than every other item.\n*/\nexport function findFirstMin<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {\n\treturn findFirstMax(array, (a, b) => -comparator(a, b));\n}\n\nexport function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): number {\n\tif (array.length === 0) {\n\t\treturn -1;\n\t}\n\n\tlet maxIdx = 0;\n\tfor (let i = 1; i < array.length; i++) {\n\t\tconst item = array[i];\n\t\tif (comparator(item, array[maxIdx]) > 0) {\n\t\t\tmaxIdx = i;\n\t\t}\n\t}\n\treturn maxIdx;\n}\n\n/**\n * Returns the first mapped value of the array which is not undefined.\n */\nexport function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {\n\tfor (const value of items) {\n\t\tconst mapped = mapFn(value);\n\t\tif (mapped !== undefined) {\n\t\t\treturn mapped;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n", "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { CancellationToken } from 'vs/base/common/cancellation';\nimport { CancellationError } from 'vs/base/common/errors';\nimport { ISplice } from 'vs/base/common/sequence';\nimport { findFirstIdxMonotonousOrArrLen } from './arraysFind';\n\n/**\n * Returns the last element of an array.\n * @param array The array.\n * @param n Which element from the end (default is zero).\n */\nexport function tail<T>(array: ArrayLike<T>, n: number = 0): T | undefined {\n\treturn array[array.length - (1 + n)];\n}\n\nexport function tail2<T>(arr: T[]): [T[], T] {\n\tif (arr.length === 0) {\n\t\tthrow new Error('Invalid tail call');\n\t}\n\n\treturn [arr.slice(0, arr.length - 1), arr[arr.length - 1]];\n}\n\nexport function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {\n\tif (one === other) {\n\t\treturn true;\n\t}\n\n\tif (!one || !other) {\n\t\treturn false;\n\t}\n\n\tif (one.length !== other.length) {\n\t\treturn false;\n\t}\n\n\tfor (let i = 0, len = one.length; i < len; i++) {\n\t\tif (!itemEquals(one[i], other[i])) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Remove the element at `index` by replacing it with the last element. This is faster than `splice`\n * but changes the order of the array\n */\nexport function removeFastWithoutKeepingOrder<T>(array: T[], index: number) {\n\tconst last = array.length - 1;\n\tif (index < last) {\n\t\tarray[index] = array[last];\n\t}\n\tarray.pop();\n}\n\n/**\n * Performs a binary search algorithm over a sorted array.\n *\n * @param array The array being searched.\n * @param key The value we search for.\n * @param comparator A function that takes two array elements and returns zero\n * if they are equal, a negative number if the first element precedes the\n * second one in the sorting order, or a positive number if the second element\n * precedes the first one.\n * @return See {@link binarySearch2}\n */\nexport function binarySearch<T>(array: ReadonlyArray<T>, key: T, comparator: (op1: T, op2: T) => number): number {\n\treturn binarySearch2(array.length, i => comparator(array[i], key));\n}\n\n/**\n * Performs a binary search algorithm over a sorted collection. Useful for cases\n * when we need to perform a binary search over something that isn't actually an\n * array, and converting data to an array would defeat the use of binary search\n * in the first place.\n *\n * @param length The collection length.\n * @param compareToKey A function that takes an index of an element in the\n * collection and returns zero if the value at this index is equal to the\n * search key, a negative number if the value precedes the search key in the\n * sorting order, or a positive number if the search key precedes the value.\n * @return A non-negative index of an element, if found. If not found, the\n * result is -(n+1) (or ~n, using bitwise notation), where n is the index\n * where the key should be inserted to maintain the sorting order.\n */\nexport function binarySearch2(length: number, compareToKey: (index: number) => number): number {\n\tlet low = 0,\n\t\thigh = length - 1;\n\n\twhile (low <= high) {\n\t\tconst mid = ((low + high) / 2) | 0;\n\t\tconst comp = compareToKey(mid);\n\t\tif (comp < 0) {\n\t\t\tlow = mid + 1;\n\t\t} else if (comp > 0) {\n\t\t\thigh = mid - 1;\n\t\t} else {\n\t\t\treturn mid;\n\t\t}\n\t}\n\treturn -(low + 1);\n}\n\ntype Compare<T> = (a: T, b: T) => number;\n\n\nexport function quickSelect<T>(nth: number, data: T[], compare: Compare<T>): T {\n\n\tnth = nth | 0;\n\n\tif (nth >= data.length) {\n\t\tthrow new TypeError('invalid index');\n\t}\n\n\tconst pivotValue = data[Math.floor(data.length * Math.random())];\n\tconst lower: T[] = [];\n\tconst higher: T[] = [];\n\tconst pivots: T[] = [];\n\n\tfor (const value of data) {\n\t\tconst val = compare(value, pivotValue);\n\t\tif (val < 0) {\n\t\t\tlower.push(value);\n\t\t} else if (val > 0) {\n\t\t\thigher.push(value);\n\t\t} else {\n\t\t\tpivots.push(value);\n\t\t}\n\t}\n\n\tif (nth < lower.length) {\n\t\treturn quickSelect(nth, lower, compare);\n\t} else if (nth < lower.length + pivots.length) {\n\t\treturn pivots[0];\n\t} else {\n\t\treturn quickSelect(nth - (lower.length + pivots.length), higher, compare);\n\t}\n}\n\nexport function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {\n\tconst result: T[][] = [];\n\tlet currentGroup: T[] | undefined = undefined;\n\tfor (const element of data.slice(0).sort(compare)) {\n\t\tif (!currentGroup || compare(currentGroup[0], element) !== 0) {\n\t\t\tcurrentGroup = [element];\n\t\t\tresult.push(currentGroup);\n\t\t} else {\n\t\t\tcurrentGroup.push(element);\n\t\t}\n\t}\n\treturn result;\n}\n\n/**\n * Splits the given items into a list of (non-empty) groups.\n * `shouldBeGrouped` is used to decide if two consecutive items should be in the same group.\n * The order of the items is preserved.\n */\nexport function* groupAdjacentBy<T>(items: Iterable<T>, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable<T[]> {\n\tlet currentGroup: T[] | undefined;\n\tlet last: T | undefined;\n\tfor (const item of items) {\n\t\tif (last !== undefined && shouldBeGrouped(last, item)) {\n\t\t\tcurrentGroup!.push(item);\n\t\t} else {\n\t\t\tif (currentGroup) {\n\t\t\t\tyield currentGroup;\n\t\t\t}\n\t\t\tcurrentGroup = [item];\n\t\t}\n\t\tlast = item;\n\t}\n\tif (currentGroup) {\n\t\tyield currentGroup;\n\t}\n}\n\nexport function forEachAdjacent<T>(arr: T[], f: (item1: T | undefined, item2: T | undefined) => void): void {\n\tfor (let i = 0; i <= arr.length; i++) {\n\t\tf(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]);\n\t}\n}\n\nexport function forEachWithNeighbors<T>(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void {\n\tfor (let i = 0; i < arr.length; i++) {\n\t\tf(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);\n\t}\n}\n\ninterface IMutableSplice<T> extends ISplice<T> {\n\treadonly toInsert: T[];\n\tdeleteCount: number;\n}\n\n/**\n * Diffs two *sorted* arrays and computes the splices which apply the diff.\n */\nexport function sortedDiff<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): ISplice<T>[] {\n\tconst result: IMutableSplice<T>[] = [];\n\n\tfunction pushSplice(start: number, deleteCount: number, toInsert: T[]): void {\n\t\tif (deleteCount === 0 && toInsert.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst latest = result[result.length - 1];\n\n\t\tif (latest && latest.start + latest.deleteCount === start) {\n\t\t\tlatest.deleteCount += deleteCount;\n\t\t\tlatest.toInsert.push(...toInsert);\n\t\t} else {\n\t\t\tresult.push({ start, deleteCount, toInsert });\n\t\t}\n\t}\n\n\tlet beforeIdx = 0;\n\tlet afterIdx = 0;\n\n\twhile (true) {\n\t\tif (beforeIdx === before.length) {\n\t\t\tpushSplice(beforeIdx, 0, after.slice(afterIdx));\n\t\t\tbreak;\n\t\t}\n\t\tif (afterIdx === after.length) {\n\t\t\tpushSplice(beforeIdx, before.length - beforeIdx, []);\n\t\t\tbreak;\n\t\t}\n\n\t\tconst beforeElement = before[beforeIdx];\n\t\tconst afterElement = after[afterIdx];\n\t\tconst n = compare(beforeElement, afterElement);\n\t\tif (n === 0) {\n\t\t\t// equal\n\t\t\tbeforeIdx += 1;\n\t\t\tafterIdx += 1;\n\t\t} else if (n < 0) {\n\t\t\t// beforeElement is smaller -> before element removed\n\t\t\tpushSplice(beforeIdx, 1, []);\n\t\t\tbeforeIdx += 1;\n\t\t} else if (n > 0) {\n\t\t\t// beforeElement is greater -> after element added\n\t\t\tpushSplice(beforeIdx, 0, [afterElement]);\n\t\t\tafterIdx += 1;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Takes two *sorted* arrays and computes their delta (removed, added elements).\n * Finishes in `Math.min(before.length, after.length)` steps.\n */\nexport function delta<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): { removed: T[]; added: T[] } {\n\tconst splices = sortedDiff(before, after, compare);\n\tconst removed: T[] = [];\n\tconst added: T[] = [];\n\n\tfor (const splice of splices) {\n\t\tremoved.push(...before.slice(splice.start, splice.start + splice.deleteCount));\n\t\tadded.push(...splice.toInsert);\n\t}\n\n\treturn { removed, added };\n}\n\n/**\n * Returns the top N elements from the array.\n *\n * Faster than sorting the entire array when the array is a lot larger than N.\n *\n * @param array The unsorted array.\n * @param compare A sort function for the elements.\n * @param n The number of elements to return.\n * @return The first n elements from array when sorted with compare.\n */\nexport function top<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, n: number): T[] {\n\tif (n === 0) {\n\t\treturn [];\n\t}\n\tconst result = array.slice(0, n).sort(compare);\n\ttopStep(array, compare, result, n, array.length);\n\treturn result;\n}\n\n/**\n * Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run.\n *\n * Returns the top N elements from the array.\n *\n * Faster than sorting the entire array when the array is a lot larger than N.\n *\n * @param array The unsorted array.\n * @param compare A sort function for the elements.\n * @param n The number of elements to return.\n * @param batch The number of elements to examine before yielding to the event loop.\n * @return The first n elements from array when sorted with compare.\n */\nexport function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise<T[]> {\n\tif (n === 0) {\n\t\treturn Promise.resolve([]);\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\t(async () => {\n\t\t\tconst o = array.length;\n\t\t\tconst result = array.slice(0, n).sort(compare);\n\t\t\tfor (let i = n, m = Math.min(n + batch, o); i < o; i = m, m = Math.min(m + batch, o)) {\n\t\t\t\tif (i > n) {\n\t\t\t\t\tawait new Promise(resolve => setTimeout(resolve)); // any other delay function would starve I/O\n\t\t\t\t}\n\t\t\t\tif (token && token.isCancellationRequested) {\n\t\t\t\t\tthrow new CancellationError();\n\t\t\t\t}\n\t\t\t\ttopStep(array, compare, result, i, m);\n\t\t\t}\n\t\t\treturn result;\n\t\t})()\n\t\t\t.then(resolve, reject);\n\t});\n}\n\nfunction topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, result: T[], i: number, m: number): void {\n\tfor (const n = result.length; i < m; i++) {\n\t\tconst element = array[i];\n\t\tif (compare(element, result[n - 1]) < 0) {\n\t\t\tresult.pop();\n\t\t\tconst j = findFirstIdxMonotonousOrArrLen(result, e => compare(element, e) < 0);\n\t\t\tresult.splice(j, 0, element);\n\t\t}\n\t}\n}\n\n/**\n * @returns New array with all falsy values removed. The original array IS NOT modified.\n */\nexport function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {\n\treturn array.filter((e): e is T => !!e);\n}\n\n/**\n * Remove all falsy values from `array`. The original array IS modified.\n */\nexport function coalesceInPlace<T>(array: Array<T | undefined | null>): asserts array is Array<T> {\n\tlet to = 0;\n\tfor (let i = 0; i < array.length; i++) {\n\t\tif (!!array[i]) {\n\t\t\tarray[to] = array[i];\n\t\t\tto += 1;\n\t\t}\n\t}\n\tarray.length = to;\n}\n\n/**\n * @deprecated Use `Array.copyWithin` instead\n */\nexport function move(array: any[], from: number, to: number): void {\n\tarray.splice(to, 0, array.splice(from, 1)[0]);\n}\n\n/**\n * @returns false if the provided object is an array and not empty.\n */\nexport function isFalsyOrEmpty(obj: any): boolean {\n\treturn !Array.isArray(obj) || obj.length === 0;\n}\n\n/**\n * @returns True if the provided object is an array and has at least one element.\n */\nexport function isNonEmptyArray<T>(obj: T[] | undefined | null): obj is T[];\nexport function isNonEmptyArray<T>(obj: readonly T[] | undefined | null): obj is readonly T[];\nexport function isNonEmptyArray<T>(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] {\n\treturn Array.isArray(obj) && obj.length > 0;\n}\n\n/**\n * Removes duplicates from the given array. The optional keyFn allows to specify\n * how elements are checked for equality by returning an alternate value for each.\n */\nexport function distinct<T>(array: ReadonlyArray<T>, keyFn: (value: T) => any = value => value): T[] {\n\tconst seen = new Set<any>();\n\n\treturn array.filter(element => {\n\t\tconst key = keyFn!(element);\n\t\tif (seen.has(key)) {\n\t\t\treturn false;\n\t\t}\n\t\tseen.add(key);\n\t\treturn true;\n\t});\n}\n\nexport function uniqueFilter<T, R>(keyFn: (t: T) => R): (t: T) => boolean {\n\tconst seen = new Set<R>();\n\n\treturn element => {\n\t\tconst key = keyFn(element);\n\n\t\tif (seen.has(key)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tseen.add(key);\n\t\treturn true;\n\t};\n}\n\nexport function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue: NotFound): T | NotFound;\nexport function firstOrDefault<T>(array: ReadonlyArray<T>): T | undefined;\nexport function firstOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue?: NotFound): T | NotFound | undefined {\n\treturn array.length > 0 ? array[0] : notFoundValue;\n}\n\nexport function lastOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue: NotFound): T | NotFound;\nexport function lastOrDefault<T>(array: ReadonlyArray<T>): T | undefined;\nexport function lastOrDefault<T, NotFound = T>(array: ReadonlyArray<T>, notFoundValue?: NotFound): T | NotFound | undefined {\n\treturn array.length > 0 ? array[array.length - 1] : notFoundValue;\n}\n\nexport function commonPrefixLength<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, equals: (a: T, b: T) => boolean = (a, b) => a === b): number {\n\tlet result = 0;\n\n\tfor (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) {\n\t\tresult++;\n\t}\n\n\treturn result;\n}\n\nexport function range(to: number): number[];\nexport function range(from: number, to: number): number[];\nexport function range(arg: number, to?: number): number[] {\n\tlet from = typeof to === 'number' ? arg : 0;\n\n\tif (typeof to === 'number') {\n\t\tfrom = arg;\n\t} else {\n\t\tfrom = 0;\n\t\tto = arg;\n\t}\n\n\tconst result: number[] = [];\n\n\tif (from <= to) {\n\t\tfor (let i = from; i < to; i++) {\n\t\t\tresult.push(i);\n\t\t}\n\t} else {\n\t\tfor (let i = from; i > to; i--) {\n\t\t\tresult.push(i);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function index<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T };\nexport function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R };\nexport function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper?: (t: T) => R): { [key: string]: R } {\n\treturn array.reduce((r, t) => {\n\t\tr[indexer(t)] = mapper ? mapper(t) : t;\n\t\treturn r;\n\t}, Object.create(null));\n}\n\n/**\n * Inserts an element into an array. Returns a function which, when\n * called, will remove that element from the array.\n *\n * @deprecated In almost all cases, use a `Set<T>` instead.\n */\nexport function insert<T>(array: T[], element: T): () => void {\n\tarray.push(element);\n\n\treturn () => remove(array, element);\n}\n\n/**\n * Removes an element from an array if it can be found.\n *\n * @deprecated In almost all cases, use a `Set<T>` instead.\n */\nexport function remove<T>(array: T[], element: T): T | undefined {\n\tconst index = array.indexOf(element);\n\tif (index > -1) {\n\t\tarray.splice(index, 1);\n\n\t\treturn element;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Insert `insertArr` inside `target` at `insertIndex`.\n * Please don't touch unless you understand https://jsperf.com/inserting-an-array-within-an-array\n */\nexport function arrayInsert<T>(target: T[], insertIndex: number, insertArr: T[]): T[] {\n\tconst before = target.slice(0, insertIndex);\n\tconst after = target.slice(insertIndex);\n\treturn before.concat(insertArr, after);\n}\n\n/**\n * Uses Fisher-Yates shuffle to shuffle the given array\n */\nexport function shuffle<T>(array: T[], _seed?: number): void {\n\tlet rand: () => number;\n\n\tif (typeof _seed === 'number') {\n\t\tlet seed = _seed;\n\t\t// Seeded random number generator in JS. Modified from:\n\t\t// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript\n\t\trand = () => {\n\t\t\tconst x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias\n\t\t\treturn x - Math.floor(x);\n\t\t};\n\t} else {\n\t\trand = Math.random;\n\t}\n\n\tfor (let i = array.length - 1; i > 0; i -= 1) {\n\t\tconst j = Math.floor(rand() * (i + 1));\n\t\tconst temp = array[i];\n\t\tarray[i] = array[j];\n\t\tarray[j] = temp;\n\t}\n}\n\n/**\n * Pushes an element to the start of the array, if found.\n */\nexport function pushToStart<T>(arr: T[], value: T): void {\n\tconst index = arr.indexOf(value);\n\n\tif (index > -1) {\n\t\tarr.splice(index, 1);\n\t\tarr.unshift(value);\n\t}\n}\n\n/**\n * Pushes an element to the end of the array, if found.\n */\nexport function pushToEnd<T>(arr: T[], value: T): void {\n\tconst index = arr.indexOf(value);\n\n\tif (index > -1) {\n\t\tarr.splice(index, 1);\n\t\tarr.push(value);\n\t}\n}\n\nexport function pushMany<T>(arr: T[], items: ReadonlyArray<T>): void {\n\tfor (const item of items) {\n\t\tarr.push(item);\n\t}\n}\n\nexport function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {\n\treturn Array.isArray(items) ?\n\t\titems.map(fn) :\n\t\tfn(items);\n}\n\nexport function asArray<T>(x: T | T[]): T[];\nexport function asArray<T>(x: T | readonly T[]): readonly T[];\nexport function asArray<T>(x: T | T[]): T[] {\n\treturn Array.isArray(x) ? x : [x];\n}\n\nexport function getRandomElement<T>(arr: T[]): T | undefined {\n\treturn arr[Math.floor(Math.random() * arr.length)];\n}\n\n/**\n * Insert the new items in the array.\n * @param array The original array.\n * @param start The zero-based location in the array from which to start inserting elements.\n * @param newItems The items to be inserted\n */\nexport function insertInto<T>(array: T[], start: number, newItems: T[]): void {\n\tconst startIdx = getActualStartIndex(array, start);\n\tconst originalLength = array.length;\n\tconst newItemsLength = newItems.length;\n\tarray.length = originalLength + newItemsLength;\n\t// Move the items after the start index, start from the end so that we don't overwrite any value.\n\tfor (let i = originalLength - 1; i >= startIdx; i--) {\n\t\tarray[i + newItemsLength] = array[i];\n\t}\n\n\tfor (let i = 0; i < newItemsLength; i++) {\n\t\tarray[i + startIdx] = newItems[i];\n\t}\n}\n\n/**\n * Removes elements from an array and inserts new elements in their place, returning the deleted elements. Alternative to the native Array.splice method, it\n * can only support limited number of items due to the maximum call stack size limit.\n * @param array The original array.\n * @param start The zero-based location in the array from which to start removing elements.\n * @param deleteCount The number of elements to remove.\n * @returns An array containing the elements that were deleted.\n */\nexport function splice<T>(array: T[], start: number, deleteCount: number, newItems: T[]): T[] {\n\tconst index = getActualStartIndex(array, start);\n\tlet result = array.splice(index, deleteCount);\n\tif (result === undefined) {\n\t\t// see https://bugs.webkit.org/show_bug.cgi?id=261140\n\t\tresult = [];\n\t}\n\tinsertInto(array, index, newItems);\n\treturn result;\n}\n\n/**\n * Determine the actual start index (same logic as the native splice() or slice())\n * If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided.\n * If negative, it will begin that many elements from the end of the array. (In this case, the origin -1, meaning -n is the index of the nth last element, and is therefore equivalent to the index of array.length - n.) If array.length + start is less than 0, it will begin from index 0.\n * @param array The target array.\n * @param start The operation index.\n */\nfunction getActualStartIndex<T>(array: T[], start: number): number {\n\treturn start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length);\n}\n\n/**\n * When comparing two values,\n * a negative number indicates that the first value is less than the second,\n * a positive number indicates that the first value is greater than the second,\n * and zero indicates that neither is the case.\n*/\nexport type CompareResult = number;\n\nexport namespace CompareResult {\n\texport function isLessThan(result: CompareResult): boolean {\n\t\treturn result < 0;\n\t}\n\n\texport function isLessThanOrEqual(result: CompareResult): boolean {\n\t\treturn result <= 0;\n\t}\n\n\texport function isGreaterThan(result: CompareResult): boolean {\n\t\treturn result > 0;\n\t}\n\n\texport function isNeitherLessOrGreaterThan(result: CompareResult): boolean {\n\t\treturn result === 0;\n\t}\n\n\texport const greaterThan = 1;\n\texport const lessThan = -1;\n\texport const neitherLessOrGreaterThan = 0;\n}\n\n/**\n * A comparator `c` defines a total order `<=` on `T` as following:\n * `c(a, b) <= 0` iff `a` <= `b`.\n * We also have `c(a, b) == 0` iff `c(b, a) == 0`.\n*/\nexport type Comparator<T> = (a: T, b: T) => CompareResult;\n\nexport function compareBy<TItem, TCompareBy>(selector: (item: TItem) => TCompareBy, comparator: Comparator<TCompareBy>): Comparator<TItem> {\n\treturn (a, b) => comparator(selector(a), selector(b));\n}\n\nexport function tieBreakComparators<TItem>(...comparators: Comparator<TItem>[]): Comparator<TItem> {\n\treturn (item1, item2) => {\n\t\tfor (const comparator of comparators) {\n\t\t\tconst result = comparator(item1, item2);\n\t\t\tif (!CompareResult.isNeitherLessOrGreaterThan(result)) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn CompareResult.neitherLessOrGreaterThan;\n\t};\n}\n\n/**\n * The natural order on numbers.\n*/\nexport const numberComparator: Comparator<number> = (a, b) => a - b;\n\nexport const booleanComparator: Comparator<boolean> = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0);\n\nexport function reverseOrder<TItem>(comparator: Comparator<TItem>): Comparator<TItem> {\n\treturn (a, b) => -comparator(a, b);\n}\n\nexport class ArrayQueue<T> {\n\tprivate firstIdx = 0;\n\tprivate lastIdx = this.items.length - 1;\n\n\t/**\n\t * Constructs a queue that is backed by the given array. Runtime is O(1).\n\t*/\n\tconstructor(private readonly items: readonly T[]) { }\n\n\tget length(): number {\n\t\treturn this.lastIdx - this.firstIdx + 1;\n\t}\n\n\t/**\n\t * Consumes elements from the beginning of the queue as long as the predicate returns true.\n\t * If no elements were consumed, `null` is returned. Has a runtime of O(result.length).\n\t*/\n\ttakeWhile(predicate: (value: T) => boolean): T[] | null {\n\t\t// P(k) := k <= this.lastIdx && predicate(this.items[k])\n\t\t// Find s := min { k | k >= this.firstIdx && !P(k) } and return this.data[this.firstIdx...s)\n\n\t\tlet startIdx = this.firstIdx;\n\t\twhile (startIdx < this.items.length && predicate(this.items[startIdx])) {\n\t\t\tstartIdx++;\n\t\t}\n\t\tconst result = startIdx === this.firstIdx ? null : this.items.slice(this.firstIdx, startIdx);\n\t\tthis.firstIdx = startIdx;\n\t\treturn result;\n\t}\n\n\t/**\n\t * Consumes elements from the end of the queue as long as the predicate returns true.\n\t * If no elements were consumed, `null` is returned.\n\t * The result has the same order as the underlying array!\n\t*/\n\ttakeFromEndWhile(predicate: (value: T) => boolean): T[] | null {\n\t\t// P(k) := this.firstIdx >= k && predicate(this.items[k])\n\t\t// Find s := max { k | k <= this.lastIdx && !P(k) } and return this.data(s...this.lastIdx]\n\n\t\tlet endIdx = this.lastIdx;\n\t\twhile (endIdx >= 0 && predicate(this.items[endIdx])) {\n\t\t\tendIdx--;\n\t\t}\n\t\tconst result = endIdx === this.lastIdx ? null : this.items.slice(endIdx + 1, this.lastIdx + 1);\n\t\tthis.lastIdx = endIdx;\n\t\treturn result;\n\t}\n\n\tpeek(): T | undefined {\n\t\tif (this.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.items[this.firstIdx];\n\t}\n\n\tpeekLast(): T | undefined {\n\t\tif (this.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this.items[this.lastIdx];\n\t}\n\n\tdequeue(): T | undefined {\n\t\tconst result = this.items[this.firstIdx];\n\t\tthis.firstIdx++;\n\t\treturn result;\n\t}\n\n\tremoveLast(): T | undefined {\n\t\tconst result = this.items[this.lastIdx];\n\t\tthis.lastIdx--;\n\t\treturn result;\n\t}\n\n\ttakeCount(count: number): T[] {\n\t\tconst result = this.items.slice(this.firstIdx, this.firstIdx + count);\n\t\tthis.firstIdx += count;\n\t\treturn result;\n\t}\n}\n\n/**\n * This class is faster than an iterator and array for lazy computed data.\n*/\nexport class CallbackIterable<T> {\n\tpublic static readonly empty = new CallbackIterable<never>(_callback => { });\n\n\tconstructor(\n\t\t/**\n\t\t * Calls the callback for every item.\n\t\t * Stops when the callback returns false.\n\t\t*/\n\t\tpublic readonly iterate: (callback: (item: T) => boolean) => void\n\t) {\n\t}\n\n\tforEach(handler: (item: T) => void) {\n\t\tthis.iterate(item => { handler(item); return true; });\n\t}\n\n\ttoArray(): T[] {\n\t\tconst result: T[] = [];\n\t\tthis.iterate(item => { result.push(item); return true; });\n\t\treturn result;\n\t}\n\n\tfilter(predicate: (item: T) => boolean): CallbackIterable<T> {\n\t\treturn new CallbackIterable(cb => this.iterate(item => predicate(item) ? cb(item) : true));\n\t}\n\n\tmap<TResult>(mapFn: (item: T) => TResult): CallbackIterable<TResult> {\n\t\treturn new CallbackIterable<TResult>(cb => this.iterate(item => cb(mapFn(item))));\n\t}\n\n\tsome(predicate: (item: T) => boolean): boolean {\n\t\tlet result = false;\n\t\tthis.iterate(item => { result = predicate(item); return !result; });\n\t\treturn result;\n\t}\n\n\tfindFirst(predicate: (item: T) => boolean): T | undefined {\n\t\tlet result: T | undefined;\n\t\tthis.iterate(item => {\n\t\t\tif (predicate(item)) {\n\t\t\t\tresult = item;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn result;\n\t}\n\n\tfindLast(predicate: (item: T) => boolean): T | undefined {\n\t\tlet result: T | undefined;\n\t\tthis.iterate(item => {\n\t\t\tif (predicate(item)) {\n\t\t\t\tresult = item;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn result;\n\t}\n\n\tfindLastMaxBy(comparator: Comparator<T>): T | undefined {\n\t\tlet result: T | undefined;\n\t\tlet first = true;\n\t\tthis.iterate(item => {\n\t\t\tif (first || CompareResult.isGreaterThan(comparator(item, result!))) {\n\t\t\t\tfirst = false;\n\t\t\t\tresult = item;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn result;\n\t}\n}\n\n/**\n * Represents a re-arrangement of items in an array.\n */\nexport class Permutation {\n\tconstructor(private readonly _indexMap: readonly number[]) { }\n\n\t/**\n\t * Returns a permutation that sorts the given array according to the given compare function.\n\t */\n\tpublic static createSortPermutation<T>(arr: readonly T[], compareFn: (a: T, b: T) => number): Permutation {\n\t\tconst sortIndices = Array.from(arr.keys()).sort((index1, index2) => compareFn(arr[index1], arr[index2]));\n\t\treturn new Permutation(sortIndices);\n\t}\n\n\t/**\n\t * Returns a new array with the elements of the given array re-arranged according to this permutation.\n\t */\n\tapply<T>(arr: readonly T[]): T[] {\n\t\treturn arr.map((_, index) => arr[this._indexMap[index]]);\n\t}\n\n\t/**\n\t * Returns a new permutation that undoes the re-arrangement of this permutation.\n\t*/\n\tinverse(): Permutation {\n\t\tconst inverseIndexMap = this._indexMap.slice();\n\t\tfor (let i = 0; i < this._indexMap.length; i++) {\n\t\t\tinverseIndexMap[this._indexMap[i]] = i;\n\t\t}\n\t\treturn new Permutation(inverseIndexMap);\n\t}\n}\n", "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\n/**\n * An interface for a JavaScript object that\n * acts a dictionary. The keys are strings.\n */\nexport type IStringDictionary<V> = Record<string, V>;\n\n/**\n * An interface for a JavaScript object that\n * acts a dictionary. The keys are numbers.\n */\nexport type INumberDictionary<V> = Record<number, V>;\n\n/**\n * Groups the collection into a dictionary based on the provided\n * group function.\n */\nexport function groupBy<K extends string | number | symbol, V>(data: V[], groupFn: (element: V) => K): Record<K, V[]> {\n\tconst result: Record<K, V[]> = Object.create(null);\n\tfor (const element of data) {\n\t\tconst key = groupFn(element);\n\t\tlet target = result[key];\n\t\tif (!target) {\n\t\t\ttarget = result[key] = [];\n\t\t}\n\t\ttarget.push(element);\n\t}\n\treturn result;\n}\n\nexport function diffSets<T>(before: Set<T>, after: Set<T>): { removed: T[]; added: T[] } {\n\tconst removed: T[] = [];\n\tconst added: T[] = [];\n\tfor (const element of before) {\n\t\tif (!after.has(element)) {\n\t\t\tremoved.push(element);\n\t\t}\n\t}\n\tfor (const element of after) {\n\t\tif (!before.has(element)) {\n\t\t\tadded.push(element);\n\t\t}\n\t}\n\treturn { removed, added };\n}\n\nexport function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[]; added: V[] } {\n\tconst removed: V[] = [];\n\tconst added: V[] = [];\n\tfor (const [index, value] of before) {\n\t\tif (!after.has(index)) {\n\t\t\tremoved.push(value);\n\t\t}\n\t}\n\tfor (const [index, value] of after) {\n\t\tif (!before.has(index)) {\n\t\t\tadded.push(value);\n\t\t}\n\t}\n\treturn { removed, added };\n}\n\n/**\n * Computes the intersection of two sets.\n *\n * @param setA - The first set.\n * @param setB - The second iterable.\n * @returns A new set containing the elements that are in both `setA` and `setB`.\n */\nexport function intersection<T>(setA: Set<T>, setB: Iterable<T>): Set<T> {\n\tconst result = new Set<T>();\n\tfor (const elem of setB) {\n\t\tif (setA.has(elem)) {\n\t\t\tresult.add(elem);\n\t\t}\n\t}\n\treturn result;\n}\n\nexport class SetWithKey<T> implements Set<T> {\n\tprivate _map = new Map<any, T>();\n\n\tconstructor(values: T[], private toKey: (t: T) => any) {\n\t\tfor (const value of values) {\n\t\t\tthis.add(value);\n\t\t}\n\t}\n\n\tget size(): number {\n\t\treturn this._map.size;\n\t}\n\n\tadd(value: T): this {\n\t\tconst key = this.toKey(value);\n\t\tthis._map.set(key, value);\n\t\treturn this;\n\t}\n\n\tdelete(value: T): boolean {\n\t\treturn this._map.delete(this.toKey(value));\n\t}\n\n\thas(value: T): boolean {\n\t\treturn this._map.has(this.toKey(value));\n\t}\n\n\t*entries(): IterableIterator<[T, T]> {\n\t\tfor (const entry of this._map.values()) {\n\t\t\tyield [entry, entry];\n\t\t}\n\t}\n\n\tkeys(): IterableIterator<T> {\n\t\treturn this.values();\n\t}\n\n\t*values(): IterableIterator<T> {\n\t\tfor (const entry of this._map.values()) {\n\t\t\tyield entry;\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis._map.clear();\n\t}\n\n\tforEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void {\n\t\tthis._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));\n\t}\n\n\t[Symbol.iterator](): IterableIterator<T> {\n\t\treturn this.values();\n\t}\n\n\t[Symbol.toStringTag]: string = 'SetWithKey';\n}\n", "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nexport function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {\n\tlet result = map.get(key);\n\tif (result === undefined) {\n\t\tresult = value;\n\t\tmap.set(key, result);\n\t}\n\n\treturn result;\n}\n\nexport function mapToString<K, V>(map: Map<K, V>): string {\n\tconst entries: string[] = [];\n\tmap.forEach((value, key) => {\n\t\tentries.push(`${key} => ${value}`);\n\t});\n\n\treturn `Map(${map.size}) {${entries.join(', ')}}`;\n}\n\nexport function setToString<K>(set: Set<K>): string {\n\tconst entries: K[] = [];\n\tset.forEach(value => {\n\t\tentries.push(value);\n\t});\n\n\treturn `Set(${set.size}) {${entries.join(', ')}}`;\n}\n\nexport const enum Touch {\n\tNone = 0,\n\tAsOld = 1,\n\tAsNew = 2\n}\n\nexport class CounterSet<T> {\n\n\tprivate map = new Map<T, number>();\n\n\tadd(value: T): CounterSet<T> {\n\t\tthis.map.set(value, (this.map.get(value) || 0) + 1);\n\t\treturn this;\n\t}\n\n\tdelete(value: T): boolean {\n\t\tlet counter = this.map.get(value) || 0;\n\n\t\tif (counter === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tcounter--;\n\n\t\tif (counter === 0) {\n\t\t\tthis.map.delete(value);\n\t\t} else {\n\t\t\tthis.map.set(value, counter);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\thas(value: T): boolean {\n\t\treturn this.map.has(value);\n\t}\n}\n\n/**\n * A map that allows access both by keys and values.\n * **NOTE**: values need to be unique.\n */\nexport class BidirectionalMap<K, V> {\n\n\tprivate readonly _m1 = new Map<K, V>();\n\tprivate readonly _m2 = new Map<V, K>();\n\n\tconstructor(entries?: readonly (readonly [K, V])[]) {\n\t\tif (entries) {\n\t\t\tfor (const [key, value] of entries) {\n\t\t\t\tthis.set(key, value);\n\t\t\t}\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis._m1.clear();\n\t\tthis._m2.