- Add iterative async stable merge sort (*O(n log n)*). - Replace Quicksort with the new merge sort (kept Quicksort for tables). - Update documentation (reorganize for logical flow, add SWO section with invalid comparator example that really throws). Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
131 lines
3.0 KiB
TypeScript
131 lines
3.0 KiB
TypeScript
import { isPromise } from "./rp.ts";
|
|
|
|
export function evalPromiseValues(vals: any[]): Promise<any[]> | any[] {
|
|
const promises = [];
|
|
const promiseResults = new Array(vals.length);
|
|
for (let i = 0; i < vals.length; i++) {
|
|
if (isPromise(vals[i])) {
|
|
promises.push(
|
|
(vals[i] as Promise<any>).then((v: any) => promiseResults[i] = v),
|
|
);
|
|
} else {
|
|
promiseResults[i] = vals[i];
|
|
}
|
|
}
|
|
if (promises.length === 0) {
|
|
return promiseResults;
|
|
}
|
|
return Promise.all(promises).then(() => promiseResults);
|
|
}
|
|
|
|
/**
|
|
* return the mid value among x, y, and z
|
|
* @param x
|
|
* @param y
|
|
* @param z
|
|
* @param compare
|
|
* @returns {Promise.<*>}
|
|
*/
|
|
async function getPivot(
|
|
x: any,
|
|
y: any,
|
|
z: any,
|
|
compare: (a: any, b: any) => Promise<number>,
|
|
) {
|
|
if (await compare(x, y) < 0) {
|
|
if (await compare(y, z) < 0) {
|
|
return y;
|
|
}
|
|
if (await compare(z, x) < 0) {
|
|
return x;
|
|
}
|
|
return z;
|
|
}
|
|
if (await compare(y, z) > 0) {
|
|
return y;
|
|
}
|
|
if (await compare(z, x) > 0) {
|
|
return x;
|
|
}
|
|
return z;
|
|
}
|
|
|
|
/**
|
|
* asynchronous quick sort
|
|
* @param arr array to sort
|
|
* @param compare asynchronous comparing function
|
|
* @param left index where the range of elements to be sorted starts
|
|
* @param right index where the range of elements to be sorted ends
|
|
* @returns {Promise.<*>}
|
|
*/
|
|
export async function asyncQuickSort(
|
|
arr: any[],
|
|
compare: (a: any, b: any) => Promise<number>,
|
|
left = 0,
|
|
right = arr.length - 1,
|
|
) {
|
|
if (left < right) {
|
|
let i = left, j = right, tmp;
|
|
const pivot = await getPivot(
|
|
arr[i],
|
|
arr[i + Math.floor((j - i) / 2)],
|
|
arr[j],
|
|
compare,
|
|
);
|
|
while (true) {
|
|
while (await compare(arr[i], pivot) < 0) {
|
|
i++;
|
|
}
|
|
while (await compare(pivot, arr[j]) < 0) {
|
|
j--;
|
|
}
|
|
if (i >= j) {
|
|
break;
|
|
}
|
|
tmp = arr[i];
|
|
arr[i] = arr[j];
|
|
arr[j] = tmp;
|
|
|
|
i++;
|
|
j--;
|
|
}
|
|
await asyncQuickSort(arr, compare, left, i - 1);
|
|
await asyncQuickSort(arr, compare, j + 1, right);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
/**
|
|
* iterative async merge sort
|
|
* @param arr tagged array of { val, idx } elements
|
|
* @param compare async comparator returning <0, 0, or >0
|
|
*/
|
|
export async function asyncMergeSort(
|
|
arr: { val: any; idx: number }[],
|
|
compare: (
|
|
a: { val: any; idx: number },
|
|
b: { val: any; idx: number },
|
|
) => Promise<number>,
|
|
): Promise<void> {
|
|
const n = arr.length;
|
|
if (n <= 1) return;
|
|
const work = new Array(n);
|
|
|
|
for (let size = 1; size < n; size *= 2) {
|
|
for (let left = 0; left < n; left += 2 * size) {
|
|
const mid = Math.min(left + size, n);
|
|
const right = Math.min(left + 2 * size, n);
|
|
|
|
let i = left, j = mid, k = left;
|
|
while (i < mid && j < right) {
|
|
const cmp = await compare(arr[i], arr[j]);
|
|
if (cmp <= 0) work[k++] = arr[i++];
|
|
else work[k++] = arr[j++];
|
|
}
|
|
while (i < mid) work[k++] = arr[i++];
|
|
while (j < right) work[k++] = arr[j++];
|
|
for (let m = left; m < right; m++) arr[m] = work[m];
|
|
}
|
|
}
|
|
}
|