Use insertion sort for references in Array#sort (#90)

This fixes that Weak Heap Sort isn't stable and thus might swap equal values, which sometimes results in not deep equal arrays of strings, for example. Insertion sort is stable, so it is used for references instead.
This commit is contained in:
Max Graey 2018-05-02 20:33:17 +03:00 committed by Daniel Wirtz
parent 8b5d1d7f74
commit 99bde3a5fa
5 changed files with 240 additions and 1901 deletions

View File

@ -314,9 +314,15 @@ export class Array<T> {
} }
return this; return this;
} }
if (isReference<T>()) {
// TODO replace this to stable sort when it implemented
return changetype<this>(insertionSort<T>(this, comparator));
} else {
return changetype<this>(length < 256 return changetype<this>(length < 256
? insertionSort<T,T>(this, comparator) ? insertionSort<T>(this, comparator)
: weakHeapSort<T,T>(this, comparator) : weakHeapSort<T>(this, comparator)
); );
} }
} }
}

View File

@ -14,25 +14,25 @@ export function defaultComparator<T>(): (a: T, b: T) => i32 {
} }
/** Sorts an Array with the 'Insertion Sort' algorithm. */ /** Sorts an Array with the 'Insertion Sort' algorithm. */
export function insertionSort<T,V>(arr: Array<T>, comparator: (a: V, b: V) => i32): Array<T> { export function insertionSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32): Array<T> {
var buffer = arr.buffer_; var buffer = arr.buffer_;
for (let i: i32 = 0, length: i32 = arr.length; i < length; i++) { for (let i: i32 = 0, length: i32 = arr.length; i < length; i++) {
let a = loadUnsafe<T,V>(buffer, i); // a = arr[i] let a = loadUnsafe<T,T>(buffer, i); // a = arr[i]
let j = i - 1; let j = i - 1;
while (j >= 0) { while (j >= 0) {
let b = loadUnsafe<T,V>(buffer, j); // b = arr[j] let b = loadUnsafe<T,T>(buffer, j); // b = arr[j]
if (comparator(a, b) < 0) { if (comparator(a, b) < 0) {
storeUnsafe<T,V>(buffer, j-- + 1, b); // arr[j + 1] = b storeUnsafe<T,T>(buffer, j-- + 1, b); // arr[j + 1] = b
} else break; } else break;
} }
storeUnsafe<T,V>(buffer, j + 1, a); // arr[j + 1] = a storeUnsafe<T,T>(buffer, j + 1, a); // arr[j + 1] = a
} }
return arr; return arr;
} }
/** Sorts an Array with the 'Weak Heap Sort' algorithm. */ /** Sorts an Array with the 'Weak Heap Sort' algorithm. */
export function weakHeapSort<T,V>(arr: Array<T>, comparator: (a: V, b: V) => i32): Array<T> { export function weakHeapSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32): Array<T> {
const shift32 = alignof<i32>(); const shift32 = alignof<u32>();
var length = arr.length; var length = arr.length;
var bitsetSize = (length + 31) >> 5 << shift32; var bitsetSize = (length + 31) >> 5 << shift32;
@ -44,40 +44,40 @@ export function weakHeapSort<T,V>(arr: Array<T>, comparator: (a: V, b: V) => i32
var buffer = arr.buffer_; var buffer = arr.buffer_;
for (let i = length - 1; i > 0; i--) { for (let i = length - 1; i > 0; i--) {
let j = i; let j = i;
while ((j & 1) == (load<i32>(bitset + (j >> 6 << shift32)) >> (j >> 1 & 31) & 1)) j >>= 1; while ((j & 1) == (load<u32>(bitset + (j >> 6 << shift32)) >> (j >> 1 & 31) & 1)) j >>= 1;
let p = j >> 1; let p = j >> 1;
let a = loadUnsafe<T,V>(buffer, p); // a = arr[p] let a = loadUnsafe<T,T>(buffer, p); // a = arr[p]
let b = loadUnsafe<T,V>(buffer, i); // b = arr[i] let b = loadUnsafe<T,T>(buffer, i); // b = arr[i]
if (comparator(a, b) < 0) { if (comparator(a, b) < 0) {
store<i32>( store<u32>(
bitset + (i >> 5 << shift32), bitset + (i >> 5 << shift32),
load<i32>(bitset + (i >> 5 << shift32)) ^ (1 << (i & 31)) load<u32>(bitset + (i >> 5 << shift32)) ^ (1 << (i & 31))
); );
storeUnsafe<T,V>(buffer, i, a); // arr[i] = a storeUnsafe<T,T>(buffer, i, a); // arr[i] = a
storeUnsafe<T,V>(buffer, p, b); // arr[p] = b storeUnsafe<T,T>(buffer, p, b); // arr[p] = b
} }
} }
for (let i = length - 1; i >= 2; i--) { for (let i = length - 1; i >= 2; i--) {
let a = loadUnsafe<T,V>(buffer, 0); // a = arr[0] let a = loadUnsafe<T,T>(buffer, 0); // a = arr[0]
storeUnsafe<T,V>(buffer, 0, loadUnsafe<T,V>(buffer, i)); // arr[0] = arr[i] storeUnsafe<T,T>(buffer, 0, loadUnsafe<T,T>(buffer, i)); // arr[0] = arr[i]
storeUnsafe<T,V>(buffer, i, a); // arr[i] = a storeUnsafe<T,T>(buffer, i, a); // arr[i] = a
let x = 1, y: i32; let x = 1, y: i32;
while ((y = (x << 1) + ((load<i32>(bitset + (x >> 5 << shift32)) >> (x & 31)) & 1)) < i) x = y; while ((y = (x << 1) + ((load<u32>(bitset + (x >> 5 << shift32)) >> (x & 31)) & 1)) < i) x = y;
while (x > 0) { while (x > 0) {
a = loadUnsafe<T,V>(buffer, 0); // a = arr[0] a = loadUnsafe<T,T>(buffer, 0); // a = arr[0]
let b = loadUnsafe<T,V>(buffer, x); // b = arr[x] let b = loadUnsafe<T,T>(buffer, x); // b = arr[x]
if (comparator(a, b) < 0) { if (comparator(a, b) < 0) {
store<i32>( store<u32>(
bitset + (x >> 5 << shift32), bitset + (x >> 5 << shift32),
load<i32>(bitset + (x >> 5 << shift32)) ^ (1 << (x & 31)) load<u32>(bitset + (x >> 5 << shift32)) ^ (1 << (x & 31))
); );
storeUnsafe<T,V>(buffer, x, a); // arr[x] = a storeUnsafe<T,T>(buffer, x, a); // arr[x] = a
storeUnsafe<T,V>(buffer, 0, b); // arr[0] = b storeUnsafe<T,T>(buffer, 0, b); // arr[0] = b
} }
x >>= 1; x >>= 1;
} }
@ -85,8 +85,8 @@ export function weakHeapSort<T,V>(arr: Array<T>, comparator: (a: V, b: V) => i32
free_memory(bitset); free_memory(bitset);
var t = loadUnsafe<T,V>(buffer, 1); // t = arr[1] var t = loadUnsafe<T,T>(buffer, 1); // t = arr[1]
storeUnsafe<T,V>(buffer, 1, loadUnsafe<T,V>(buffer, 0)); // arr[1] = arr[0] storeUnsafe<T,T>(buffer, 1, loadUnsafe<T,T>(buffer, 0)); // arr[1] = arr[0]
storeUnsafe<T,V>(buffer, 0, t); // arr[0] = t storeUnsafe<T,T>(buffer, 0, t); // arr[0] = t
return arr; return arr;
} }

View File

@ -4645,7 +4645,7 @@
) )
) )
) )
(func $~lib/internal/array/insertionSort<i32,i32> (; 72 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/internal/array/insertionSort<i32> (; 72 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
@ -4773,7 +4773,7 @@
(func $~lib/allocator/arena/free_memory (; 73 ;) (type $iv) (param $0 i32) (func $~lib/allocator/arena/free_memory (; 73 ;) (type $iv) (param $0 i32)
(nop) (nop)
) )
(func $~lib/internal/array/weakHeapSort<i32,i32> (; 74 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/internal/array/weakHeapSort<i32> (; 74 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
@ -4834,7 +4834,7 @@
(i32.const 1) (i32.const 1)
) )
(i32.and (i32.and
(i32.shr_s (i32.shr_u
(i32.load (i32.load
(i32.add (i32.add
(get_local $6) (get_local $6)
@ -5029,7 +5029,7 @@
(i32.const 1) (i32.const 1)
) )
(i32.and (i32.and
(i32.shr_s (i32.shr_u
(i32.load (i32.load
(i32.add (i32.add
(get_local $6) (get_local $6)
@ -5273,11 +5273,11 @@
(get_local $2) (get_local $2)
(i32.const 256) (i32.const 256)
) )
(call $~lib/internal/array/insertionSort<i32,i32> (call $~lib/internal/array/insertionSort<i32>
(get_local $0) (get_local $0)
(get_local $1) (get_local $1)
) )
(call $~lib/internal/array/weakHeapSort<i32,i32> (call $~lib/internal/array/weakHeapSort<i32>
(get_local $0) (get_local $0)
(get_local $1) (get_local $1)
) )
@ -5532,7 +5532,108 @@
) )
) )
) )
(func $std/array/Proxy<i32>#constructor (; 85 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/array/Array<Array<i32>>#sort (; 85 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(if
(i32.le_s
(tee_local $2
(i32.load offset=4
(get_local $0)
)
)
(i32.const 1)
)
(return
(get_local $0)
)
)
(set_local $3
(i32.load
(get_local $0)
)
)
(if
(i32.eq
(get_local $2)
(i32.const 2)
)
(block
(set_local $2
(i32.load offset=8
(i32.add
(get_local $3)
(i32.const 4)
)
)
)
(set_local $4
(i32.load offset=8
(get_local $3)
)
)
(if
(block (result i32)
(set_global $~argc
(i32.const 2)
)
(i32.lt_s
(call_indirect (type $iii)
(get_local $2)
(get_local $4)
(get_local $1)
)
(i32.const 0)
)
)
(block
(i32.store offset=8
(i32.add
(get_local $3)
(i32.const 4)
)
(get_local $4)
)
(i32.store offset=8
(get_local $3)
(get_local $2)
)
)
)
(return
(get_local $0)
)
)
)
(call $~lib/internal/array/insertionSort<i32>
(get_local $0)
(get_local $1)
)
)
(func $std/array/assertSorted<Array<i32>> (; 86 ;) (type $iiv) (param $0 i32) (param $1 i32)
(if
(i32.eqz
(call $std/array/isSorted<i32>
(call $~lib/array/Array<Array<i32>>#sort
(get_local $0)
(get_local $1)
)
(get_local $1)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 96)
(i32.const 605)
(i32.const 2)
)
(unreachable)
)
)
)
(func $std/array/Proxy<i32>#constructor (; 87 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(if (result i32) (if (result i32)
(get_local $0) (get_local $0)
@ -5550,7 +5651,7 @@
) )
) )
) )
(func $std/array/createReverseOrderedElementsArray (; 86 ;) (type $ii) (param $0 i32) (result i32) (func $std/array/createReverseOrderedElementsArray (; 88 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(set_local $1 (set_local $1
(call $~lib/array/Array<i32>#constructor (call $~lib/array/Array<i32>#constructor
@ -5598,7 +5699,7 @@
) )
(get_local $1) (get_local $1)
) )
(func $start~anonymous|48 (; 87 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $start~anonymous|48 (; 89 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.sub (i32.sub
(i32.load (i32.load
(get_local $0) (get_local $0)
@ -5608,7 +5709,7 @@
) )
) )
) )
(func $~lib/memory/compare_memory (; 88 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (func $~lib/memory/compare_memory (; 90 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(if (if
(i32.eq (i32.eq
(get_local $0) (get_local $0)
@ -5668,7 +5769,7 @@
(i32.const 0) (i32.const 0)
) )
) )
(func $~lib/string/String.__gt (; 89 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String.__gt (; 91 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(if (if
@ -5752,7 +5853,7 @@
(i32.const 0) (i32.const 0)
) )
) )
(func $~lib/string/String.__lt (; 90 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String.__lt (; 92 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(if (if
@ -5836,7 +5937,7 @@
(i32.const 0) (i32.const 0)
) )
) )
(func $start~anonymous|49 (; 91 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $start~anonymous|49 (; 93 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.sub (i32.sub
(call $~lib/string/String.__gt (call $~lib/string/String.__gt
(get_local $0) (get_local $0)
@ -5848,7 +5949,7 @@
) )
) )
) )
(func $~lib/string/String.__eq (; 92 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String.__eq (; 94 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(if (if
(i32.eq (i32.eq
@ -5910,7 +6011,7 @@
) )
) )
) )
(func $~lib/string/String.__ne (; 93 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String.__ne (; 95 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.eqz (i32.eqz
(call $~lib/string/String.__eq (call $~lib/string/String.__eq
(get_local $0) (get_local $0)
@ -5918,7 +6019,7 @@
) )
) )
) )
(func $std/array/isArraysEqual<String> (; 94 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (func $std/array/isArraysEqual<String> (; 96 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32) (local $3 i32)
(if (if
(i32.eqz (i32.eqz
@ -5979,7 +6080,7 @@
) )
(i32.const 1) (i32.const 1)
) )
(func $std/array/isArraysEqual<String>|trampoline (; 95 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (func $std/array/isArraysEqual<String>|trampoline (; 97 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(block $1of1 (block $1of1
(block $0of1 (block $0of1
(block $oob (block $oob
@ -6002,7 +6103,7 @@
(get_local $2) (get_local $2)
) )
) )
(func $~lib/internal/string/allocate (; 96 ;) (type $ii) (param $0 i32) (result i32) (func $~lib/internal/string/allocate (; 98 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(if (if
(i32.eqz (i32.eqz
@ -6049,7 +6150,7 @@
) )
(get_local $1) (get_local $1)
) )
(func $~lib/string/String#charAt (; 97 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String#charAt (; 99 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(if (if
(i32.eqz (i32.eqz
@ -6094,7 +6195,7 @@
) )
(get_local $2) (get_local $2)
) )
(func $~lib/string/String#concat (; 98 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String#concat (; 100 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32) (local $4 i32)
@ -6181,7 +6282,7 @@
) )
(get_local $2) (get_local $2)
) )
(func $~lib/string/String.__concat (; 99 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (func $~lib/string/String.__concat (; 101 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if (if
(i32.eqz (i32.eqz
(get_local $0) (get_local $0)
@ -6195,7 +6296,7 @@
(get_local $1) (get_local $1)
) )
) )
(func $std/array/createRandomString (; 100 ;) (type $ii) (param $0 i32) (result i32) (func $std/array/createRandomString (; 102 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(set_local $1 (set_local $1
@ -6240,7 +6341,7 @@
) )
(get_local $1) (get_local $1)
) )
(func $std/array/createRandomStringArray (; 101 ;) (type $ii) (param $0 i32) (result i32) (func $std/array/createRandomStringArray (; 103 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(set_local $1 (set_local $1
(call $~lib/array/Array<i32>#constructor (call $~lib/array/Array<i32>#constructor
@ -6284,7 +6385,7 @@
) )
(get_local $1) (get_local $1)
) )
(func $start (; 102 ;) (type $v) (func $start (; 104 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset (set_global $~lib/allocator/arena/startOffset
(i32.and (i32.and
(i32.add (i32.add
@ -9503,7 +9604,7 @@
(i32.const 512) (i32.const 512)
) )
) )
(call $std/array/assertSorted<i32> (call $std/array/assertSorted<Array<i32>>
(get_global $std/array/reversedNested512) (get_global $std/array/reversedNested512)
(i32.const 47) (i32.const 47)
) )
@ -9512,11 +9613,11 @@
(i32.const 512) (i32.const 512)
) )
) )
(call $std/array/assertSorted<i32> (call $std/array/assertSorted<Array<i32>>
(get_global $std/array/reversedElements512) (get_global $std/array/reversedElements512)
(i32.const 48) (i32.const 48)
) )
(call $std/array/assertSorted<i32> (call $std/array/assertSorted<Array<i32>>
(get_global $std/array/randomStringsActual) (get_global $std/array/randomStringsActual)
(i32.const 49) (i32.const 49)
) )
@ -9548,7 +9649,7 @@
(i32.const 400) (i32.const 400)
) )
) )
(call $std/array/assertSorted<i32> (call $std/array/assertSorted<Array<i32>>
(get_global $std/array/randomStrings400) (get_global $std/array/randomStrings400)
(i32.const 50) (i32.const 50)
) )

View File

@ -672,8 +672,8 @@ assertSorted<Proxy<i32>>(reversedElements512, (a: Proxy<i32>, b: Proxy<i32>): i3
// Test sorting strings // Test sorting strings
var randomStringsActual: string[] = ['a', 'b', 'a', 'ab', 'ba', '', null]; var randomStringsActual: string[] = ["a", "b", "a", "ab", "ba", "", null];
var randomStringsExpected: string[] = ['', 'a', 'a', 'ab', 'b', 'ba', null]; var randomStringsExpected: string[] = ["", "a", "a", "ab", "b", "ba", null];
assertSorted<string>(randomStringsActual, (a: string, b: string): i32 => <i32>(a > b) - <i32>(a < b)); assertSorted<string>(randomStringsActual, (a: string, b: string): i32 => <i32>(a > b) - <i32>(a < b));
assert(isArraysEqual<string>(randomStringsActual, randomStringsExpected)); assert(isArraysEqual<string>(randomStringsActual, randomStringsExpected));

File diff suppressed because it is too large Load Diff