• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    __String,
3    CharacterCodes,
4    Comparer,
5    Comparison,
6    Debug,
7    EqualityComparer,
8    ESMap,
9    isWhiteSpaceLike,
10    Iterator,
11    Map,
12    MapLike,
13    Push,
14    Queue,
15    ReadonlyESMap,
16    ReadonlySet,
17    Set,
18    SortedArray,
19    SortedReadonlyArray,
20    TextSpan,
21    UnderscoreEscapedMap
22} from "./_namespaces/ts";
23
24/** @internal */
25export function getIterator<I extends readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined>(iterable: I): Iterator<
26    I extends ReadonlyESMap<infer K, infer V> ? [K, V] :
27    I extends ReadonlySet<infer T> ? T :
28    I extends readonly (infer T)[] ? T :
29    I extends undefined ? undefined :
30    never>;
31/** @internal */
32export function getIterator<K, V>(iterable: ReadonlyESMap<K, V>): Iterator<[K, V]>;
33/** @internal */
34export function getIterator<K, V>(iterable: ReadonlyESMap<K, V> | undefined): Iterator<[K, V]> | undefined;
35/** @internal */
36export function getIterator<T>(iterable: readonly T[] | ReadonlySet<T>): Iterator<T>;
37/** @internal */
38export function getIterator<T>(iterable: readonly T[] | ReadonlySet<T> | undefined): Iterator<T> | undefined;
39/** @internal */
40export function getIterator(iterable: readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined): Iterator<any> | undefined {
41    if (iterable) {
42        if (isArray(iterable)) return arrayIterator(iterable);
43        if (iterable instanceof Map) return iterable.entries();
44        if (iterable instanceof Set) return iterable.values();
45        throw new Error("Iteration not supported.");
46    }
47}
48
49/** @internal */
50export const emptyArray: never[] = [] as never[];
51/** @internal */
52export const emptyMap: ReadonlyESMap<never, never> = new Map<never, never>();
53/** @internal */
54export const emptySet: ReadonlySet<never> = new Set<never>();
55
56/** @internal */
57export function length(array: readonly any[] | undefined): number {
58    return array ? array.length : 0;
59}
60
61/**
62 * Iterates through 'array' by index and performs the callback on each element of array until the callback
63 * returns a truthy value, then returns that value.
64 * If no such value is found, the callback is applied to each element of array and undefined is returned.
65 *
66 * @internal
67 */
68export function forEach<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
69    if (array) {
70        for (let i = 0; i < array.length; i++) {
71            const result = callback(array[i], i);
72            if (result) {
73                return result;
74            }
75        }
76    }
77    return undefined;
78}
79
80/**
81 * Like `forEach`, but iterates in reverse order.
82 *
83 * @internal
84 */
85export function forEachRight<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
86    if (array) {
87        for (let i = array.length - 1; i >= 0; i--) {
88            const result = callback(array[i], i);
89            if (result) {
90                return result;
91            }
92        }
93    }
94    return undefined;
95}
96
97/** Like `forEach`, but suitable for use with numbers and strings (which may be falsy).
98 *
99 * @internal
100 */
101export function firstDefined<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
102    if (array === undefined) {
103        return undefined;
104    }
105
106    for (let i = 0; i < array.length; i++) {
107        const result = callback(array[i], i);
108        if (result !== undefined) {
109            return result;
110        }
111    }
112    return undefined;
113}
114
115/** @internal */
116export function firstDefinedIterator<T, U>(iter: Iterator<T>, callback: (element: T) => U | undefined): U | undefined {
117    while (true) {
118        const iterResult = iter.next();
119        if (iterResult.done) {
120            return undefined;
121        }
122        const result = callback(iterResult.value);
123        if (result !== undefined) {
124            return result;
125        }
126    }
127}
128
129/** @internal */
130export function reduceLeftIterator<T, U>(iterator: Iterator<T> | undefined, f: (memo: U, value: T, i: number) => U, initial: U): U {
131    let result = initial;
132    if (iterator) {
133        for (let step = iterator.next(), pos = 0; !step.done; step = iterator.next(), pos++) {
134            result = f(result, step.value, pos);
135        }
136    }
137    return result;
138}
139
140/** @internal */
141export function zipWith<T, U, V>(arrayA: readonly T[], arrayB: readonly U[], callback: (a: T, b: U, index: number) => V): V[] {
142    const result: V[] = [];
143    Debug.assertEqual(arrayA.length, arrayB.length);
144    for (let i = 0; i < arrayA.length; i++) {
145        result.push(callback(arrayA[i], arrayB[i], i));
146    }
147    return result;
148}
149
150/** @internal */
151export function zipToIterator<T, U>(arrayA: readonly T[], arrayB: readonly U[]): Iterator<[T, U]> {
152    Debug.assertEqual(arrayA.length, arrayB.length);
153    let i = 0;
154    return {
155        next() {
156            if (i === arrayA.length) {
157                return { value: undefined as never, done: true };
158            }
159            i++;
160            return { value: [arrayA[i - 1], arrayB[i - 1]] as [T, U], done: false };
161        }
162    };
163}
164
165/** @internal */
166export function zipToMap<K, V>(keys: readonly K[], values: readonly V[]): ESMap<K, V> {
167    Debug.assert(keys.length === values.length);
168    const map = new Map<K, V>();
169    for (let i = 0; i < keys.length; ++i) {
170        map.set(keys[i], values[i]);
171    }
172    return map;
173}
174
175/**
176 * Creates a new array with `element` interspersed in between each element of `input`
177 * if there is more than 1 value in `input`. Otherwise, returns the existing array.
178 *
179 * @internal
180 */
181export function intersperse<T>(input: T[], element: T): T[] {
182    if (input.length <= 1) {
183        return input;
184    }
185    const result: T[] = [];
186    for (let i = 0, n = input.length; i < n; i++) {
187        if (i) result.push(element);
188        result.push(input[i]);
189    }
190    return result;
191}
192
193/**
194 * Iterates through `array` by index and performs the callback on each element of array until the callback
195 * returns a falsey value, then returns false.
196 * If no such value is found, the callback is applied to each element of array and `true` is returned.
197 *
198 * @internal
199 */
200export function every<T>(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean {
201    if (array) {
202        for (let i = 0; i < array.length; i++) {
203            if (!callback(array[i], i)) {
204                return false;
205            }
206        }
207    }
208
209    return true;
210}
211
212/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found.
213 *
214 * @internal
215 */
216export function find<T, U extends T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined;
217/** @internal */
218export function find<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined;
219/** @internal */
220export function find<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined {
221    if (array === undefined) return undefined;
222    for (let i = startIndex ?? 0; i < array.length; i++) {
223        const value = array[i];
224        if (predicate(value, i)) {
225            return value;
226        }
227    }
228    return undefined;
229}
230
231/** @internal */
232export function findLast<T, U extends T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => element is U, startIndex?: number): U | undefined;
233/** @internal */
234export function findLast<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined;
235/** @internal */
236export function findLast<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): T | undefined {
237    if (array === undefined) return undefined;
238    for (let i = startIndex ?? array.length - 1; i >= 0; i--) {
239        const value = array[i];
240        if (predicate(value, i)) {
241            return value;
242        }
243    }
244    return undefined;
245}
246
247/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found.
248 *
249 * @internal
250 */
251export function findIndex<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): number {
252    if (array === undefined) return -1;
253    for (let i = startIndex ?? 0; i < array.length; i++) {
254        if (predicate(array[i], i)) {
255            return i;
256        }
257    }
258    return -1;
259}
260
261/** @internal */
262export function findLastIndex<T>(array: readonly T[] | undefined, predicate: (element: T, index: number) => boolean, startIndex?: number): number {
263    if (array === undefined) return -1;
264    for (let i = startIndex ?? array.length - 1; i >= 0; i--) {
265        if (predicate(array[i], i)) {
266            return i;
267        }
268    }
269    return -1;
270}
271
272/**
273 * Returns the first truthy result of `callback`, or else fails.
274 * This is like `forEach`, but never returns undefined.
275 *
276 * @internal
277 */
278export function findMap<T, U>(array: readonly T[], callback: (element: T, index: number) => U | undefined): U {
279    for (let i = 0; i < array.length; i++) {
280        const result = callback(array[i], i);
281        if (result) {
282            return result;
283        }
284    }
285    return Debug.fail();
286}
287
288/** @internal */
289export function contains<T>(array: readonly T[] | undefined, value: T, equalityComparer: EqualityComparer<T> = equateValues): boolean {
290    if (array) {
291        for (const v of array) {
292            if (equalityComparer(v, value)) {
293                return true;
294            }
295        }
296    }
297    return false;
298}
299
300/** @internal */
301export function arraysEqual<T>(a: readonly T[], b: readonly T[], equalityComparer: EqualityComparer<T> = equateValues): boolean {
302    return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i]));
303}
304
305/** @internal */
306export function indexOfAnyCharCode(text: string, charCodes: readonly number[], start?: number): number {
307    for (let i = start || 0; i < text.length; i++) {
308        if (contains(charCodes, text.charCodeAt(i))) {
309            return i;
310        }
311    }
312    return -1;
313}
314
315/** @internal */
316export function countWhere<T>(array: readonly T[] | undefined, predicate: (x: T, i: number) => boolean): number {
317    let count = 0;
318    if (array) {
319        for (let i = 0; i < array.length; i++) {
320            const v = array[i];
321            if (predicate(v, i)) {
322                count++;
323            }
324        }
325    }
326    return count;
327}
328
329/**
330 * Filters an array by a predicate function. Returns the same array instance if the predicate is
331 * true for all elements, otherwise returns a new array instance containing the filtered subset.
332 *
333 * @internal
334 */
335export function filter<T, U extends T>(array: T[], f: (x: T) => x is U): U[];
336/** @internal */
337export function filter<T>(array: T[], f: (x: T) => boolean): T[];
338/** @internal */
339export function filter<T, U extends T>(array: readonly T[], f: (x: T) => x is U): readonly U[];
340/** @internal */
341export function filter<T, U extends T>(array: readonly T[], f: (x: T) => boolean): readonly T[];
342/** @internal */
343export function filter<T, U extends T>(array: T[] | undefined, f: (x: T) => x is U): U[] | undefined;
344/** @internal */
345export function filter<T>(array: T[] | undefined, f: (x: T) => boolean): T[] | undefined;
346/** @internal */
347export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => x is U): readonly U[] | undefined;
348/** @internal */
349export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined;
350/** @internal */
351export function filter<T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined {
352    if (array) {
353        const len = array.length;
354        let i = 0;
355        while (i < len && f(array[i])) i++;
356        if (i < len) {
357            const result = array.slice(0, i);
358            i++;
359            while (i < len) {
360                const item = array[i];
361                if (f(item)) {
362                    result.push(item);
363                }
364                i++;
365            }
366            return result;
367        }
368    }
369    return array;
370}
371
372/** @internal */
373export function filterMutate<T>(array: T[], f: (x: T, i: number, array: T[]) => boolean): void {
374    let outIndex = 0;
375    for (let i = 0; i < array.length; i++) {
376        if (f(array[i], i, array)) {
377            array[outIndex] = array[i];
378            outIndex++;
379        }
380    }
381    array.length = outIndex;
382}
383
384/** @internal */
385export function clear(array: unknown[]): void {
386    array.length = 0;
387}
388
389/** @internal */
390export function map<T, U>(array: readonly T[], f: (x: T, i: number) => U): U[];
391/** @internal */
392export function map<T, U>(array: readonly T[] | undefined, f: (x: T, i: number) => U): U[] | undefined;
393/** @internal */
394export function map<T, U>(array: readonly T[] | undefined, f: (x: T, i: number) => U): U[] | undefined {
395    let result: U[] | undefined;
396    if (array) {
397        result = [];
398        for (let i = 0; i < array.length; i++) {
399            result.push(f(array[i], i));
400        }
401    }
402    return result;
403}
404
405
406/** @internal */
407export function mapIterator<T, U>(iter: Iterator<T>, mapFn: (x: T) => U): Iterator<U> {
408    return {
409        next() {
410            const iterRes = iter.next();
411            return iterRes.done ? iterRes as { done: true, value: never } : { value: mapFn(iterRes.value), done: false };
412        }
413    };
414}
415
416/**
417 * Maps from T to T and avoids allocation if all elements map to themselves
418 *
419 * @internal */
420export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[];
421/** @internal */
422export function sameMap<T>(array: readonly T[], f: (x: T, i: number) => T): readonly T[];
423/** @internal */
424export function sameMap<T>(array: T[] | undefined, f: (x: T, i: number) => T): T[] | undefined;
425/** @internal */
426export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined;
427/** @internal */
428export function sameMap<T>(array: readonly T[] | undefined, f: (x: T, i: number) => T): readonly T[] | undefined {
429    if (array) {
430        for (let i = 0; i < array.length; i++) {
431            const item = array[i];
432            const mapped = f(item, i);
433            if (item !== mapped) {
434                const result = array.slice(0, i);
435                result.push(mapped);
436                for (i++; i < array.length; i++) {
437                    result.push(f(array[i], i));
438                }
439                return result;
440            }
441        }
442    }
443    return array;
444}
445
446/**
447 * Flattens an array containing a mix of array or non-array elements.
448 *
449 * @param array The array to flatten.
450 *
451 * @internal
452 */
453export function flatten<T>(array: T[][] | readonly (T | readonly T[] | undefined)[]): T[] {
454    const result = [];
455    for (const v of array) {
456        if (v) {
457            if (isArray(v)) {
458                addRange(result, v);
459            }
460            else {
461                result.push(v);
462            }
463        }
464    }
465    return result;
466}
467
468/**
469 * Maps an array. If the mapped value is an array, it is spread into the result.
470 *
471 * @param array The array to map.
472 * @param mapfn The callback used to map the result into one or more values.
473 *
474 * @internal
475 */
476export function flatMap<T, U>(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): readonly U[] {
477    let result: U[] | undefined;
478    if (array) {
479        for (let i = 0; i < array.length; i++) {
480            const v = mapfn(array[i], i);
481            if (v) {
482                if (isArray(v)) {
483                    result = addRange(result, v);
484                }
485                else {
486                    result = append(result, v);
487                }
488            }
489        }
490    }
491    return result || emptyArray;
492}
493
494/** @internal */
495export function flatMapToMutable<T, U>(array: readonly T[] | undefined, mapfn: (x: T, i: number) => U | readonly U[] | undefined): U[] {
496    const result: U[] = [];
497    if (array) {
498        for (let i = 0; i < array.length; i++) {
499            const v = mapfn(array[i], i);
500            if (v) {
501                if (isArray(v)) {
502                    addRange(result, v);
503                }
504                else {
505                    result.push(v);
506                }
507            }
508        }
509    }
510    return result;
511}
512
513/** @internal */
514export function flatMapIterator<T, U>(iter: Iterator<T>, mapfn: (x: T) => readonly U[] | Iterator<U> | undefined): Iterator<U> {
515    const first = iter.next();
516    if (first.done) {
517        return emptyIterator;
518    }
519    let currentIter = getIterator(first.value);
520    return {
521        next() {
522            while (true) {
523                const currentRes = currentIter.next();
524                if (!currentRes.done) {
525                    return currentRes;
526                }
527                const iterRes = iter.next();
528                if (iterRes.done) {
529                    return iterRes as { done: true, value: never };
530                }
531                currentIter = getIterator(iterRes.value);
532            }
533        },
534    };
535
536    function getIterator(x: T): Iterator<U> {
537        const res = mapfn(x);
538        return res === undefined ? emptyIterator : isArray(res) ? arrayIterator(res) : res;
539    }
540}
541
542/**
543 * Maps an array. If the mapped value is an array, it is spread into the result.
544 * Avoids allocation if all elements map to themselves.
545 *
546 * @param array The array to map.
547 * @param mapfn The callback used to map the result into one or more values.
548 *
549 * @internal
550 */
551export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | readonly T[]): T[];
552/** @internal */
553export function sameFlatMap<T>(array: readonly T[], mapfn: (x: T, i: number) => T | readonly T[]): readonly T[];
554/** @internal */
555export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] {
556    let result: T[] | undefined;
557    if (array) {
558        for (let i = 0; i < array.length; i++) {
559            const item = array[i];
560            const mapped = mapfn(item, i);
561            if (result || item !== mapped || isArray(mapped)) {
562                if (!result) {
563                    result = array.slice(0, i);
564                }
565                if (isArray(mapped)) {
566                    addRange(result, mapped);
567                }
568                else {
569                    result.push(mapped);
570                }
571            }
572        }
573    }
574    return result || array;
575}
576
577/** @internal */
578export function mapAllOrFail<T, U>(array: readonly T[], mapFn: (x: T, i: number) => U | undefined): U[] | undefined {
579    const result: U[] = [];
580    for (let i = 0; i < array.length; i++) {
581        const mapped = mapFn(array[i], i);
582        if (mapped === undefined) {
583            return undefined;
584        }
585        result.push(mapped);
586    }
587    return result;
588}
589
590/** @internal */
591export function mapDefined<T, U>(array: readonly T[] | undefined, mapFn: (x: T, i: number) => U | undefined): U[] {
592    const result: U[] = [];
593    if (array) {
594        for (let i = 0; i < array.length; i++) {
595            const mapped = mapFn(array[i], i);
596            if (mapped !== undefined) {
597                result.push(mapped);
598            }
599        }
600    }
601    return result;
602}
603
604/** @internal */
605export function mapDefinedIterator<T, U>(iter: Iterator<T>, mapFn: (x: T) => U | undefined): Iterator<U> {
606    return {
607        next() {
608            while (true) {
609                const res = iter.next();
610                if (res.done) {
611                    return res as { done: true, value: never };
612                }
613                const value = mapFn(res.value);
614                if (value !== undefined) {
615                    return { value, done: false };
616                }
617            }
618        }
619    };
620}
621
622/** @internal */
623export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1>, f: (key: K1, value: V1) => readonly [K2, V2] | undefined): ESMap<K2, V2>;
624/** @internal */
625export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): ESMap<K2, V2> | undefined;
626/** @internal */
627export function mapDefinedEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): ESMap<K2, V2> | undefined {
628    if (!map) {
629        return undefined;
630    }
631
632    const result = new Map<K2, V2>();
633    map.forEach((value, key) => {
634        const entry = f(key, value);
635        if (entry !== undefined) {
636            const [newKey, newValue] = entry;
637            if (newKey !== undefined && newValue !== undefined) {
638                result.set(newKey, newValue);
639            }
640        }
641    });
642
643    return result;
644}
645
646/** @internal */
647export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1>, f: (value: V1) => V2 | undefined): Set<V2>;
648/** @internal */
649export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1> | undefined, f: (value: V1) => V2 | undefined): Set<V2> | undefined;
650/** @internal */
651export function mapDefinedValues<V1, V2>(set: ReadonlySet<V1> | undefined, f: (value: V1) => V2 | undefined): Set<V2> | undefined {
652    if (set) {
653        const result = new Set<V2>();
654        set.forEach(value => {
655            const newValue = f(value);
656            if (newValue !== undefined) {
657                result.add(newValue);
658            }
659        });
660        return result;
661    }
662}
663
664/** @internal */
665export function getOrUpdate<K, V>(map: ESMap<K, V>, key: K, callback: () => V) {
666    if (map.has(key)) {
667        return map.get(key)!;
668    }
669    const value = callback();
670    map.set(key, value);
671    return value;
672}
673
674/** @internal */
675export function tryAddToSet<T>(set: Set<T>, value: T) {
676    if (!set.has(value)) {
677        set.add(value);
678        return true;
679    }
680    return false;
681}
682
683/** @internal */
684export const emptyIterator: Iterator<never> = { next: () => ({ value: undefined as never, done: true }) };
685
686/** @internal */
687export function singleIterator<T>(value: T): Iterator<T> {
688    let done = false;
689    return {
690        next() {
691            const wasDone = done;
692            done = true;
693            return wasDone ? { value: undefined as never, done: true } : { value, done: false };
694        }
695    };
696}
697
698/**
699 * Maps contiguous spans of values with the same key.
700 *
701 * @param array The array to map.
702 * @param keyfn A callback used to select the key for an element.
703 * @param mapfn A callback used to map a contiguous chunk of values to a single value.
704 *
705 * @internal
706 */
707export function spanMap<T, K, U>(array: readonly T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[];
708/** @internal */
709export function spanMap<T, K, U>(array: readonly T[] | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined;
710/** @internal */
711export function spanMap<T, K, U>(array: readonly T[] | undefined, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] | undefined {
712    let result: U[] | undefined;
713    if (array) {
714        result = [];
715        const len = array.length;
716        let previousKey: K | undefined;
717        let key: K | undefined;
718        let start = 0;
719        let pos = 0;
720        while (start < len) {
721            while (pos < len) {
722                const value = array[pos];
723                key = keyfn(value, pos);
724                if (pos === 0) {
725                    previousKey = key;
726                }
727                else if (key !== previousKey) {
728                    break;
729                }
730
731                pos++;
732            }
733
734            if (start < pos) {
735                const v = mapfn(array.slice(start, pos), previousKey!, start, pos);
736                if (v) {
737                    result.push(v);
738                }
739
740                start = pos;
741            }
742
743            previousKey = key;
744            pos++;
745        }
746    }
747
748    return result;
749}
750
751/** @internal */
752export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1>, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2>;
753/** @internal */
754export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2> | undefined;
755/** @internal */
756export function mapEntries<K1, V1, K2, V2>(map: ReadonlyESMap<K1, V1> | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): ESMap<K2, V2> | undefined {
757    if (!map) {
758        return undefined;
759    }
760
761    const result = new Map<K2, V2>();
762    map.forEach((value, key) => {
763        const [newKey, newValue] = f(key, value);
764        result.set(newKey, newValue);
765    });
766    return result;
767}
768
769/** @internal */
770export function some<T>(array: readonly T[] | undefined): array is readonly T[];
771/** @internal */
772export function some<T>(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean;
773/** @internal */
774export function some<T>(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean {
775    if (array) {
776        if (predicate) {
777            for (const v of array) {
778                if (predicate(v)) {
779                    return true;
780                }
781            }
782        }
783        else {
784            return array.length > 0;
785        }
786    }
787    return false;
788}
789
790/** Calls the callback with (start, afterEnd) index pairs for each range where 'pred' is true.
791 *
792 * @internal
793 */
794export function getRangesWhere<T>(arr: readonly T[], pred: (t: T) => boolean, cb: (start: number, afterEnd: number) => void): void {
795    let start: number | undefined;
796    for (let i = 0; i < arr.length; i++) {
797        if (pred(arr[i])) {
798            start = start === undefined ? i : start;
799        }
800        else {
801            if (start !== undefined) {
802                cb(start, i);
803                start = undefined;
804            }
805        }
806    }
807    if (start !== undefined) cb(start, arr.length);
808}
809
810/** @internal */
811export function concatenate<T>(array1: T[], array2: T[]): T[];
812/** @internal */
813export function concatenate<T>(array1: readonly T[], array2: readonly T[]): readonly T[];
814/** @internal */
815export function concatenate<T>(array1: T[] | undefined, array2: T[] | undefined): T[];
816/** @internal */
817export function concatenate<T>(array1: readonly T[] | undefined, array2: readonly T[] | undefined): readonly T[];
818/** @internal */
819export function concatenate<T>(array1: T[], array2: T[]): T[] {
820    if (!some(array2)) return array1;
821    if (!some(array1)) return array2;
822    return [...array1, ...array2];
823}
824
825function selectIndex(_: unknown, i: number) {
826    return i;
827}
828
829/** @internal */
830export function indicesOf(array: readonly unknown[]): number[] {
831    return array.map(selectIndex);
832}
833
834function deduplicateRelational<T>(array: readonly T[], equalityComparer: EqualityComparer<T>, comparer: Comparer<T>) {
835    // Perform a stable sort of the array. This ensures the first entry in a list of
836    // duplicates remains the first entry in the result.
837    const indices = indicesOf(array);
838    stableSortIndices(array, indices, comparer);
839
840    let last = array[indices[0]];
841    const deduplicated: number[] = [indices[0]];
842    for (let i = 1; i < indices.length; i++) {
843        const index = indices[i];
844        const item = array[index];
845        if (!equalityComparer(last, item)) {
846            deduplicated.push(index);
847            last = item;
848        }
849    }
850
851    // restore original order
852    deduplicated.sort();
853    return deduplicated.map(i => array[i]);
854}
855
856function deduplicateEquality<T>(array: readonly T[], equalityComparer: EqualityComparer<T>) {
857    const result: T[] = [];
858    for (const item of array) {
859        pushIfUnique(result, item, equalityComparer);
860    }
861    return result;
862}
863
864/**
865 * Deduplicates an unsorted array.
866 * @param equalityComparer An `EqualityComparer` used to determine if two values are duplicates.
867 * @param comparer An optional `Comparer` used to sort entries before comparison, though the
868 * result will remain in the original order in `array`.
869 *
870 * @internal
871 */
872export function deduplicate<T>(array: readonly T[], equalityComparer: EqualityComparer<T>, comparer?: Comparer<T>): T[] {
873    return array.length === 0 ? [] :
874        array.length === 1 ? array.slice() :
875        comparer ? deduplicateRelational(array, equalityComparer, comparer) :
876        deduplicateEquality(array, equalityComparer);
877}
878
879/**
880 * Deduplicates an array that has already been sorted.
881 */
882function deduplicateSorted<T>(array: SortedReadonlyArray<T>, comparer: EqualityComparer<T> | Comparer<T>): SortedReadonlyArray<T> {
883    if (array.length === 0) return emptyArray as any as SortedReadonlyArray<T>;
884
885    let last = array[0];
886    const deduplicated: T[] = [last];
887    for (let i = 1; i < array.length; i++) {
888        const next = array[i];
889        switch (comparer(next, last)) {
890            // equality comparison
891            case true:
892
893            // relational comparison
894            // falls through
895            case Comparison.EqualTo:
896                continue;
897
898            case Comparison.LessThan:
899                // If `array` is sorted, `next` should **never** be less than `last`.
900                return Debug.fail("Array is unsorted.");
901        }
902
903        deduplicated.push(last = next);
904    }
905
906    return deduplicated as any as SortedReadonlyArray<T>;
907}
908
909/** @internal */
910export function createSortedArray<T>(): SortedArray<T> {
911    return [] as any as SortedArray<T>; // TODO: GH#19873
912}
913
914/** @internal */
915export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>, allowDuplicates?: boolean): boolean {
916    if (array.length === 0) {
917        array.push(insert);
918        return true;
919    }
920
921    const insertIndex = binarySearch(array, insert, identity, compare);
922    if (insertIndex < 0) {
923        array.splice(~insertIndex, 0, insert);
924        return true;
925    }
926
927    if (allowDuplicates) {
928        array.splice(insertIndex, 0, insert);
929        return true;
930    }
931
932    return false;
933}
934
935/** @internal */
936export function sortAndDeduplicate<T>(array: readonly string[]): SortedReadonlyArray<string>;
937/** @internal */
938export function sortAndDeduplicate<T>(array: readonly T[], comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T>;
939/** @internal */
940export function sortAndDeduplicate<T>(array: readonly T[], comparer?: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T> {
941    return deduplicateSorted(sort(array, comparer), equalityComparer || comparer || compareStringsCaseSensitive as any as Comparer<T>);
942}
943
944/** @internal */
945export function arrayIsSorted<T>(array: readonly T[], comparer: Comparer<T>) {
946    if (array.length < 2) return true;
947    let prevElement = array[0];
948    for (const element of array.slice(1)) {
949        if (comparer(prevElement, element) === Comparison.GreaterThan) {
950            return false;
951        }
952        prevElement = element;
953    }
954    return true;
955}
956
957/** @internal */
958export function arrayIsEqualTo<T>(array1: readonly T[] | undefined, array2: readonly T[] | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean {
959    if (!array1 || !array2) {
960        return array1 === array2;
961    }
962
963    if (array1.length !== array2.length) {
964        return false;
965    }
966
967    for (let i = 0; i < array1.length; i++) {
968        if (!equalityComparer(array1[i], array2[i], i)) {
969            return false;
970        }
971    }
972
973    return true;
974}
975
976/**
977 * Compacts an array, removing any falsey elements.
978 *
979 * @internal
980 */
981export function compact<T>(array: (T | undefined | null | false | 0 | "")[]): T[];
982/** @internal */
983export function compact<T>(array: readonly (T | undefined | null | false | 0 | "")[]): readonly T[];
984// ESLint thinks these can be combined with the above - they cannot; they'd produce higher-priority inferences and prevent the falsey types from being stripped
985/** @internal */
986export function compact<T>(array: T[]): T[]; // eslint-disable-line @typescript-eslint/unified-signatures
987/** @internal */
988export function compact<T>(array: readonly T[]): readonly T[]; // eslint-disable-line @typescript-eslint/unified-signatures
989/** @internal */
990export function compact<T>(array: T[]): T[] {
991    let result: T[] | undefined;
992    if (array) {
993        for (let i = 0; i < array.length; i++) {
994            const v = array[i];
995            if (result || !v) {
996                if (!result) {
997                    result = array.slice(0, i);
998                }
999                if (v) {
1000                    result.push(v);
1001                }
1002            }
1003        }
1004    }
1005    return result || array;
1006}
1007
1008/**
1009 * Gets the relative complement of `arrayA` with respect to `arrayB`, returning the elements that
1010 * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
1011 * based on the provided comparer.
1012 *
1013 * @internal
1014 */
1015export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T>): T[] | undefined {
1016    if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB;
1017    const result: T[] = [];
1018    loopB: for (let offsetA = 0, offsetB = 0; offsetB < arrayB.length; offsetB++) {
1019        if (offsetB > 0) {
1020            // Ensure `arrayB` is properly sorted.
1021            Debug.assertGreaterThanOrEqual(comparer(arrayB[offsetB], arrayB[offsetB - 1]), Comparison.EqualTo);
1022        }
1023
1024        loopA: for (const startA = offsetA; offsetA < arrayA.length; offsetA++) {
1025            if (offsetA > startA) {
1026                // Ensure `arrayA` is properly sorted. We only need to perform this check if
1027                // `offsetA` has changed since we entered the loop.
1028                Debug.assertGreaterThanOrEqual(comparer(arrayA[offsetA], arrayA[offsetA - 1]), Comparison.EqualTo);
1029            }
1030
1031            switch (comparer(arrayB[offsetB], arrayA[offsetA])) {
1032                case Comparison.LessThan:
1033                    // If B is less than A, B does not exist in arrayA. Add B to the result and
1034                    // move to the next element in arrayB without changing the current position
1035                    // in arrayA.
1036                    result.push(arrayB[offsetB]);
1037                    continue loopB;
1038                case Comparison.EqualTo:
1039                    // If B is equal to A, B exists in arrayA. Move to the next element in
1040                    // arrayB without adding B to the result or changing the current position
1041                    // in arrayA.
1042                    continue loopB;
1043                case Comparison.GreaterThan:
1044                    // If B is greater than A, we need to keep looking for B in arrayA. Move to
1045                    // the next element in arrayA and recheck.
1046                    continue loopA;
1047            }
1048        }
1049    }
1050    return result;
1051}
1052
1053/** @internal */
1054export function sum<T extends Record<K, number>, K extends string>(array: readonly T[], prop: K): number {
1055    let result = 0;
1056    for (const v of array) {
1057        result += v[prop];
1058    }
1059    return result;
1060}
1061
1062/**
1063 * Appends a value to an array, returning the array.
1064 *
1065 * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array
1066 * is created if `value` was appended.
1067 * @param value The value to append to the array. If `value` is `undefined`, nothing is
1068 * appended.
1069 *
1070 * @internal
1071 */
1072export function append<TArray extends any[] | undefined, TValue extends NonNullable<TArray>[number] | undefined>(to: TArray, value: TValue): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable<TArray>[number][];
1073/** @internal */
1074export function append<T>(to: T[], value: T | undefined): T[];
1075/** @internal */
1076export function append<T>(to: T[] | undefined, value: T): T[];
1077/** @internal */
1078export function append<T>(to: T[] | undefined, value: T | undefined): T[] | undefined;
1079/** @internal */
1080export function append<T>(to: Push<T>, value: T | undefined): void;
1081/** @internal */
1082export function append<T>(to: T[], value: T | undefined): T[] | undefined {
1083    if (value === undefined) return to;
1084    if (to === undefined) return [value];
1085    to.push(value);
1086    return to;
1087}
1088
1089/**
1090 * Combines two arrays, values, or undefineds into the smallest container that can accommodate the resulting set:
1091 *
1092 * ```
1093 * undefined -> undefined -> undefined
1094 * T -> undefined -> T
1095 * T -> T -> T[]
1096 * T[] -> undefined -> T[] (no-op)
1097 * T[] -> T -> T[]         (append)
1098 * T[] -> T[] -> T[]       (concatenate)
1099 * ```
1100 *
1101 * @internal
1102 */
1103export function combine<T>(xs: T | readonly T[] | undefined, ys: T | readonly T[] | undefined): T | readonly T[] | undefined;
1104/** @internal */
1105export function combine<T>(xs: T | T[] | undefined, ys: T | T[] | undefined): T | T[] | undefined;
1106/** @internal */
1107export function combine<T>(xs: T | T[] | undefined, ys: T | T[] | undefined) {
1108    if (xs === undefined) return ys;
1109    if (ys === undefined) return xs;
1110    if (isArray(xs)) return isArray(ys) ? concatenate(xs, ys) : append(xs, ys);
1111    if (isArray(ys)) return append(ys, xs);
1112    return [xs, ys];
1113}
1114
1115/**
1116 * Gets the actual offset into an array for a relative offset. Negative offsets indicate a
1117 * position offset from the end of the array.
1118 */
1119function toOffset(array: readonly any[], offset: number) {
1120    return offset < 0 ? array.length + offset : offset;
1121}
1122
1123/**
1124 * Appends a range of value to an array, returning the array.
1125 *
1126 * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array
1127 * is created if `value` was appended.
1128 * @param from The values to append to the array. If `from` is `undefined`, nothing is
1129 * appended. If an element of `from` is `undefined`, that element is not appended.
1130 * @param start The offset in `from` at which to start copying values.
1131 * @param end The offset in `from` at which to stop copying values (non-inclusive).
1132 *
1133 * @internal
1134 */
1135export function addRange<T>(to: T[], from: readonly T[] | undefined, start?: number, end?: number): T[];
1136/** @internal */
1137export function addRange<T>(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined;
1138/** @internal */
1139export function addRange<T>(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined {
1140    if (from === undefined || from.length === 0) return to;
1141    if (to === undefined) return from.slice(start, end);
1142    start = start === undefined ? 0 : toOffset(from, start);
1143    end = end === undefined ? from.length : toOffset(from, end);
1144    for (let i = start; i < end && i < from.length; i++) {
1145        if (from[i] !== undefined) {
1146            to.push(from[i]);
1147        }
1148    }
1149    return to;
1150}
1151
1152/**
1153 * @return Whether the value was added.
1154 *
1155 * @internal
1156 */
1157export function pushIfUnique<T>(array: T[], toAdd: T, equalityComparer?: EqualityComparer<T>): boolean {
1158    if (contains(array, toAdd, equalityComparer)) {
1159        return false;
1160    }
1161    else {
1162        array.push(toAdd);
1163        return true;
1164    }
1165}
1166
1167/**
1168 * Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array.
1169 *
1170 * @internal
1171 */
1172export function appendIfUnique<T>(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer<T>): T[] {
1173    if (array) {
1174        pushIfUnique(array, toAdd, equalityComparer);
1175        return array;
1176    }
1177    else {
1178        return [toAdd];
1179    }
1180}
1181
1182function stableSortIndices<T>(array: readonly T[], indices: number[], comparer: Comparer<T>) {
1183    // sort indices by value then position
1184    indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y));
1185}
1186
1187/**
1188 * Returns a new sorted array.
1189 *
1190 * @internal
1191 */
1192export function sort<T>(array: readonly T[], comparer?: Comparer<T>): SortedReadonlyArray<T> {
1193    return (array.length === 0 ? array : array.slice().sort(comparer)) as SortedReadonlyArray<T>;
1194}
1195
1196/** @internal */
1197export function arrayIterator<T>(array: readonly T[]): Iterator<T> {
1198    let i = 0;
1199    return { next: () => {
1200        if (i === array.length) {
1201            return { value: undefined as never, done: true };
1202        }
1203        else {
1204            i++;
1205            return { value: array[i - 1], done: false };
1206        }
1207    }};
1208}
1209
1210/** @internal */
1211export function arrayReverseIterator<T>(array: readonly T[]): Iterator<T> {
1212    let i = array.length;
1213    return {
1214        next: () => {
1215            if (i === 0) {
1216                return { value: undefined as never, done: true };
1217            }
1218            else {
1219                i--;
1220                return { value: array[i], done: false };
1221            }
1222        }
1223    };
1224}
1225
1226/**
1227 * Stable sort of an array. Elements equal to each other maintain their relative position in the array.
1228 *
1229 * @internal
1230 */
1231export function stableSort<T>(array: readonly T[], comparer: Comparer<T>): SortedReadonlyArray<T> {
1232    const indices = indicesOf(array);
1233    stableSortIndices(array, indices, comparer);
1234    return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>;
1235}
1236
1237/** @internal */
1238export function rangeEquals<T>(array1: readonly T[], array2: readonly T[], pos: number, end: number) {
1239    while (pos < end) {
1240        if (array1[pos] !== array2[pos]) {
1241            return false;
1242        }
1243        pos++;
1244    }
1245    return true;
1246}
1247
1248/**
1249 * Returns the element at a specific offset in an array if non-empty, `undefined` otherwise.
1250 * A negative offset indicates the element should be retrieved from the end of the array.
1251 *
1252 * @internal
1253 */
1254export function elementAt<T>(array: readonly T[] | undefined, offset: number): T | undefined {
1255    if (array) {
1256        offset = toOffset(array, offset);
1257        if (offset < array.length) {
1258            return array[offset];
1259        }
1260    }
1261    return undefined;
1262}
1263
1264/**
1265 * Returns the first element of an array if non-empty, `undefined` otherwise.
1266 *
1267 * @internal
1268 */
1269export function firstOrUndefined<T>(array: readonly T[] | undefined): T | undefined {
1270    return array === undefined || array.length === 0 ? undefined : array[0];
1271}
1272
1273/** @internal */
1274export function first<T>(array: readonly T[]): T {
1275    Debug.assert(array.length !== 0);
1276    return array[0];
1277}
1278
1279/**
1280 * Returns the last element of an array if non-empty, `undefined` otherwise.
1281 *
1282 * @internal
1283 */
1284export function lastOrUndefined<T>(array: readonly T[] | undefined): T | undefined {
1285    return array === undefined || array.length === 0 ? undefined : array[array.length - 1];
1286}
1287
1288/** @internal */
1289export function last<T>(array: readonly T[]): T {
1290    Debug.assert(array.length !== 0);
1291    return array[array.length - 1];
1292}
1293
1294/**
1295 * Returns the only element of an array if it contains only one element, `undefined` otherwise.
1296 *
1297 * @internal
1298 */
1299export function singleOrUndefined<T>(array: readonly T[] | undefined): T | undefined {
1300    return array && array.length === 1
1301        ? array[0]
1302        : undefined;
1303}
1304
1305/**
1306 * Returns the only element of an array if it contains only one element; throws otherwise.
1307 *
1308 * @internal
1309 */
1310export function single<T>(array: readonly T[]): T {
1311    return Debug.checkDefined(singleOrUndefined(array));
1312}
1313
1314/**
1315 * Returns the only element of an array if it contains only one element; otherwise, returns the
1316 * array.
1317 *
1318 * @internal
1319 */
1320export function singleOrMany<T>(array: T[]): T | T[];
1321/** @internal */
1322export function singleOrMany<T>(array: readonly T[]): T | readonly T[];
1323/** @internal */
1324export function singleOrMany<T>(array: T[] | undefined): T | T[] | undefined;
1325/** @internal */
1326export function singleOrMany<T>(array: readonly T[] | undefined): T | readonly T[] | undefined;
1327/** @internal */
1328export function singleOrMany<T>(array: readonly T[] | undefined): T | readonly T[] | undefined {
1329    return array && array.length === 1
1330        ? array[0]
1331        : array;
1332}
1333
1334/** @internal */
1335export function replaceElement<T>(array: readonly T[], index: number, value: T): T[] {
1336    const result = array.slice(0);
1337    result[index] = value;
1338    return result;
1339}
1340
1341/**
1342 * Performs a binary search, finding the index at which `value` occurs in `array`.
1343 * If no such index is found, returns the 2's-complement of first index at which
1344 * `array[index]` exceeds `value`.
1345 * @param array A sorted array whose first element must be no larger than number
1346 * @param value The value to be searched for in the array.
1347 * @param keySelector A callback used to select the search key from `value` and each element of
1348 * `array`.
1349 * @param keyComparer A callback used to compare two keys in a sorted array.
1350 * @param offset An offset into `array` at which to start the search.
1351 *
1352 * @internal
1353 */
1354export function binarySearch<T, U>(array: readonly T[], value: T, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
1355    return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset);
1356}
1357
1358/**
1359 * Performs a binary search, finding the index at which an object with `key` occurs in `array`.
1360 * If no such index is found, returns the 2's-complement of first index at which
1361 * `array[index]` exceeds `key`.
1362 * @param array A sorted array whose first element must be no larger than number
1363 * @param key The key to be searched for in the array.
1364 * @param keySelector A callback used to select the search key from each element of `array`.
1365 * @param keyComparer A callback used to compare two keys in a sorted array.
1366 * @param offset An offset into `array` at which to start the search.
1367 *
1368 * @internal
1369 */
1370export function binarySearchKey<T, U>(array: readonly T[], key: U, keySelector: (v: T, i: number) => U, keyComparer: Comparer<U>, offset?: number): number {
1371    if (!some(array)) {
1372        return -1;
1373    }
1374
1375    let low = offset || 0;
1376    let high = array.length - 1;
1377    while (low <= high) {
1378        const middle = low + ((high - low) >> 1);
1379        const midKey = keySelector(array[middle], middle);
1380        switch (keyComparer(midKey, key)) {
1381            case Comparison.LessThan:
1382                low = middle + 1;
1383                break;
1384            case Comparison.EqualTo:
1385                return middle;
1386            case Comparison.GreaterThan:
1387                high = middle - 1;
1388                break;
1389        }
1390    }
1391
1392    return ~low;
1393}
1394
1395/** @internal */
1396export function reduceLeft<T, U>(array: readonly T[] | undefined, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
1397/** @internal */
1398export function reduceLeft<T>(array: readonly T[], f: (memo: T, value: T, i: number) => T): T | undefined;
1399/** @internal */
1400export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T | undefined {
1401    if (array && array.length > 0) {
1402        const size = array.length;
1403        if (size > 0) {
1404            let pos = start === undefined || start < 0 ? 0 : start;
1405            const end = count === undefined || pos + count > size - 1 ? size - 1 : pos + count;
1406            let result: T;
1407            if (arguments.length <= 2) {
1408                result = array[pos];
1409                pos++;
1410            }
1411            else {
1412                result = initial!;
1413            }
1414            while (pos <= end) {
1415                result = f(result, array[pos], pos);
1416                pos++;
1417            }
1418            return result;
1419        }
1420    }
1421    return initial;
1422}
1423
1424const hasOwnProperty = Object.prototype.hasOwnProperty;
1425
1426/**
1427 * Indicates whether a map-like contains an own property with the specified key.
1428 *
1429 * @param map A map-like.
1430 * @param key A property key.
1431 *
1432 * @internal
1433 */
1434export function hasProperty(map: MapLike<any>, key: string): boolean {
1435    return hasOwnProperty.call(map, key);
1436}
1437
1438/**
1439 * Gets the value of an owned property in a map-like.
1440 *
1441 * @param map A map-like.
1442 * @param key A property key.
1443 *
1444 * @internal
1445 */
1446export function getProperty<T>(map: MapLike<T>, key: string): T | undefined {
1447    return hasOwnProperty.call(map, key) ? map[key] : undefined;
1448}
1449
1450/**
1451 * Gets the owned, enumerable property keys of a map-like.
1452 *
1453 * @internal
1454 */
1455export function getOwnKeys<T>(map: MapLike<T>): string[] {
1456    const keys: string[] = [];
1457    for (const key in map) {
1458        if (hasOwnProperty.call(map, key)) {
1459            keys.push(key);
1460        }
1461    }
1462
1463    return keys;
1464}
1465
1466/** @internal */
1467export function getAllKeys(obj: object): string[] {
1468    const result: string[] = [];
1469    do {
1470        const names = Object.getOwnPropertyNames(obj);
1471        for (const name of names) {
1472            pushIfUnique(result, name);
1473        }
1474    } while (obj = Object.getPrototypeOf(obj));
1475    return result;
1476}
1477
1478/** @internal */
1479export function getOwnValues<T>(collection: MapLike<T> | T[]): T[] {
1480    const values: T[] = [];
1481    for (const key in collection) {
1482        if (hasOwnProperty.call(collection, key)) {
1483            values.push((collection as MapLike<T>)[key]);
1484        }
1485    }
1486
1487    return values;
1488}
1489
1490const _entries = Object.entries || (<T>(obj: MapLike<T>) => {
1491    const keys = getOwnKeys(obj);
1492    const result: [string, T][] = Array(keys.length);
1493    for (let i = 0; i < keys.length; i++) {
1494        result[i] = [keys[i], obj[keys[i]]];
1495    }
1496    return result;
1497});
1498
1499/** @internal */
1500export function getEntries<T>(obj: MapLike<T>): [string, T][] {
1501    return obj ? _entries(obj) : [];
1502}
1503
1504/** @internal */
1505export function arrayOf<T>(count: number, f: (index: number) => T): T[] {
1506    const result = new Array(count);
1507    for (let i = 0; i < count; i++) {
1508        result[i] = f(i);
1509    }
1510    return result;
1511}
1512
1513/** Shims `Array.from`.
1514 *
1515 * @internal
1516 */
1517export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map: (t: T) => U): U[];
1518/** @internal */
1519export function arrayFrom<T>(iterator: Iterator<T> | IterableIterator<T>): T[];
1520/** @internal */
1521export function arrayFrom<T, U>(iterator: Iterator<T> | IterableIterator<T>, map?: (t: T) => U): (T | U)[] {
1522    const result: (T | U)[] = [];
1523    for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
1524        result.push(map ? map(iterResult.value) : iterResult.value);
1525    }
1526    return result;
1527}
1528
1529
1530/** @internal */
1531export function assign<T extends object>(t: T, ...args: (T | undefined)[]) {
1532    for (const arg of args) {
1533        if (arg === undefined) continue;
1534        for (const p in arg) {
1535            if (hasProperty(arg, p)) {
1536                t[p] = arg[p];
1537            }
1538        }
1539    }
1540    return t;
1541}
1542
1543/**
1544 * Performs a shallow equality comparison of the contents of two map-likes.
1545 *
1546 * @param left A map-like whose properties should be compared.
1547 * @param right A map-like whose properties should be compared.
1548 *
1549 * @internal
1550 */
1551export function equalOwnProperties<T>(left: MapLike<T> | undefined, right: MapLike<T> | undefined, equalityComparer: EqualityComparer<T> = equateValues) {
1552    if (left === right) return true;
1553    if (!left || !right) return false;
1554    for (const key in left) {
1555        if (hasOwnProperty.call(left, key)) {
1556            if (!hasOwnProperty.call(right, key)) return false;
1557            if (!equalityComparer(left[key], right[key])) return false;
1558        }
1559    }
1560
1561    for (const key in right) {
1562        if (hasOwnProperty.call(right, key)) {
1563            if (!hasOwnProperty.call(left, key)) return false;
1564        }
1565    }
1566
1567    return true;
1568}
1569
1570/**
1571 * Creates a map from the elements of an array.
1572 *
1573 * @param array the array of input elements.
1574 * @param makeKey a function that produces a key for a given element.
1575 *
1576 * This function makes no effort to avoid collisions; if any two elements produce
1577 * the same key with the given 'makeKey' function, then the element with the higher
1578 * index in the array will be the one associated with the produced key.
1579 *
1580 * @internal
1581 */
1582export function arrayToMap<K, V>(array: readonly V[], makeKey: (value: V) => K | undefined): ESMap<K, V>;
1583/** @internal */
1584export function arrayToMap<K, V1, V2>(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V2): ESMap<K, V2>;
1585/** @internal */
1586export function arrayToMap<T>(array: readonly T[], makeKey: (value: T) => string | undefined): ESMap<string, T>;
1587/** @internal */
1588export function arrayToMap<T, U>(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): ESMap<string, U>;
1589/** @internal */
1590export function arrayToMap<K, V1, V2>(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V1 | V2 = identity): ESMap<K, V1 | V2> {
1591    const result = new Map<K, V1 | V2>();
1592    for (const value of array) {
1593        const key = makeKey(value);
1594        if (key !== undefined) result.set(key, makeValue(value));
1595    }
1596    return result;
1597}
1598
1599/** @internal */
1600export function arrayToNumericMap<T>(array: readonly T[], makeKey: (value: T) => number): T[];
1601/** @internal */
1602export function arrayToNumericMap<T, U>(array: readonly T[], makeKey: (value: T) => number, makeValue: (value: T) => U): U[];
1603/** @internal */
1604export function arrayToNumericMap<T, U>(array: readonly T[], makeKey: (value: T) => number, makeValue: (value: T) => T | U = identity): (T | U)[] {
1605    const result: (T | U)[] = [];
1606    for (const value of array) {
1607        result[makeKey(value)] = makeValue(value);
1608    }
1609    return result;
1610}
1611
1612/** @internal */
1613export function arrayToMultiMap<K, V>(values: readonly V[], makeKey: (value: V) => K): MultiMap<K, V>;
1614/** @internal */
1615export function arrayToMultiMap<K, V, U>(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => U): MultiMap<K, U>;
1616/** @internal */
1617export function arrayToMultiMap<K, V, U>(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => V | U = identity): MultiMap<K, V | U> {
1618    const result = createMultiMap<K, V | U>();
1619    for (const value of values) {
1620        result.add(makeKey(value), makeValue(value));
1621    }
1622    return result;
1623}
1624
1625/** @internal */
1626export function group<T, K>(values: readonly T[], getGroupId: (value: T) => K): readonly (readonly T[])[];
1627/** @internal */
1628export function group<T, K, R>(values: readonly T[], getGroupId: (value: T) => K, resultSelector: (values: readonly T[]) => R): R[];
1629/** @internal */
1630export function group<T>(values: readonly T[], getGroupId: (value: T) => string): readonly (readonly T[])[];
1631/** @internal */
1632export function group<T, R>(values: readonly T[], getGroupId: (value: T) => string, resultSelector: (values: readonly T[]) => R): R[];
1633/** @internal */
1634export function group<T, K>(values: readonly T[], getGroupId: (value: T) => K, resultSelector: (values: readonly T[]) => readonly T[] = identity): readonly (readonly T[])[] {
1635    return arrayFrom(arrayToMultiMap(values, getGroupId).values(), resultSelector);
1636}
1637
1638/** @internal */
1639export function clone<T>(object: T): T {
1640    const result: any = {};
1641    for (const id in object) {
1642        if (hasOwnProperty.call(object, id)) {
1643            result[id] = (object as any)[id];
1644        }
1645    }
1646    return result;
1647}
1648
1649/**
1650 * Creates a new object by adding the own properties of `second`, then the own properties of `first`.
1651 *
1652 * NOTE: This means that if a property exists in both `first` and `second`, the property in `first` will be chosen.
1653 *
1654 * @internal
1655 */
1656export function extend<T1, T2>(first: T1, second: T2): T1 & T2 {
1657    const result: T1 & T2 = {} as any;
1658    for (const id in second) {
1659        if (hasOwnProperty.call(second, id)) {
1660            (result as any)[id] = (second as any)[id];
1661        }
1662    }
1663
1664    for (const id in first) {
1665        if (hasOwnProperty.call(first, id)) {
1666            (result as any)[id] = (first as any)[id];
1667        }
1668    }
1669
1670    return result;
1671}
1672
1673/** @internal */
1674export function copyProperties<T1 extends T2, T2>(first: T1, second: T2) {
1675    for (const id in second) {
1676        if (hasOwnProperty.call(second, id)) {
1677            (first as any)[id] = second[id];
1678        }
1679    }
1680}
1681
1682/** @internal */
1683export function maybeBind<T, A extends any[], R>(obj: T, fn: ((this: T, ...args: A) => R) | undefined): ((...args: A) => R) | undefined {
1684    return fn ? fn.bind(obj) : undefined;
1685}
1686
1687/** @internal */
1688export interface MultiMap<K, V> extends ESMap<K, V[]> {
1689    /**
1690     * Adds the value to an array of values associated with the key, and returns the array.
1691     * Creates the array if it does not already exist.
1692     */
1693    add(key: K, value: V): V[];
1694    /**
1695     * Removes a value from an array of values associated with the key.
1696     * Does not preserve the order of those values.
1697     * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`.
1698     */
1699    remove(key: K, value: V): void;
1700}
1701
1702/** @internal */
1703export function createMultiMap<K, V>(): MultiMap<K, V>;
1704/** @internal */
1705export function createMultiMap<V>(): MultiMap<string, V>;
1706/** @internal */
1707export function createMultiMap<K, V>(): MultiMap<K, V> {
1708    const map = new Map<K, V[]>() as MultiMap<K, V>;
1709    map.add = multiMapAdd;
1710    map.remove = multiMapRemove;
1711    return map;
1712}
1713function multiMapAdd<K, V>(this: MultiMap<K, V>, key: K, value: V) {
1714    let values = this.get(key);
1715    if (values) {
1716        values.push(value);
1717    }
1718    else {
1719        this.set(key, values = [value]);
1720    }
1721    return values;
1722}
1723function multiMapRemove<K, V>(this: MultiMap<K, V>, key: K, value: V) {
1724    const values = this.get(key);
1725    if (values) {
1726        unorderedRemoveItem(values, value);
1727        if (!values.length) {
1728            this.delete(key);
1729        }
1730    }
1731}
1732
1733/** @internal */
1734export interface UnderscoreEscapedMultiMap<T> extends UnderscoreEscapedMap<T[]> {
1735    /**
1736     * Adds the value to an array of values associated with the key, and returns the array.
1737     * Creates the array if it does not already exist.
1738     */
1739    add(key: __String, value: T): T[];
1740    /**
1741     * Removes a value from an array of values associated with the key.
1742     * Does not preserve the order of those values.
1743     * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`.
1744     */
1745    remove(key: __String, value: T): void;
1746}
1747
1748/** @internal */
1749export function createUnderscoreEscapedMultiMap<T>(): UnderscoreEscapedMultiMap<T> {
1750    return createMultiMap() as UnderscoreEscapedMultiMap<T>;
1751}
1752
1753/** @internal */
1754export function createQueue<T>(items?: readonly T[]): Queue<T> {
1755    const elements: (T | undefined)[] = items?.slice() || [];
1756    let headIndex = 0;
1757
1758    function isEmpty() {
1759        return headIndex === elements.length;
1760    }
1761
1762    function enqueue(...items: T[]) {
1763        elements.push(...items);
1764    }
1765
1766    function dequeue(): T {
1767        if (isEmpty()) {
1768            throw new Error("Queue is empty");
1769        }
1770
1771        const result = elements[headIndex] as T;
1772        elements[headIndex] = undefined; // Don't keep referencing dequeued item
1773        headIndex++;
1774
1775        // If more than half of the queue is empty, copy the remaining elements to the
1776        // front and shrink the array (unless we'd be saving fewer than 100 slots)
1777        if (headIndex > 100 && headIndex > (elements.length >> 1)) {
1778            const newLength = elements.length - headIndex;
1779            elements.copyWithin(/*target*/ 0, /*start*/ headIndex);
1780
1781            elements.length = newLength;
1782            headIndex = 0;
1783        }
1784
1785        return result;
1786    }
1787
1788    return {
1789        enqueue,
1790        dequeue,
1791        isEmpty,
1792    };
1793}
1794
1795/**
1796 * Creates a Set with custom equality and hash code functionality.  This is useful when you
1797 * want to use something looser than object identity - e.g. "has the same span".
1798 *
1799 * If `equals(a, b)`, it must be the case that `getHashCode(a) === getHashCode(b)`.
1800 * The converse is not required.
1801 *
1802 * To facilitate a perf optimization (lazy allocation of bucket arrays), `TElement` is
1803 * assumed not to be an array type.
1804 *
1805 * @internal
1806 */
1807export function createSet<TElement, THash = number>(getHashCode: (element: TElement) => THash, equals: EqualityComparer<TElement>): Set<TElement> {
1808    const multiMap = new Map<THash, TElement | TElement[]>();
1809    let size = 0;
1810
1811    function getElementIterator(): Iterator<TElement> {
1812        const valueIt = multiMap.values();
1813        let arrayIt: Iterator<TElement> | undefined;
1814        return {
1815            next: () => {
1816                while (true) {
1817                    if (arrayIt) {
1818                        const n = arrayIt.next();
1819                        if (!n.done) {
1820                            return { value: n.value };
1821                        }
1822                        arrayIt = undefined;
1823                    }
1824                    else {
1825                        const n = valueIt.next();
1826                        if (n.done) {
1827                            return { value: undefined, done: true };
1828                        }
1829                        if (!isArray(n.value)) {
1830                            return { value: n.value };
1831                        }
1832                        arrayIt = arrayIterator(n.value);
1833                    }
1834                }
1835            }
1836        };
1837    }
1838
1839    const set: Set<TElement> = {
1840        has(element: TElement): boolean {
1841            const hash = getHashCode(element);
1842            if (!multiMap.has(hash)) return false;
1843            const candidates = multiMap.get(hash)!;
1844            if (!isArray(candidates)) return equals(candidates, element);
1845
1846            for (const candidate of candidates) {
1847                if (equals(candidate, element)) {
1848                    return true;
1849                }
1850            }
1851            return false;
1852        },
1853        add(element: TElement): Set<TElement> {
1854            const hash = getHashCode(element);
1855            if (multiMap.has(hash)) {
1856                const values = multiMap.get(hash)!;
1857                if (isArray(values)) {
1858                    if (!contains(values, element, equals)) {
1859                        values.push(element);
1860                        size++;
1861                    }
1862                }
1863                else {
1864                    const value = values;
1865                    if (!equals(value, element)) {
1866                        multiMap.set(hash, [ value, element ]);
1867                        size++;
1868                    }
1869                }
1870            }
1871            else {
1872                multiMap.set(hash, element);
1873                size++;
1874            }
1875
1876            return this;
1877        },
1878        delete(element: TElement): boolean {
1879            const hash = getHashCode(element);
1880            if (!multiMap.has(hash)) return false;
1881            const candidates = multiMap.get(hash)!;
1882            if (isArray(candidates)) {
1883                for (let i = 0; i < candidates.length; i++) {
1884                    if (equals(candidates[i], element)) {
1885                        if (candidates.length === 1) {
1886                            multiMap.delete(hash);
1887                        }
1888                        else if (candidates.length === 2) {
1889                            multiMap.set(hash, candidates[1 - i]);
1890                        }
1891                        else {
1892                            unorderedRemoveItemAt(candidates, i);
1893                        }
1894                        size--;
1895                        return true;
1896                    }
1897                }
1898            }
1899            else {
1900                const candidate = candidates;
1901                if (equals(candidate, element)) {
1902                    multiMap.delete(hash);
1903                    size--;
1904                    return true;
1905                }
1906            }
1907
1908            return false;
1909        },
1910        clear(): void {
1911            multiMap.clear();
1912            size = 0;
1913        },
1914        get size() {
1915            return size;
1916        },
1917        forEach(action: (value: TElement, key: TElement) => void): void {
1918            for (const elements of arrayFrom(multiMap.values())) {
1919                if (isArray(elements)) {
1920                    for (const element of elements) {
1921                        action(element, element);
1922                    }
1923                }
1924                else {
1925                    const element = elements;
1926                    action(element, element);
1927                }
1928            }
1929        },
1930        keys(): Iterator<TElement> {
1931            return getElementIterator();
1932        },
1933        values(): Iterator<TElement> {
1934            return getElementIterator();
1935        },
1936        entries(): Iterator<[TElement, TElement]> {
1937            const it = getElementIterator();
1938            return {
1939                next: () => {
1940                    const n = it.next();
1941                    return n.done ? n : { value: [ n.value, n.value ] };
1942                }
1943            };
1944        },
1945    };
1946
1947    return set;
1948}
1949
1950/**
1951 * Tests whether a value is an array.
1952 *
1953 * @internal
1954 */
1955export function isArray(value: any): value is readonly unknown[] {
1956    return Array.isArray ? Array.isArray(value) : value instanceof Array;
1957}
1958
1959/** @internal */
1960export function toArray<T>(value: T | T[]): T[];
1961/** @internal */
1962export function toArray<T>(value: T | readonly T[]): readonly T[];
1963/** @internal */
1964export function toArray<T>(value: T | T[]): T[] {
1965    return isArray(value) ? value : [value];
1966}
1967
1968/**
1969 * Tests whether a value is string
1970 *
1971 * @internal
1972 */
1973export function isString(text: unknown): text is string {
1974    return typeof text === "string";
1975}
1976/** @internal */
1977export function isNumber(x: unknown): x is number {
1978    return typeof x === "number";
1979}
1980
1981/** @internal */
1982export function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined;
1983/** @internal */
1984export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined;
1985/** @internal */
1986export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined {
1987    return value !== undefined && test(value) ? value : undefined;
1988}
1989
1990/** @internal */
1991export function cast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut {
1992    if (value !== undefined && test(value)) return value;
1993
1994    return Debug.fail(`Invalid cast. The supplied value ${value} did not pass the test '${Debug.getFunctionName(test)}'.`);
1995}
1996
1997/** Does nothing.
1998 *
1999 * @internal
2000 */
2001export function noop(_?: unknown): void { }
2002
2003/** @internal */
2004export const noopPush: Push<any> = {
2005    push: noop,
2006    length: 0
2007};
2008
2009/** Do nothing and return false
2010 *
2011 * @internal
2012 */
2013export function returnFalse(): false {
2014    return false;
2015}
2016
2017/** Do nothing and return true
2018 *
2019 * @internal
2020 */
2021export function returnTrue(): true {
2022    return true;
2023}
2024
2025/** Do nothing and return undefined
2026 *
2027 * @internal
2028 */
2029export function returnUndefined(): undefined {
2030    return undefined;
2031}
2032
2033/** Returns its argument.
2034 *
2035 * @internal
2036 */
2037export function identity<T>(x: T) {
2038    return x;
2039}
2040
2041/** Returns lower case string
2042 *
2043 * @internal
2044 */
2045export function toLowerCase(x: string) {
2046    return x.toLowerCase();
2047}
2048
2049// We convert the file names to lower case as key for file name on case insensitive file system
2050// While doing so we need to handle special characters (eg \u0130) to ensure that we dont convert
2051// it to lower case, fileName with its lowercase form can exist along side it.
2052// Handle special characters and make those case sensitive instead
2053//
2054// |-#--|-Unicode--|-Char code-|-Desc-------------------------------------------------------------------|
2055// | 1. | i        | 105       | Ascii i                                                                |
2056// | 2. | I        | 73        | Ascii I                                                                |
2057// |-------- Special characters ------------------------------------------------------------------------|
2058// | 3. | \u0130   | 304       | Upper case I with dot above                                            |
2059// | 4. | i,\u0307 | 105,775   | i, followed by 775: Lower case of (3rd item)                           |
2060// | 5. | I,\u0307 | 73,775    | I, followed by 775: Upper case of (4th item), lower case is (4th item) |
2061// | 6. | \u0131   | 305       | Lower case i without dot, upper case is I (2nd item)                   |
2062// | 7. | \u00DF   | 223       | Lower case sharp s                                                     |
2063//
2064// Because item 3 is special where in its lowercase character has its own
2065// upper case form we cant convert its case.
2066// Rest special characters are either already in lower case format or
2067// they have corresponding upper case character so they dont need special handling
2068//
2069// But to avoid having to do string building for most common cases, also ignore
2070// a-z, 0-9, \u0131, \u00DF, \, /, ., : and space
2071const fileNameLowerCaseRegExp = /[^\u0130\u0131\u00DFa-z0-9\\/:\-_\. ]+/g;
2072/**
2073 * Case insensitive file systems have descripencies in how they handle some characters (eg. turkish Upper case I with dot on top - \u0130)
2074 * This function is used in places where we want to make file name as a key on these systems
2075 * It is possible on mac to be able to refer to file name with I with dot on top as a fileName with its lower case form
2076 * But on windows we cannot. Windows can have fileName with I with dot on top next to its lower case and they can not each be referred with the lowercase forms
2077 * Technically we would want this function to be platform sepcific as well but
2078 * our api has till now only taken caseSensitive as the only input and just for some characters we dont want to update API and ensure all customers use those api
2079 * We could use upper case and we would still need to deal with the descripencies but
2080 * we want to continue using lower case since in most cases filenames are lowercasewe and wont need any case changes and avoid having to store another string for the key
2081 * So for this function purpose, we go ahead and assume character I with dot on top it as case sensitive since its very unlikely to use lower case form of that special character
2082 *
2083 * @internal
2084 */
2085export function toFileNameLowerCase(x: string) {
2086    return fileNameLowerCaseRegExp.test(x) ?
2087        x.replace(fileNameLowerCaseRegExp, toLowerCase) :
2088        x;
2089}
2090
2091/** Throws an error because a function is not implemented.
2092 *
2093 * @internal
2094 */
2095export function notImplemented(): never {
2096    throw new Error("Not implemented");
2097}
2098
2099/** @internal */
2100export function memoize<T>(callback: () => T): () => T {
2101    let value: T;
2102    return () => {
2103        if (callback) {
2104            value = callback();
2105            callback = undefined!;
2106        }
2107        return value;
2108    };
2109}
2110
2111/** A version of `memoize` that supports a single primitive argument
2112 *
2113 * @internal
2114 */
2115export function memoizeOne<A extends string | number | boolean | undefined, T>(callback: (arg: A) => T): (arg: A) => T {
2116    const map = new Map<string, T>();
2117    return (arg: A) => {
2118        const key = `${typeof arg}:${arg}`;
2119        let value = map.get(key);
2120        if (value === undefined && !map.has(key)) {
2121            value = callback(arg);
2122            map.set(key, value);
2123        }
2124        return value!;
2125    };
2126}
2127
2128/**
2129 * High-order function, composes functions. Note that functions are composed inside-out;
2130 * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
2131 *
2132 * @param args The functions to compose.
2133 *
2134 * @internal
2135 */
2136export function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
2137/** @internal */
2138export function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
2139    if (!!e) {
2140        const args: ((t: T) => T)[] = [];
2141        for (let i = 0; i < arguments.length; i++) {
2142            args[i] = arguments[i];
2143        }
2144
2145        return t => reduceLeft(args, (u, f) => f(u), t);
2146    }
2147    else if (d) {
2148        return t => d(c(b(a(t))));
2149    }
2150    else if (c) {
2151        return t => c(b(a(t)));
2152    }
2153    else if (b) {
2154        return t => b(a(t));
2155    }
2156    else if (a) {
2157        return t => a(t);
2158    }
2159    else {
2160        return t => t;
2161    }
2162}
2163
2164/** @internal */
2165export const enum AssertionLevel {
2166    None = 0,
2167    Normal = 1,
2168    Aggressive = 2,
2169    VeryAggressive = 3,
2170}
2171
2172/**
2173 * Safer version of `Function` which should not be called.
2174 * Every function should be assignable to this, but this should not be assignable to every function.
2175 *
2176 * @internal
2177 */
2178export type AnyFunction = (...args: never[]) => void;
2179/** @internal */
2180export type AnyConstructor = new (...args: unknown[]) => unknown;
2181
2182/** @internal */
2183export function equateValues<T>(a: T, b: T) {
2184    return a === b;
2185}
2186
2187/**
2188 * Compare the equality of two strings using a case-sensitive ordinal comparison.
2189 *
2190 * Case-sensitive comparisons compare both strings one code-point at a time using the integer
2191 * value of each code-point after applying `toUpperCase` to each string. We always map both
2192 * strings to their upper-case form as some unicode characters do not properly round-trip to
2193 * lowercase (such as `ẞ` (German sharp capital s)).
2194 *
2195 * @internal
2196 */
2197export function equateStringsCaseInsensitive(a: string, b: string) {
2198    return a === b
2199        || a !== undefined
2200        && b !== undefined
2201        && a.toUpperCase() === b.toUpperCase();
2202}
2203
2204/**
2205 * Compare the equality of two strings using a case-sensitive ordinal comparison.
2206 *
2207 * Case-sensitive comparisons compare both strings one code-point at a time using the
2208 * integer value of each code-point.
2209 *
2210 * @internal
2211 */
2212export function equateStringsCaseSensitive(a: string, b: string) {
2213    return equateValues(a, b);
2214}
2215
2216function compareComparableValues(a: string | undefined, b: string | undefined): Comparison;
2217function compareComparableValues(a: number | undefined, b: number | undefined): Comparison;
2218function compareComparableValues(a: string | number | undefined, b: string | number | undefined) {
2219    return a === b ? Comparison.EqualTo :
2220        a === undefined ? Comparison.LessThan :
2221        b === undefined ? Comparison.GreaterThan :
2222        a < b ? Comparison.LessThan :
2223        Comparison.GreaterThan;
2224}
2225
2226/**
2227 * Compare two numeric values for their order relative to each other.
2228 * To compare strings, use any of the `compareStrings` functions.
2229 *
2230 * @internal
2231 */
2232export function compareValues(a: number | undefined, b: number | undefined): Comparison {
2233    return compareComparableValues(a, b);
2234}
2235
2236/**
2237 * Compare two TextSpans, first by `start`, then by `length`.
2238 *
2239 * @internal
2240 */
2241export function compareTextSpans(a: Partial<TextSpan> | undefined, b: Partial<TextSpan> | undefined): Comparison {
2242    return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length);
2243}
2244
2245/** @internal */
2246export function min<T>(items: readonly [T, ...T[]], compare: Comparer<T>): T;
2247/** @internal */
2248export function min<T>(items: readonly T[], compare: Comparer<T>): T | undefined;
2249/** @internal */
2250export function min<T>(items: readonly T[], compare: Comparer<T>): T | undefined {
2251    return reduceLeft(items, (x, y) => compare(x, y) === Comparison.LessThan ? x : y);
2252}
2253
2254/**
2255 * Compare two strings using a case-insensitive ordinal comparison.
2256 *
2257 * Ordinal comparisons are based on the difference between the unicode code points of both
2258 * strings. Characters with multiple unicode representations are considered unequal. Ordinal
2259 * comparisons provide predictable ordering, but place "a" after "B".
2260 *
2261 * Case-insensitive comparisons compare both strings one code-point at a time using the integer
2262 * value of each code-point after applying `toUpperCase` to each string. We always map both
2263 * strings to their upper-case form as some unicode characters do not properly round-trip to
2264 * lowercase (such as `ẞ` (German sharp capital s)).
2265 *
2266 * @internal
2267 */
2268export function compareStringsCaseInsensitive(a: string, b: string) {
2269    if (a === b) return Comparison.EqualTo;
2270    if (a === undefined) return Comparison.LessThan;
2271    if (b === undefined) return Comparison.GreaterThan;
2272    a = a.toUpperCase();
2273    b = b.toUpperCase();
2274    return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
2275}
2276
2277/**
2278 * Compare two strings using a case-sensitive ordinal comparison.
2279 *
2280 * Ordinal comparisons are based on the difference between the unicode code points of both
2281 * strings. Characters with multiple unicode representations are considered unequal. Ordinal
2282 * comparisons provide predictable ordering, but place "a" after "B".
2283 *
2284 * Case-sensitive comparisons compare both strings one code-point at a time using the integer
2285 * value of each code-point.
2286 *
2287 *  @internal
2288 */
2289export function compareStringsCaseSensitive(a: string | undefined, b: string | undefined): Comparison {
2290    return compareComparableValues(a, b);
2291}
2292
2293/** @internal */
2294export function getStringComparer(ignoreCase?: boolean) {
2295    return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
2296}
2297
2298/**
2299 * Creates a string comparer for use with string collation in the UI.
2300 */
2301const createUIStringComparer = (() => {
2302    let defaultComparer: Comparer<string> | undefined;
2303    let enUSComparer: Comparer<string> | undefined;
2304
2305    const stringComparerFactory = getStringComparerFactory();
2306    return createStringComparer;
2307
2308    function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) {
2309        if (a === b) return Comparison.EqualTo;
2310        if (a === undefined) return Comparison.LessThan;
2311        if (b === undefined) return Comparison.GreaterThan;
2312        const value = comparer(a, b);
2313        return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
2314    }
2315
2316    function createIntlCollatorStringComparer(locale: string | undefined): Comparer<string> {
2317        // Intl.Collator.prototype.compare is bound to the collator. See NOTE in
2318        // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
2319        const comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" }).compare;
2320        return (a, b) => compareWithCallback(a, b, comparer);
2321    }
2322
2323    function createLocaleCompareStringComparer(locale: string | undefined): Comparer<string> {
2324        // if the locale is not the default locale (`undefined`), use the fallback comparer.
2325        if (locale !== undefined) return createFallbackStringComparer();
2326
2327        return (a, b) => compareWithCallback(a, b, compareStrings);
2328
2329        function compareStrings(a: string, b: string) {
2330            return a.localeCompare(b);
2331        }
2332    }
2333
2334    function createFallbackStringComparer(): Comparer<string> {
2335        // An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b".
2336        // We first sort case insensitively.  So "Aaa" will come before "baa".
2337        // Then we sort case sensitively, so "aaa" will come before "Aaa".
2338        //
2339        // For case insensitive comparisons we always map both strings to their
2340        // upper-case form as some unicode characters do not properly round-trip to
2341        // lowercase (such as `ẞ` (German sharp capital s)).
2342        return (a, b) => compareWithCallback(a, b, compareDictionaryOrder);
2343
2344        function compareDictionaryOrder(a: string, b: string) {
2345            return compareStrings(a.toUpperCase(), b.toUpperCase()) || compareStrings(a, b);
2346        }
2347
2348        function compareStrings(a: string, b: string) {
2349            return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
2350        }
2351    }
2352
2353    function getStringComparerFactory() {
2354        // If the host supports Intl, we use it for comparisons using the default locale.
2355        if (typeof Intl === "object" && typeof Intl.Collator === "function") {
2356            return createIntlCollatorStringComparer;
2357        }
2358
2359        // If the host does not support Intl, we fall back to localeCompare.
2360        // localeCompare in Node v0.10 is just an ordinal comparison, so don't use it.
2361        if (typeof String.prototype.localeCompare === "function" &&
2362            typeof String.prototype.toLocaleUpperCase === "function" &&
2363            "a".localeCompare("B") < 0) {
2364            return createLocaleCompareStringComparer;
2365        }
2366
2367        // Otherwise, fall back to ordinal comparison:
2368        return createFallbackStringComparer;
2369    }
2370
2371    function createStringComparer(locale: string | undefined) {
2372        // Hold onto common string comparers. This avoids constantly reallocating comparers during
2373        // tests.
2374        if (locale === undefined) {
2375            return defaultComparer || (defaultComparer = stringComparerFactory(locale));
2376        }
2377        else if (locale === "en-US") {
2378            return enUSComparer || (enUSComparer = stringComparerFactory(locale));
2379        }
2380        else {
2381            return stringComparerFactory(locale);
2382        }
2383    }
2384})();
2385
2386let uiComparerCaseSensitive: Comparer<string> | undefined;
2387let uiLocale: string | undefined;
2388
2389/** @internal */
2390export function getUILocale() {
2391    return uiLocale;
2392}
2393
2394/** @internal */
2395export function setUILocale(value: string | undefined) {
2396    if (uiLocale !== value) {
2397        uiLocale = value;
2398        uiComparerCaseSensitive = undefined;
2399    }
2400}
2401
2402/**
2403 * Compare two strings in a using the case-sensitive sort behavior of the UI locale.
2404 *
2405 * Ordering is not predictable between different host locales, but is best for displaying
2406 * ordered data for UI presentation. Characters with multiple unicode representations may
2407 * be considered equal.
2408 *
2409 * Case-sensitive comparisons compare strings that differ in base characters, or
2410 * accents/diacritic marks, or case as unequal.
2411 *
2412 * @internal
2413 */
2414export function compareStringsCaseSensitiveUI(a: string, b: string) {
2415    const comparer = uiComparerCaseSensitive || (uiComparerCaseSensitive = createUIStringComparer(uiLocale));
2416    return comparer(a, b);
2417}
2418
2419/** @internal */
2420export function compareProperties<T extends object, K extends keyof T>(a: T | undefined, b: T | undefined, key: K, comparer: Comparer<T[K]>): Comparison {
2421    return a === b ? Comparison.EqualTo :
2422        a === undefined ? Comparison.LessThan :
2423        b === undefined ? Comparison.GreaterThan :
2424        comparer(a[key], b[key]);
2425}
2426
2427/** True is greater than false.
2428 *
2429 * @internal
2430 */
2431export function compareBooleans(a: boolean, b: boolean): Comparison {
2432    return compareValues(a ? 1 : 0, b ? 1 : 0);
2433}
2434
2435/**
2436 * Given a name and a list of names that are *not* equal to the name, return a spelling suggestion if there is one that is close enough.
2437 * Names less than length 3 only check for case-insensitive equality.
2438 *
2439 * find the candidate with the smallest Levenshtein distance,
2440 *    except for candidates:
2441 *      * With no name
2442 *      * Whose length differs from the target name by more than 0.34 of the length of the name.
2443 *      * Whose levenshtein distance is more than 0.4 of the length of the name
2444 *        (0.4 allows 1 substitution/transposition for every 5 characters,
2445 *         and 1 insertion/deletion at 3 characters)
2446 *
2447 * @internal
2448 */
2449export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined {
2450    const maximumLengthDifference = Math.max(2, Math.floor(name.length * 0.34));
2451    let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother.
2452    let bestCandidate: T | undefined;
2453    for (const candidate of candidates) {
2454        const candidateName = getName(candidate);
2455        if (candidateName !== undefined && Math.abs(candidateName.length - name.length) <= maximumLengthDifference) {
2456            if (candidateName === name) {
2457                continue;
2458            }
2459            // Only consider candidates less than 3 characters long when they differ by case.
2460            // Otherwise, don't bother, since a user would usually notice differences of a 2-character name.
2461            if (candidateName.length < 3 && candidateName.toLowerCase() !== name.toLowerCase()) {
2462                continue;
2463            }
2464
2465            const distance = levenshteinWithMax(name, candidateName, bestDistance - 0.1);
2466            if (distance === undefined) {
2467                continue;
2468            }
2469
2470            Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined
2471            bestDistance = distance;
2472            bestCandidate = candidate;
2473        }
2474    }
2475    return bestCandidate;
2476}
2477
2478function levenshteinWithMax(s1: string, s2: string, max: number): number | undefined {
2479    let previous = new Array(s2.length + 1);
2480    let current = new Array(s2.length + 1);
2481    /** Represents any value > max. We don't care about the particular value. */
2482    const big = max + 0.01;
2483
2484    for (let i = 0; i <= s2.length; i++) {
2485        previous[i] = i;
2486    }
2487
2488    for (let i = 1; i <= s1.length; i++) {
2489        const c1 = s1.charCodeAt(i - 1);
2490        const minJ = Math.ceil(i > max ? i - max : 1);
2491        const maxJ = Math.floor(s2.length > max + i ? max + i : s2.length);
2492        current[0] = i;
2493        /** Smallest value of the matrix in the ith column. */
2494        let colMin = i;
2495        for (let j = 1; j < minJ; j++) {
2496            current[j] = big;
2497        }
2498        for (let j = minJ; j <= maxJ; j++) {
2499            // case difference should be significantly cheaper than other differences
2500            const substitutionDistance = s1[i - 1].toLowerCase() === s2[j-1].toLowerCase()
2501                ? (previous[j - 1] + 0.1)
2502                : (previous[j - 1] + 2);
2503            const dist = c1 === s2.charCodeAt(j - 1)
2504                ? previous[j - 1]
2505                : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ substitutionDistance);
2506            current[j] = dist;
2507            colMin = Math.min(colMin, dist);
2508        }
2509        for (let j = maxJ + 1; j <= s2.length; j++) {
2510            current[j] = big;
2511        }
2512        if (colMin > max) {
2513            // Give up -- everything in this column is > max and it can't get better in future columns.
2514            return undefined;
2515        }
2516
2517        const temp = previous;
2518        previous = current;
2519        current = temp;
2520    }
2521
2522    const res = previous[s2.length];
2523    return res > max ? undefined : res;
2524}
2525
2526/** @internal */
2527export function endsWith(str: string, suffix: string): boolean {
2528    const expectedPos = str.length - suffix.length;
2529    return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
2530}
2531
2532/** @internal */
2533export function removeSuffix(str: string, suffix: string): string {
2534    return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : str;
2535}
2536
2537/** @internal */
2538export function tryRemoveSuffix(str: string, suffix: string): string | undefined {
2539    return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : undefined;
2540}
2541
2542/** @internal */
2543export function stringContains(str: string, substring: string): boolean {
2544    return str.indexOf(substring) !== -1;
2545}
2546
2547/**
2548 * Takes a string like "jquery-min.4.2.3" and returns "jquery"
2549 *
2550 * @internal
2551 */
2552export function removeMinAndVersionNumbers(fileName: string) {
2553    // We used to use the regex /[.-]((min)|(\d+(\.\d+)*))$/ and would just .replace it twice.
2554    // Unfortunately, that regex has O(n^2) performance because v8 doesn't match from the end of the string.
2555    // Instead, we now essentially scan the filename (backwards) ourselves.
2556
2557    let end: number = fileName.length;
2558
2559    for (let pos = end - 1; pos > 0; pos--) {
2560        let ch: number = fileName.charCodeAt(pos);
2561        if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
2562            // Match a \d+ segment
2563            do {
2564                --pos;
2565                ch = fileName.charCodeAt(pos);
2566            } while (pos > 0 && ch >= CharacterCodes._0 && ch <= CharacterCodes._9);
2567        }
2568        else if (pos > 4 && (ch === CharacterCodes.n || ch === CharacterCodes.N)) {
2569            // Looking for "min" or "min"
2570            // Already matched the 'n'
2571            --pos;
2572            ch = fileName.charCodeAt(pos);
2573            if (ch !== CharacterCodes.i && ch !== CharacterCodes.I) {
2574                break;
2575            }
2576            --pos;
2577            ch = fileName.charCodeAt(pos);
2578            if (ch !== CharacterCodes.m && ch !== CharacterCodes.M) {
2579                break;
2580            }
2581            --pos;
2582            ch = fileName.charCodeAt(pos);
2583        }
2584        else {
2585            // This character is not part of either suffix pattern
2586            break;
2587        }
2588
2589        if (ch !== CharacterCodes.minus && ch !== CharacterCodes.dot) {
2590            break;
2591        }
2592
2593        end = pos;
2594    }
2595
2596    // end might be fileName.length, in which case this should internally no-op
2597    return end === fileName.length ? fileName : fileName.slice(0, end);
2598}
2599
2600/** Remove an item from an array, moving everything to its right one space left.
2601 *
2602 * @internal
2603 */
2604export function orderedRemoveItem<T>(array: T[], item: T): boolean {
2605    for (let i = 0; i < array.length; i++) {
2606        if (array[i] === item) {
2607            orderedRemoveItemAt(array, i);
2608            return true;
2609        }
2610    }
2611    return false;
2612}
2613
2614/** Remove an item by index from an array, moving everything to its right one space left.
2615 *
2616 * @internal
2617 */
2618export function orderedRemoveItemAt<T>(array: T[], index: number): void {
2619    // This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`.
2620    for (let i = index; i < array.length - 1; i++) {
2621        array[i] = array[i + 1];
2622    }
2623    array.pop();
2624}
2625
2626/** @internal */
2627export function unorderedRemoveItemAt<T>(array: T[], index: number): void {
2628    // Fill in the "hole" left at `index`.
2629    array[index] = array[array.length - 1];
2630    array.pop();
2631}
2632
2633/** Remove the *first* occurrence of `item` from the array.
2634 *
2635 * @internal
2636 */
2637export function unorderedRemoveItem<T>(array: T[], item: T) {
2638    return unorderedRemoveFirstItemWhere(array, element => element === item);
2639}
2640
2641/** Remove the *first* element satisfying `predicate`. */
2642function unorderedRemoveFirstItemWhere<T>(array: T[], predicate: (element: T) => boolean) {
2643    for (let i = 0; i < array.length; i++) {
2644        if (predicate(array[i])) {
2645            unorderedRemoveItemAt(array, i);
2646            return true;
2647        }
2648    }
2649    return false;
2650}
2651
2652/** @internal */
2653export type GetCanonicalFileName = (fileName: string) => string;
2654/** @internal */
2655export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): GetCanonicalFileName {
2656    return useCaseSensitiveFileNames ? identity : toFileNameLowerCase;
2657}
2658
2659/** Represents a "prefix*suffix" pattern.
2660 *
2661 * @internal
2662 */
2663export interface Pattern {
2664    prefix: string;
2665    suffix: string;
2666}
2667
2668/** @internal */
2669export function patternText({ prefix, suffix }: Pattern): string {
2670    return `${prefix}*${suffix}`;
2671}
2672
2673/**
2674 * Given that candidate matches pattern, returns the text matching the '*'.
2675 * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
2676 *
2677 * @internal
2678 */
2679export function matchedText(pattern: Pattern, candidate: string): string {
2680    Debug.assert(isPatternMatch(pattern, candidate));
2681    return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length);
2682}
2683
2684/** Return the object corresponding to the best pattern to match `candidate`.
2685 *
2686 * @internal
2687 */
2688export function findBestPatternMatch<T>(values: readonly T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
2689    let matchedValue: T | undefined;
2690    // use length of prefix as betterness criteria
2691    let longestMatchPrefixLength = -1;
2692
2693    for (const v of values) {
2694        const pattern = getPattern(v);
2695        if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
2696            longestMatchPrefixLength = pattern.prefix.length;
2697            matchedValue = v;
2698        }
2699    }
2700
2701    return matchedValue;
2702}
2703
2704/** @internal */
2705export function startsWith(str: string, prefix: string): boolean {
2706    return str.lastIndexOf(prefix, 0) === 0;
2707}
2708
2709/** @internal */
2710export function removePrefix(str: string, prefix: string): string {
2711    return startsWith(str, prefix) ? str.substr(prefix.length) : str;
2712}
2713
2714/** @internal */
2715export function tryRemovePrefix(str: string, prefix: string, getCanonicalFileName: GetCanonicalFileName = identity): string | undefined {
2716    return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix)) ? str.substring(prefix.length) : undefined;
2717}
2718
2719/** @internal */
2720export function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) {
2721    return candidate.length >= prefix.length + suffix.length &&
2722        startsWith(candidate, prefix) &&
2723        endsWith(candidate, suffix);
2724}
2725
2726/** @internal */
2727export function and<T>(f: (arg: T) => boolean, g: (arg: T) => boolean) {
2728    return (arg: T) => f(arg) && g(arg);
2729}
2730
2731/** @internal */
2732export function or<P, R1 extends P, R2 extends P>(f1: (p1: P) => p1 is R1, f2: (p2: P) => p2 is R2): (p: P) => p is R1 | R2;
2733/** @internal */
2734export function or<P, R1 extends P, R2 extends P, R3 extends P>(f1: (p1: P) => p1 is R1, f2: (p2: P) => p2 is R2, f3: (p3: P) => p3 is R3): (p: P) => p is R1 | R2 | R3;
2735/** @internal */
2736export function or<T extends unknown[], U>(...fs: ((...args: T) => U)[]): (...args: T) => U;
2737/** @internal */
2738export function or<T extends unknown[], U>(...fs: ((...args: T) => U)[]): (...args: T) => U {
2739    return (...args) => {
2740        let lastResult: U;
2741        for (const f of fs) {
2742            lastResult = f(...args);
2743            if (lastResult) {
2744                return lastResult;
2745            }
2746        }
2747        return lastResult!;
2748    };
2749}
2750
2751/** @internal */
2752export function not<T extends unknown[]>(fn: (...args: T) => boolean): (...args: T) => boolean {
2753    return (...args) => !fn(...args);
2754}
2755
2756/** @internal */
2757export function assertType<T>(_: T): void { }
2758
2759/** @internal */
2760export function singleElementArray<T>(t: T | undefined): T[] | undefined {
2761    return t === undefined ? undefined : [t];
2762}
2763
2764/** @internal */
2765export function enumerateInsertsAndDeletes<T, U>(newItems: readonly T[], oldItems: readonly U[], comparer: (a: T, b: U) => Comparison, inserted: (newItem: T) => void, deleted: (oldItem: U) => void, unchanged?: (oldItem: U, newItem: T) => void) {
2766    unchanged = unchanged || noop;
2767    let newIndex = 0;
2768    let oldIndex = 0;
2769    const newLen = newItems.length;
2770    const oldLen = oldItems.length;
2771    let hasChanges = false;
2772    while (newIndex < newLen && oldIndex < oldLen) {
2773        const newItem = newItems[newIndex];
2774        const oldItem = oldItems[oldIndex];
2775        const compareResult = comparer(newItem, oldItem);
2776        if (compareResult === Comparison.LessThan) {
2777            inserted(newItem);
2778            newIndex++;
2779            hasChanges = true;
2780        }
2781        else if (compareResult === Comparison.GreaterThan) {
2782            deleted(oldItem);
2783            oldIndex++;
2784            hasChanges = true;
2785        }
2786        else {
2787            unchanged(oldItem, newItem);
2788            newIndex++;
2789            oldIndex++;
2790        }
2791    }
2792    while (newIndex < newLen) {
2793        inserted(newItems[newIndex++]);
2794        hasChanges = true;
2795    }
2796    while (oldIndex < oldLen) {
2797        deleted(oldItems[oldIndex++]);
2798        hasChanges = true;
2799    }
2800    return hasChanges;
2801}
2802
2803/** @internal */
2804export function fill<T>(length: number, cb: (index: number) => T): T[] {
2805    const result = Array<T>(length);
2806    for (let i = 0; i < length; i++) {
2807        result[i] = cb(i);
2808    }
2809    return result;
2810}
2811
2812/** @internal */
2813export function cartesianProduct<T>(arrays: readonly T[][]) {
2814    const result: T[][] = [];
2815    cartesianProductWorker(arrays, result, /*outer*/ undefined, 0);
2816    return result;
2817}
2818
2819function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) {
2820    for (const element of arrays[index]) {
2821        let inner: T[];
2822        if (outer) {
2823            inner = outer.slice();
2824            inner.push(element);
2825        }
2826        else {
2827            inner = [element];
2828        }
2829        if (index === arrays.length - 1) {
2830            result.push(inner);
2831        }
2832        else {
2833            cartesianProductWorker(arrays, result, inner, index + 1);
2834        }
2835    }
2836}
2837
2838
2839/**
2840 * Returns string left-padded with spaces or zeros until it reaches the given length.
2841 *
2842 * @param s String to pad.
2843 * @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged.
2844 * @param padString Character to use as padding (default " ").
2845 *
2846 * @internal
2847 */
2848export function padLeft(s: string, length: number, padString: " " | "0" = " ") {
2849    return length <= s.length ? s : padString.repeat(length - s.length) + s;
2850}
2851
2852/**
2853 * Returns string right-padded with spaces until it reaches the given length.
2854 *
2855 * @param s String to pad.
2856 * @param length Final padded length. If less than or equal to 's.length', returns 's' unchanged.
2857 * @param padString Character to use as padding (default " ").
2858 *
2859 * @internal
2860 */
2861export function padRight(s: string, length: number, padString: " " = " ") {
2862    return length <= s.length ? s : s + padString.repeat(length - s.length);
2863}
2864
2865/** @internal */
2866export function takeWhile<T, U extends T>(array: readonly T[], predicate: (element: T) => element is U): U[];
2867/** @internal */
2868export function takeWhile<T>(array: readonly T[], predicate: (element: T) => boolean): T[] {
2869    const len = array.length;
2870    let index = 0;
2871    while (index < len && predicate(array[index])) {
2872        index++;
2873    }
2874    return array.slice(0, index);
2875}
2876
2877/**
2878 * Removes the leading and trailing white space and line terminator characters from a string.
2879 *
2880 * @internal
2881 */
2882export const trimString = !!String.prototype.trim ? ((s: string) => s.trim()) : (s: string) => trimStringEnd(trimStringStart(s));
2883
2884/**
2885 * Returns a copy with trailing whitespace removed.
2886 *
2887 * @internal
2888 */
2889export const trimStringEnd = !!String.prototype.trimEnd ? ((s: string) => s.trimEnd()) : trimEndImpl;
2890
2891/**
2892 * Returns a copy with leading whitespace removed.
2893 *
2894 * @internal
2895 */
2896export const trimStringStart = !!String.prototype.trimStart ? ((s: string) => s.trimStart()) : (s: string) => s.replace(/^\s+/g, "");
2897
2898/**
2899 * https://jsbench.me/gjkoxld4au/1
2900 * The simple regex for this, /\s+$/g is O(n^2) in v8.
2901 * The native .trimEnd method is by far best, but since that's technically ES2019,
2902 * we provide a (still much faster than the simple regex) fallback.
2903 */
2904function trimEndImpl(s: string) {
2905    let end = s.length - 1;
2906    while (end >= 0) {
2907        if (!isWhiteSpaceLike(s.charCodeAt(end))) break;
2908        end--;
2909    }
2910    return s.slice(0, end + 1);
2911}
2912
2913declare const process: any;
2914
2915/** @internal */
2916export function isNodeLikeSystem(): boolean {
2917    // This is defined here rather than in sys.ts to prevent a cycle from its
2918    // use in performanceCore.ts.
2919    return typeof process !== "undefined"
2920        && process.nextTick
2921        && !process.browser
2922        && typeof require !== "undefined";
2923}
2924