diff --git a/package-lock.json b/package-lock.json index 709bccd75..1779557fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "extra-array", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "extra-array", - "version": "4.0.2", + "version": "4.0.3", "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "^24.1.0", diff --git a/package.json b/package.json index 0260fc9dc..51019955e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "extra-array", - "version": "4.0.2", + "version": "4.0.3", "description": "An array is a collection of values, stored contiguously.", "main": "index.js", "module": "index.mjs", diff --git a/src/index.ts b/src/index.ts index 9b79fb9ca..5e532c8a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -130,28 +130,6 @@ function toSet(x: T[], fm: MapFunction | null=null): Set { // #region GENERATE // ---------------- -/** - * Convert an iterable to array. - * @param x an iterable - * @returns x as array - */ -export function fromIterable(x: Iterable): T[] { - return [...x]; -} -export {fromIterable as from}; - - -/** - * Convert an iterable to array! - * @param x an iterable (updatable if array!) - * @returns x as array - */ -export function fromIterable$(x: Iterable): T[] { - return Array.isArray(x)? x : [...x]; -} -export {fromIterable$ as from$}; - - /** * Generate array from given number range. * @param v start number @@ -197,6 +175,28 @@ export function fromApplication(fm: MapFunction, v: T, n: number): T[] return a; } export {fromApplication as fromApply}; + + +/** + * Convert an iterable to array. + * @param x an iterable + * @returns x as array + */ +export function fromIterable(x: Iterable): T[] { + return [...x]; +} +export {fromIterable as from}; + + +/** + * Convert an iterable to array! + * @param x an iterable (updatable if array!) + * @returns x as array + */ +export function fromIterable$(x: Iterable): T[] { + return Array.isArray(x)? x : [...x]; +} +export {fromIterable$ as from$}; // #endregion @@ -242,18 +242,6 @@ export function is(v: any): v is any[] { } -/** - * Examine if array is sorted. - * @param x an array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns x is sorted? - */ -export function isSorted(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { - return searchUnsortedValue(x, fc, fm) === -1; -} - - /** * Obtain all indices. * @param x an array @@ -317,8 +305,8 @@ export function ientries(x: T[]): IEntries { -// #region LENGTH -// -------------- +// #region INDEX +// ------------- /** * Get zero-based index for an element in array. @@ -345,6 +333,22 @@ export function indexRange(x: T[], i: number=0, I: number=x.length): [number, var I = I>=0? Math.min(I, X) : Math.max(X+I, 0); return [i, Math.max(i, I)]; } +// #endregion + + + + +// #region LENGTH +// -------------- + +/** + * Check if an array is empty. + * @param x an array + * @returns |x| = 0? + */ +export function isEmpty(x: T[]): boolean { + return x.length===0; +} /** @@ -361,16 +365,6 @@ export function length(x: T[], i: number=0, I: number=x.length): number { export {length as size}; -/** - * Check if an array is empty. - * @param x an array - * @returns |x| = 0? - */ -export function isEmpty(x: T[]): boolean { - return x.length===0; -} - - /** * Resize an array to given length! * @param x an array @@ -621,514 +615,851 @@ export function removePath$(x: any[], p: number[]): any[] { -// #region PROPERTY -// ---------------- - -/** - * Count values which satisfy a test. - * @param x an array - * @param ft test function (v, i, x) - * @returns Σtᵢ | tᵢ = 1 if ft(vᵢ) else 0; vᵢ ∈ x - */ -export function count(x: T[], ft: TestFunction): number { - var i = -1, a = 0; - for (var v of x) - if (ft(v, ++i, x)) ++a; - return a; -} - +// #region SORT +// ------------ /** - * Count occurrences of each distinct value. + * Examine if array is sorted. * @param x an array + * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns Map \{value ⇒ count\} + * @returns x is sorted? */ -export function countEach(x: T[], fm: MapFunction | null=null): Map { - var fm = fm || IDENTITY; - var i = -1, a = new Map(); - for (var v of x) { - var w = fm(v, ++i, x); - a.set(w, (a.get(w) || 0) + 1); - } - return a; +export function isSorted(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + return searchUnsortedValue(x, fc, fm) === -1; } -export {countEach as countAs}; // DEPRECATED /** - * Find first smallest value. + * Examine if array has an unsorted value. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns v | v ≤ vᵢ; vᵢ ∈ x + * @returns x is not sorted? */ -export function minimum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { - var i = searchMinimumValue(x, fc, fm); - return x[i]; +export function hasUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + return searchUnsortedValue(x, fc, fm) >= 0; } -export {minimum as min}; /** - * Find first smallest entry. + * Find first index of an unsorted value. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns [min_index, min_value] + * @returns index of first unsorted value, -1 if sorted */ -export function minimumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { - var i = searchMinimumValue(x, fc, fm); - return [i, x[i]]; +export function searchUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X<=1) return -1; + var w0 = fm(x[0], 0, x); + for (var i=1; i0) return i; + w0 = w; + } + return -1; } -export {minimumEntry as minEntry}; /** - * Find first largest value. + * Arrange values in order. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns v | v ≥ vᵢ; vᵢ ∈ x + * @param fs swap function (x, i, j) + * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j */ -export function maximum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { - var i = searchMaximumValue(x, fc, fm); - return x[i]; +export function sort(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + return sort$(x.slice(), fc, fm, fs); } -export {maximum as max}; +export {sort as toSorted}; /** - * Find first largest entry. - * @param x an array + * Arrange values in order! + * @param x an array (updated!) * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns [max_index, max_value] + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function maximumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { - var i = searchMaximumValue(x, fc, fm); - return [i, x[i]]; +export function sort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + var fc = fc || COMPARE; + if (!fm) return x.sort(fc); + var X = x.length; + var fm = fm || IDENTITY; + var fs = fs || swapRaw$; + return partialIntroSort$(x, 0, X, X, fc, fm, fs); } -export {maximumEntry as maxEntry}; /** - * Find smallest and largest values. + * Partially arrange values in order. * @param x an array + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns [min_value, max_value] + * @param fs swap function (x, i, j) + * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j */ -export function range(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [T, T] { - var [a, b] = rangeEntries(x, fc, fm); - return [a[1], b[1]]; +export function partialSort(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + return partialSort$(x.slice(), i, I, n, fc, fm, fs); } /** - * Find smallest and largest entries. - * @param x an array + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns [min_entry, max_entry] + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function rangeEntries(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [[number, T], [number, T]] { +export function partialSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { var fc = fc || COMPARE; var fm = fm || IDENTITY; - var X = x.length; - if (X===0) return [[-1, undefined], [-1, undefined]]; - var v = x[0], w = fm(v, 0, x); - var mi = 0, mv = v, mw = w; - var ni = 0, nv = v, nw = w; - for (var i=1; i0) { ni = i; nv = v; nw = w; } - } - return [[mi, mv], [ni, nv]]; + var fs = fs || swapRaw$; + // TODO: Check various sort functions. + return partialIntroSort$(x, i, I, n, fc, fm, fs); } /** - * Find smallest values. - * @param x an array - * @param n number of values + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns n smallest values in ascending order + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function minimums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { - var is = searchMinimumValues(x, n, fc, fm); - return is.map(i => x[i]); +function partialIntroSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + var d = Math.floor(Math.log2(I-i)*2); // Maximum depth of recursion + var s = 16; // When to switch to insertion sort + return partialIntroSortHelper$(x, i, I, d, s, n, fc, fm, fs); +} + + +// Partially arrange values in order with hybrid quick sort, heap sort, and insertion sort. +function partialIntroSortHelper$(x: T[], i: number, I: number, d: number, s: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + if (n<=0 || I-i<=1) return x; // Nothing to sort + if (I-i<=s) return partialInsertionSort$(x, i, I, n, fc, fm, fs); // Insertion sort + if (d<=0) return partialHeapSort$(x, i, I, n, fc, fm, fs); // Heap sort + var p = i + Math.floor((I-i)*Math.random()); // Choose pivot + var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array + partialIntroSortHelper$(x, i, p, d, s, Math.min(p-i, n), fc, fm, fs); // Sort left part + partialIntroSortHelper$(x, p+1, I, d, s, Math.min(I-p-1, n), fc, fm, fs); // Sort right part + return x; } /** - * Find smallest entries. - * @param x an array - * @param n number of values - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns n smallest entries in ascending order - */ -export function minimumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { - var is = searchMinimumValues(x, n, fc, fm); - return is.map(i => [i, x[i]]); -} - - -/** - * Find largest values. - * @param x an array - * @param n number of values + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns n largest values in descending order + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function maximums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { - var is = searchMaximumValues(x, n, fc, fm); - return is.map(i => x[i]); +function partialQuickSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + if (n<=0 || I-i<=1) return x; // Nothing to sort + var p = i + Math.floor((I-i)*Math.random()); // Choose pivot + var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array + partialQuickSort$(x, i, p, Math.min(p-i, n), fc, fm, fs); // Sort left part + partialQuickSort$(x, p+1, I, Math.min(I-p-1, n), fc, fm, fs); // Sort right part + return x; +} + + +// TODO: Make this a generic function. +// Partition the array into two parts, such that values in the first part are less than values in the second part. +function quickSortPartition$(x: T[], i: number, I: number, p: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): number { + var wp = fm(x[p], p, x); // Pivot value + var j = i-1; // Last index of values ≤ pivot + fs(x, p, I-1); // Move pivot to end + for (var k=i; k 0) continue; + fs(x, ++j, k); // Move value ≤ pivot to left + } + fs(x, ++j, I-1); // Move pivot to middle + return j; // Return pivot index } /** - * Find largest entries. - * @param x an array - * @param n number of values + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns n largest entries in descending order + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function maximumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { - var is = searchMaximumValues(x, n, fc, fm); - return is.map(i => [i, x[i]]); +function partialHeapSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + buildReverseMinHeap$(x, i, I, fc, fm, fs); + for (var r=I-1; n>0 && i(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + for (var r=I-Math.floor((I-i)/2); r(x: T[], vd?: T): T { - return x.length>0? x[0] : vd; +function reverseMinHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + var s = r; // Index of smallest value + var lt = 2*r - I; // Left child, reverse of lt = 2*r+1 + var rt = lt - 1; // Right child, reverse of rt = 2*r+2 + if (lt>=i && fc(fm(x[lt], lt, x), fm(x[s], s, x)) < 0) s = lt; // Left child is smaller? + if (rt>=i && fc(fm(x[rt], rt, x), fm(x[s], s, x)) < 0) s = rt; // Right child is smaller? + if (s !== r) { // Smallest is not root? + fs(x, s, r); // Swap root with smallest + reverseMinHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap + } } -export {head as front}; -export {head as first}; -/** - * Get last value. - * @param x an array - * @param vd default value - * @returns x[|x|-1] || vd - */ -export function last(x: T[], vd?: T): T { - return x.length>0? x[x.length-1] : vd; +// Build a max-heap, where root node is the smallest and placed at the beginning. +function buildMaxHeap$(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + for (var r=i+Math.floor((I-i)/2)-1; r>=i; --r) + maxHeapify$(x, i, I, r, fc, fm, fs); } -export {last as back}; /** - * Get values from middle. - * @param x an array + * Max-heapify values, such that root node is the largest and placed at the beginning. + * @param x an array (updated!) * @param i start index - * @param n number of values [1] - * @returns x[i..i+n] + * @param I end index (exclusive) + * @param r root index + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) */ -export function middle(x: T[], i: number, n: number=1): T[] { - return x.slice(i, i+n); +function maxHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + var s = r; // Index of largest value + var lt = 2*r - i + 1; // Left child, like lt = 2*r+1 + var rt = lt + 1; // Right child, like rt = 2*r+2 + if (lt 0) s = lt; // Left child is larger? + if (rt 0) s = rt; // Right child is larger? + if (s !== r) { // Largest is not root? + fs(x, s, r); // Swap root with largest + maxHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap + } } /** - * Get values except first. - * @param x an array - * @returns x[1..|x|] + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort (ignored) + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function tail(x: T[]): T[] { - return x.slice(1); +function partialInsertionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + // NOTE: Insertion sort does not support partial sorting, so we ignore n. + if (fs===swapRaw$) return partialInsertionSortSwapless$(x, i, I, n, fc, fm, fs); + else return partialInsertionSortSwap$ (x, i, I, n, fc, fm, fs); } -/** - * Get values except last. - * @param x an array - * @returns x[0..|x|-1] - */ -export function init(x: T[]): T[] { - return x.slice(0, -1); +// Sort values in order with swap-enabled version of insertion sort. +function partialInsertionSortSwap$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) + fs(x, k, k+1); + } + return x; } -/** - * Get part of an array. - * @param x an array - * @param i start index [0] - * @param I end index [|x|] - * @returns x[i..I] - */ -export function slice(x: T[], i: number=0, I: number=x.length): T[] { - return x.slice(i, I); +// Sort values in order with swapless version of insertion sort. +function partialInsertionSortSwapless$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) + x[k+1] = x[k]; + x[k+1] = key; + } + return x; } /** - * Get part of an array! + * Partially arrange values in order! * @param x an array (updated!) - * @param i start index [0] - * @param I end index [|x|] - * @returns x = x[i..I] + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j */ -export function slice$(x: T[], i: number=0, I: number=x.length): T[] { - x.copyWithin(0, i, I); - x.length = length(x, i, I); +function partialSelectionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i; n>0 && j 0) { l = k; wl = wk; } + } + fs(x, j, l); + } return x; } +// #endregion + + +// #region PROPERTY +// ---------------- + /** - * Keep first n values only. + * Count values which satisfy a test. * @param x an array - * @param n number of values [1] - * @returns x[0..n] + * @param ft test function (v, i, x) + * @returns Σtᵢ | tᵢ = 1 if ft(vᵢ) else 0; vᵢ ∈ x */ -export function take(x: T[], n: number=1): T[] { - return x.slice(0, n); +export function count(x: T[], ft: TestFunction): number { + var i = -1, a = 0; + for (var v of x) + if (ft(v, ++i, x)) ++a; + return a; } -export {take as left}; /** - * Keep last n values only. + * Count occurrences of each distinct value. * @param x an array - * @param n number of values [1] - * @returns x[0..n] + * @param fm map function (v, i, x) + * @returns Map \{value ⇒ count\} */ -export function takeRight(x: T[], n: number=1): T[] { - return x.slice(x.length-n); +export function countEach(x: T[], fm: MapFunction | null=null): Map { + var fm = fm || IDENTITY; + var i = -1, a = new Map(); + for (var v of x) { + var w = fm(v, ++i, x); + a.set(w, (a.get(w) || 0) + 1); + } + return a; } -export {takeRight as right}; +export {countEach as countAs}; // DEPRECATED /** - * Keep values from left, while a test passes. + * Find first smallest value. * @param x an array - * @param ft test function (v, i, x) - * @returns x[0..T-1] | ft(x[i]) = true ∀ i ∈ [0, T-1] & ft(x[T]) = false + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns v | v ≤ vᵢ; vᵢ ∈ x */ -export function takeWhile(x: T[], ft: TestFunction): T[] { - return x.slice(0, scanWhile(x, ft)); +export function minimum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { + var i = searchMinimumValue(x, fc, fm); + return x[i]; } +export {minimum as min}; /** - * Keep values from right, while a test passes. + * Find first smallest entry. * @param x an array - * @param ft test function (v, i, x) - * @returns x[T..] | ft(x[i]) = true ∀ i ∈ [T, |x|-1] & ft(x[T-1]) = false + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns [min_index, min_value] */ -export function takeWhileRight(x: T[], ft: TestFunction): T[] { - return x.slice(scanWhileRight(x, ft)); +export function minimumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { + var i = searchMinimumValue(x, fc, fm); + return [i, x[i]]; } +export {minimumEntry as minEntry}; /** - * Discard first n values only. + * Find first largest value. * @param x an array - * @param n number of values [1] - * @returns x[n..] + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns v | v ≥ vᵢ; vᵢ ∈ x */ -export function drop(x: T[], n: number=1): T[] { - return x.slice(n); +export function maximum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { + var i = searchMaximumValue(x, fc, fm); + return x[i]; } +export {maximum as max}; /** - * Discard last n values only. + * Find first largest entry. * @param x an array - * @param n number of values [1] - * @returns x[0..-n] + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns [max_index, max_value] */ -export function dropRight(x: T[], n: number=1): T[] { - return x.slice(0, x.length-n); +export function maximumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { + var i = searchMaximumValue(x, fc, fm); + return [i, x[i]]; } +export {maximumEntry as maxEntry}; /** - * Discard values from left, while a test passes. + * Find smallest and largest values. * @param x an array - * @param ft test function (v, i, x) - * @returns x[T..] | ft(x[i]) = true ∀ i ∈ [0, T-1] & ft(x[T]) = false + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns [min_value, max_value] */ -export function dropWhile(x: T[], ft: TestFunction): T[] { - return x.slice(scanWhile(x, ft)); +export function range(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [T, T] { + var [a, b] = rangeEntries(x, fc, fm); + return [a[1], b[1]]; } /** - * Discard values from right, while a test passes. + * Find smallest and largest entries. * @param x an array - * @param ft test function (v, i, x) - * @returns x[0..T-1] | ft(x[i]) = true ∀ i ∈ [T, |x|-1] & ft(x[T-1]) = false + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns [min_entry, max_entry] */ -export function dropWhileRight(x: T[], ft: TestFunction): T[] { - return x.slice(0, scanWhileRight(x, ft)); +export function rangeEntries(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [[number, T], [number, T]] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X===0) return [[-1, undefined], [-1, undefined]]; + var v = x[0], w = fm(v, 0, x); + var mi = 0, mv = v, mw = w; + var ni = 0, nv = v, nw = w; + for (var i=1; i0) { ni = i; nv = v; nw = w; } + } + return [[mi, mv], [ni, nv]]; } -// #endregion - +/** + * Find smallest values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest values in ascending order + */ +export function minimums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => x[i]); +} -// #region ARRANGEMENTS -// -------------------- /** - * Obtain all possible prefixes. + * Find smallest entries. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [[], x[..1], x[..2], ...] if n<0; [x[..n]] otherwise + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest entries in ascending order */ -export function prefixes(x: T[], n: number=-1): T[][] { - return [...iprefixes(x, n)]; +export function minimumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); } /** - * List all possible prefixes. + * Find largest values. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [], x[..1], x[..2], ... if n<0; x[..n] otherwise + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest values in descending order */ -export function* iprefixes(x: T[], n: number=-1): IterableIterator { - var X = x.length; - if (n>X) return; - if (n>=0) { yield x.slice(0, n); return; } - for (var i=0; i<=X; ++i) - yield x.slice(0, i); +export function maximums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => x[i]); } /** - * Obtain all possible suffixes. + * Find largest entries. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [x[0..], x[1..], x[2..], ...] if n<0; [x[-n..]] otherwise + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest entries in descending order */ -export function suffixes(x: T[], n: number=-1): T[][] { - return [...isuffixes(x, n)]; +export function maximumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); } +// #endregion + + +// #region PART +// ------------ + /** - * List all possible suffixes. + * Get first value. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns x[0..], x[1..], x[2..], ... if n<0; x[-n..] otherwise + * @param vd default value + * @returns x[0] || vd */ -export function* isuffixes(x: T[], n: number=-1): IterableIterator { - var X = x.length; - if (n>X) return; - if (n>=0) { yield x.slice(x.length - n); return; } - for (var i=0; i<=X; ++i) - yield x.slice(i); +export function head(x: T[], vd?: T): T { + return x.length>0? x[0] : vd; } +export {head as front}; +export {head as first}; /** - * Obtain all possible infixes. + * Get last value. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [[], x[0..1], x[0..2], ..., x[1..2], ...] if n<0; [only of length n] otherwise + * @param vd default value + * @returns x[|x|-1] || vd */ -export function infixes(x: T[], n: number=-1): T[][] { - return [...iinfixes(x, n)]; +export function last(x: T[], vd?: T): T { + return x.length>0? x[x.length-1] : vd; } +export {last as back}; /** - * List all possible infixes. + * Get values from middle. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [], x[0..1], x[0..2], ..., x[1..2], ... if n<0; only of length n otherwise + * @param i start index + * @param n number of values [1] + * @returns x[i..i+n] */ -export function iinfixes(x: T[], n: number=-1): IterableIterator { - if (n>=0) return infixesFixed(x, n); - else return infixesAll(x); +export function middle(x: T[], i: number, n: number=1): T[] { + return x.slice(i, i+n); } -function* infixesFixed(x: T[], n: number): IterableIterator { - var X = x.length; - if (n>X) return; - if (n===0) { yield []; return; } - for (var i=0, I=X-n+1; i(x: T[]): IterableIterator { - var X = x.length; yield []; - for (var i=0; i(x: T[]): T[] { + return x.slice(1); } /** - * Obtain all possible subsequences. + * Get values except last. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [elements selected by bit from 0..2^|x|] if n<0; [only of length n] otherwise + * @returns x[0..|x|-1] */ -export function subsequences(x: T[], n: number=-1): T[][] { - return [...isubsequences(x, n)]; +export function init(x: T[]): T[] { + return x.slice(0, -1); } /** - * List all possible subsequences. + * Get part of an array. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns elements selected by bit from 0..2^|x| if n<0; only of length n otherwise + * @param i start index [0] + * @param I end index [|x|] + * @returns x[i..I] */ -export function* isubsequences(x: T[], n: number=-1): IterableIterator { - var X = x.length; - if (n>X) return; - if (n===X) { yield x; return; } - if (n===0 || X===0) { yield []; return; } - var y = x.slice(0, -1); - yield* isubsequences(y, n); - for (var s of isubsequences(y, n-1)) { - s.push(x[X-1]); - yield s; - } +export function slice(x: T[], i: number=0, I: number=x.length): T[] { + return x.slice(i, I); } /** - * Obtain all possible permutations. - * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [[], arrangements of length 1, of length 2, ...] if n<0; [only of length n] otherwise + * Get part of an array! + * @param x an array (updated!) + * @param i start index [0] + * @param I end index [|x|] + * @returns x = x[i..I] */ -export function permutations(x: T[], n: number=-1): T[][] { - return [...ipermutations(x, n)]; +export function slice$(x: T[], i: number=0, I: number=x.length): T[] { + x.copyWithin(0, i, I); + x.length = length(x, i, I); + return x; } /** - * List all possible permutations. + * Keep first n values only. * @param x an array - * @param n number of values [-1 ⇒ any] - * @returns [], arrangements of length 1, of length 2, ... if n<0; only of length n otherwise - */ + * @param n number of values [1] + * @returns x[0..n] + */ +export function take(x: T[], n: number=1): T[] { + return x.slice(0, n); +} +export {take as left}; + + +/** + * Keep last n values only. + * @param x an array + * @param n number of values [1] + * @returns x[0..n] + */ +export function takeRight(x: T[], n: number=1): T[] { + return x.slice(x.length-n); +} +export {takeRight as right}; + + +/** + * Keep values from left, while a test passes. + * @param x an array + * @param ft test function (v, i, x) + * @returns x[0..T-1] | ft(x[i]) = true ∀ i ∈ [0, T-1] & ft(x[T]) = false + */ +export function takeWhile(x: T[], ft: TestFunction): T[] { + return x.slice(0, scanWhile(x, ft)); +} + + +/** + * Keep values from right, while a test passes. + * @param x an array + * @param ft test function (v, i, x) + * @returns x[T..] | ft(x[i]) = true ∀ i ∈ [T, |x|-1] & ft(x[T-1]) = false + */ +export function takeWhileRight(x: T[], ft: TestFunction): T[] { + return x.slice(scanWhileRight(x, ft)); +} + + +/** + * Discard first n values only. + * @param x an array + * @param n number of values [1] + * @returns x[n..] + */ +export function drop(x: T[], n: number=1): T[] { + return x.slice(n); +} + + +/** + * Discard last n values only. + * @param x an array + * @param n number of values [1] + * @returns x[0..-n] + */ +export function dropRight(x: T[], n: number=1): T[] { + return x.slice(0, x.length-n); +} + + +/** + * Discard values from left, while a test passes. + * @param x an array + * @param ft test function (v, i, x) + * @returns x[T..] | ft(x[i]) = true ∀ i ∈ [0, T-1] & ft(x[T]) = false + */ +export function dropWhile(x: T[], ft: TestFunction): T[] { + return x.slice(scanWhile(x, ft)); +} + + +/** + * Discard values from right, while a test passes. + * @param x an array + * @param ft test function (v, i, x) + * @returns x[0..T-1] | ft(x[i]) = true ∀ i ∈ [T, |x|-1] & ft(x[T-1]) = false + */ +export function dropWhileRight(x: T[], ft: TestFunction): T[] { + return x.slice(0, scanWhileRight(x, ft)); +} +// #endregion + + + + +// #region ARRANGEMENTS +// -------------------- + +/** + * Obtain all possible prefixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], x[..1], x[..2], ...] if n<0; [x[..n]] otherwise + */ +export function prefixes(x: T[], n: number=-1): T[][] { + return [...iprefixes(x, n)]; +} + + +/** + * List all possible prefixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [], x[..1], x[..2], ... if n<0; x[..n] otherwise + */ +export function* iprefixes(x: T[], n: number=-1): IterableIterator { + var X = x.length; + if (n>X) return; + if (n>=0) { yield x.slice(0, n); return; } + for (var i=0; i<=X; ++i) + yield x.slice(0, i); +} + + +/** + * Obtain all possible suffixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [x[0..], x[1..], x[2..], ...] if n<0; [x[-n..]] otherwise + */ +export function suffixes(x: T[], n: number=-1): T[][] { + return [...isuffixes(x, n)]; +} + + +/** + * List all possible suffixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns x[0..], x[1..], x[2..], ... if n<0; x[-n..] otherwise + */ +export function* isuffixes(x: T[], n: number=-1): IterableIterator { + var X = x.length; + if (n>X) return; + if (n>=0) { yield x.slice(x.length - n); return; } + for (var i=0; i<=X; ++i) + yield x.slice(i); +} + + +/** + * Obtain all possible infixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], x[0..1], x[0..2], ..., x[1..2], ...] if n<0; [only of length n] otherwise + */ +export function infixes(x: T[], n: number=-1): T[][] { + return [...iinfixes(x, n)]; +} + + +/** + * List all possible infixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [], x[0..1], x[0..2], ..., x[1..2], ... if n<0; only of length n otherwise + */ +export function iinfixes(x: T[], n: number=-1): IterableIterator { + if (n>=0) return infixesFixed(x, n); + else return infixesAll(x); +} + +function* infixesFixed(x: T[], n: number): IterableIterator { + var X = x.length; + if (n>X) return; + if (n===0) { yield []; return; } + for (var i=0, I=X-n+1; i(x: T[]): IterableIterator { + var X = x.length; yield []; + for (var i=0; i(x: T[], n: number=-1): T[][] { + return [...isubsequences(x, n)]; +} + + +/** + * List all possible subsequences. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns elements selected by bit from 0..2^|x| if n<0; only of length n otherwise + */ +export function* isubsequences(x: T[], n: number=-1): IterableIterator { + var X = x.length; + if (n>X) return; + if (n===X) { yield x; return; } + if (n===0 || X===0) { yield []; return; } + var y = x.slice(0, -1); + yield* isubsequences(y, n); + for (var s of isubsequences(y, n-1)) { + s.push(x[X-1]); + yield s; + } +} + + +/** + * Obtain all possible permutations. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], arrangements of length 1, of length 2, ...] if n<0; [only of length n] otherwise + */ +export function permutations(x: T[], n: number=-1): T[][] { + return [...ipermutations(x, n)]; +} + + +/** + * List all possible permutations. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [], arrangements of length 1, of length 2, ... if n<0; only of length n otherwise + */ export function* ipermutations(x: T[], n: number=-1): IterableIterator { var X = x.length; if (n>X) return; @@ -1594,28 +1925,6 @@ export function searchMaximumValues(x: T[], n: number, fc: CompareFuncti } -/** - * Find first index of an unsorted value. - * @param x an array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns index of first unsorted value, -1 if sorted - */ -export function searchUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; - var X = x.length; - if (X<=1) return -1; - var w0 = fm(x[0], 0, x); - for (var i=1; i0) return i; - w0 = w; - } - return -1; -} - - /** * Find first index of an adjacent duplicate value. * @param x an array @@ -1764,19 +2073,7 @@ export function hasValue(x: T[], v: T, fc: CompareFunction | null=n /** - * Examine if array has an unsorted value. - * @param x an array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns x is not sorted? - */ -export function hasUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { - return searchUnsortedValue(x, fc, fm) >= 0; -} - - -/** - * Examine if array starts with a prefix. + * Examine if array starts with a prefix. * @param x an array * @param y search prefix * @param fc compare function (a, b) @@ -3185,295 +3482,4 @@ export function cartesianProduct(xs: T[][], fm: MapFunction return a; } // #endregion - - - - -// #region SORT -// ------------ - -/** - * Arrange values in order. - * @param x an array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j - */ -export function sort(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - return sort$(x.slice(), fc, fm, fs); -} -export {sort as toSorted}; - - -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -export function sort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - if (!fm) return x.sort(fc); - var X = x.length; - var fm = fm || IDENTITY; - var fs = fs || swapRaw$; - return partialIntroSort$(x, 0, X, X, fc, fm, fs); -} - - -/** - * Partially arrange values in order. - * @param x an array - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j - */ -export function partialSort(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - return partialSort$(x.slice(), i, I, n, fc, fm, fs); -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -export function partialSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; - var fs = fs || swapRaw$; - // TODO: Check various sort functions. - return partialIntroSort$(x, i, I, n, fc, fm, fs); -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function partialIntroSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - var d = Math.floor(Math.log2(I-i)*2); // Maximum depth of recursion - var s = 16; // When to switch to insertion sort - return partialIntroSortHelper$(x, i, I, d, s, n, fc, fm, fs); -} - - -// Partially arrange values in order with hybrid quick sort, heap sort, and insertion sort. -function partialIntroSortHelper$(x: T[], i: number, I: number, d: number, s: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - if (n<=0 || I-i<=1) return x; // Nothing to sort - if (I-i<=s) return partialInsertionSort$(x, i, I, n, fc, fm, fs); // Insertion sort - if (d<=0) return partialHeapSort$(x, i, I, n, fc, fm, fs); // Heap sort - var p = i + Math.floor((I-i)*Math.random()); // Choose pivot - var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array - partialIntroSortHelper$(x, i, p, d, s, Math.min(p-i, n), fc, fm, fs); // Sort left part - partialIntroSortHelper$(x, p+1, I, d, s, Math.min(I-p-1, n), fc, fm, fs); // Sort right part - return x; -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function partialQuickSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - if (n<=0 || I-i<=1) return x; // Nothing to sort - var p = i + Math.floor((I-i)*Math.random()); // Choose pivot - var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array - partialQuickSort$(x, i, p, Math.min(p-i, n), fc, fm, fs); // Sort left part - partialQuickSort$(x, p+1, I, Math.min(I-p-1, n), fc, fm, fs); // Sort right part - return x; -} - - -// TODO: Make this a generic function. -// Partition the array into two parts, such that values in the first part are less than values in the second part. -function quickSortPartition$(x: T[], i: number, I: number, p: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): number { - var wp = fm(x[p], p, x); // Pivot value - var j = i-1; // Last index of values ≤ pivot - fs(x, p, I-1); // Move pivot to end - for (var k=i; k 0) continue; - fs(x, ++j, k); // Move value ≤ pivot to left - } - fs(x, ++j, I-1); // Move pivot to middle - return j; // Return pivot index -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function partialHeapSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - buildReverseMinHeap$(x, i, I, fc, fm, fs); - for (var r=I-1; n>0 && i(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { - for (var r=I-Math.floor((I-i)/2); r(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { - var s = r; // Index of smallest value - var lt = 2*r - I; // Left child, reverse of lt = 2*r+1 - var rt = lt - 1; // Right child, reverse of rt = 2*r+2 - if (lt>=i && fc(fm(x[lt], lt, x), fm(x[s], s, x)) < 0) s = lt; // Left child is smaller? - if (rt>=i && fc(fm(x[rt], rt, x), fm(x[s], s, x)) < 0) s = rt; // Right child is smaller? - if (s !== r) { // Smallest is not root? - fs(x, s, r); // Swap root with smallest - reverseMinHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap - } -} - - -// Build a max-heap, where root node is the smallest and placed at the beginning. -function buildMaxHeap$(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { - for (var r=i+Math.floor((I-i)/2)-1; r>=i; --r) - maxHeapify$(x, i, I, r, fc, fm, fs); -} - - -/** - * Max-heapify values, such that root node is the largest and placed at the beginning. - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param r root index - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - */ -function maxHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { - var s = r; // Index of largest value - var lt = 2*r - i + 1; // Left child, like lt = 2*r+1 - var rt = lt + 1; // Right child, like rt = 2*r+2 - if (lt 0) s = lt; // Left child is larger? - if (rt 0) s = rt; // Right child is larger? - if (s !== r) { // Largest is not root? - fs(x, s, r); // Swap root with largest - maxHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap - } -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort (ignored) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function partialInsertionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - // NOTE: Insertion sort does not support partial sorting, so we ignore n. - if (fs===swapRaw$) return partialInsertionSortSwapless$(x, i, I, n, fc, fm, fs); - else return partialInsertionSortSwap$ (x, i, I, n, fc, fm, fs); -} - - -// Sort values in order with swap-enabled version of insertion sort. -function partialInsertionSortSwap$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) - fs(x, k, k+1); - } - return x; -} - - -// Sort values in order with swapless version of insertion sort. -function partialInsertionSortSwapless$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) - x[k+1] = x[k]; - x[k+1] = key; - } - return x; -} - - -/** - * Partially arrange values in order! - * @param x an array (updated!) - * @param i start index - * @param I end index (exclusive) - * @param n minimum number of values to sort - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function partialSelectionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { - for (var j=i; n>0 && j 0) { l = k; wl = wk; } - } - fs(x, j, l); - } - return x; -} -// #endregion // #endregion diff --git a/wiki b/wiki index 4de4f82e8..54a4afe88 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 4de4f82e8d0b747148fb76e0d6b69dde850812c1 +Subproject commit 54a4afe880dc57f2f4145dac3db4f33a5ca55495