import { compareImpl } from "./string"; type Comparator = (a: T, b: T) => i32; // @ts-ignore: decorator @lazy @inline const EMPTY = u32.MAX_VALUE; // @ts-ignore: decorator @inline const INSERTION_SORT_THRESHOLD = 48; // @ts-ignore: decorator @inline const MIN_RUN_LENGTH = 32; // @ts-ignore: decorator @inline function log2u(n: u32): u32 { return 31 - clz(n); } // @ts-ignore: decorator @inline export function COMPARATOR(): Comparator { if (isInteger()) { if (isSigned() && sizeof() <= 4) { return (a, b) => i32(a) - i32(b); } else { return (a, b) => i32(a > b) - i32(a < b); } } else if (isFloat()) { if (sizeof() == 4) { return (a, b) => { let ia = reinterpret(f32(a)); let ib = reinterpret(f32(b)); ia ^= ia >> 31 >>> 1; ib ^= ib >> 31 >>> 1; return i32(ia > ib) - i32(ia < ib); }; } else { return (a, b) => { let ia = reinterpret(f64(a)); let ib = reinterpret(f64(b)); ia ^= ia >> 63 >>> 1; ib ^= ib >> 63 >>> 1; return i32(ia > ib) - i32(ia < ib); }; } } else if (isString()) { return (a, b) => { if ( changetype(a) == changetype(b) || changetype(a) == 0 || changetype(b) == 0 ) return 0; let alen = changetype(a).length; let blen = changetype(b).length; if (!(alen | blen)) return 0; if (!alen) return -1; if (!blen) return 1; let res = compareImpl( changetype(a), 0, changetype(b), 0, min(alen, blen) ); return res ? res : alen - blen; }; } else { return (a, b) => i32(a > b) - i32(a < b); } } // Power Sort implementation (stable) from paper "Nearly-Optimal Mergesorts" // https://arxiv.org/pdf/1805.04154.pdf // This method usually outperform TimSort. // TODO: refactor c >>> 31 to c < 0 when binaryen will support this opt export function SORT( ptr: usize, len: i32, comparator: Comparator ): void { if (len <= INSERTION_SORT_THRESHOLD) { if (len <= 1) return; if (ASC_SHRINK_LEVEL < 1) { switch (len) { case 3: { let a = load(ptr, 0); let b = load(ptr, 1 << alignof()); let c = comparator(a, b) > 0; store(ptr, select(b, a, c), 0); a = select(a, b, c); b = load(ptr, 2 << alignof()); c = comparator(a, b) > 0; store(ptr, select(b, a, c), 1 << alignof()); store(ptr, select(a, b, c), 2 << alignof()); } case 2: { let a = load(ptr, 0); let b = load(ptr, 1 << alignof()); let c = comparator(a, b) > 0; store(ptr, select(b, a, c), 0); store(ptr, select(a, b, c), 1 << alignof()); return; } } } insertionSort(ptr, 0, len - 1, 0, comparator); return; } let lgPlus2 = log2u(len) + 2; let lgPlus2Size = lgPlus2 << alignof(); let leftRunStartBuf = __alloc(lgPlus2Size << 1); let leftRunEndBuf = leftRunStartBuf + lgPlus2Size; for (let i: u32 = 0; i < lgPlus2; ++i) { store(leftRunStartBuf + (i << alignof()), EMPTY); } let buffer = __alloc(len << alignof()); let hi = len - 1; let endA = extendRunRight(ptr, 0, hi, comparator); let lenA = endA + 1; if (lenA < MIN_RUN_LENGTH) { endA = min(hi, MIN_RUN_LENGTH - 1); insertionSort(ptr, 0, endA, lenA, comparator); } let top: u32 = 0, startA = 0; while (endA < hi) { let startB = endA + 1; let endB = extendRunRight(ptr, startB, hi, comparator); let lenB = endB - startB + 1; if (lenB < MIN_RUN_LENGTH) { endB = min(hi, startB + MIN_RUN_LENGTH - 1); insertionSort(ptr, startB, endB, lenB, comparator); } let k = nodePower(0, hi, startA, startB, endB); for (let i = top; i > k; --i) { let start = load(leftRunStartBuf + (i << alignof())); if (start != EMPTY) { mergeRuns( ptr, start, load(leftRunEndBuf + (i << alignof())) + 1, endA, buffer, comparator ); startA = start; store(leftRunStartBuf + (i << alignof()), EMPTY); } } store(leftRunStartBuf + (k << alignof()), startA); store(leftRunEndBuf + (k << alignof()), endA); startA = startB; endA = endB; top = k; } for (let i = top; i != 0; --i) { let start = load(leftRunStartBuf + (i << alignof())); if (start != EMPTY) { mergeRuns( ptr, start, load(leftRunEndBuf + (i << alignof())) + 1, hi, buffer, comparator ); } } // dealloc aux buffers __free(buffer); __free(leftRunStartBuf); } function insertionSort( ptr: usize, left: i32, right: i32, presorted: i32, comparator: Comparator ): void { if (ASC_SHRINK_LEVEL >= 1) { // slightly improved original insertion sort for (let i = left + presorted; i <= right; ++i) { let j = i - 1; let a = load(ptr + (i << alignof())); while (j >= left) { let b = load(ptr + (j << alignof())); if (comparator(a, b) < 0) { store(ptr + (j << alignof()), b, 1 << alignof()); --j; } else break; } store(ptr + (j << alignof()), a, 1 << alignof()); } } else { // even-odd two-way insertion sort which allow increase minRunLen let range = right - left + 1; let i = left + select(range & 1, presorted - ((range - presorted) & 1), presorted == 0); for (; i <= right; i += 2) { let a = load(ptr + (i << alignof()), 0); let b = load(ptr + (i << alignof()), 1 << alignof()); let min = b, max = a; if (comparator(a, b) <= 0) { min = a, max = b; } let j = i - 1; while (j >= left) { a = load(ptr + (j << alignof())); if (comparator(a, max) > 0) { store(ptr + (j << alignof()), a, 2 << alignof()); --j; } else break; } store(ptr + (j << alignof()), max, 2 << alignof()); while (j >= left) { a = load(ptr + (j << alignof())); if (comparator(a, min) > 0) { store(ptr + (j << alignof()), a, 1 << alignof()); --j; } else break; } store(ptr + (j << alignof()), min, 1 << alignof()); } } } function nodePower(left: u32, right: u32, startA: u32, startB: u32, endB: u32): u32 { let n: u64 = right - left + 1; let s = startB - (left << 1); let l = startA + s; let r = endB + s + 1; let a = (l << 30) / n; let b = (r << 30) / n; return clz((a ^ b)); } function extendRunRight( ptr: usize, i: i32, right: i32, comparator: Comparator ): i32 { if (i == right) return i; let j = i; if (comparator( load(ptr + ( j << alignof())), load(ptr + (++j << alignof())) ) > 0) { while ( j < right && (comparator( load(ptr + (j << alignof()), 1 << alignof()), load(ptr + (j << alignof())) ) >>> 31) // < 0 ) ++j; // reverse let k = j; while (i < k) { let tmp = load(ptr + (i << alignof())); store(ptr + (i << alignof()), load(ptr + (k << alignof()))); ++i; store(ptr + (k << alignof()), tmp); --k; } } else { while ( j < right && comparator( load(ptr + (j << alignof()), 1 << alignof()), load(ptr + (j << alignof())) ) >= 0 ) ++j; } return j; } // Merges arr[l..m - 1] and arr[m..r] function mergeRuns( ptr: usize, l: i32, m: i32, r: i32, buffer: usize, comparator: Comparator ): void { --m; let i: i32, j: i32, t = r + m; for (i = m + 1; i > l; --i) { store( buffer + ((i - 1) << alignof()), load(ptr + ((i - 1) << alignof())) ); } for (j = m; j < r; ++j) { store( buffer + ((t - j) << alignof()), load(ptr + (j << alignof()), 1 << alignof()) ); } for (let k = l; k <= r; ++k) { let a = load(buffer + (j << alignof())); let b = load(buffer + (i << alignof())); if (comparator(a, b) < 0) { store(ptr + (k << alignof()), a); --j; } else { store(ptr + (k << alignof()), b); ++i; } } }