use quicksort instead of default system sort (#413)

This commit is contained in:
Bruce Martin
2018-11-07 13:35:00 -08:00
committed by GitHub
parent 121360c0a5
commit 9f7e1f1cb0
3 changed files with 159 additions and 1 deletions

View File

@@ -0,0 +1,56 @@
import { sort, sortIndex } from "../../../src/util/typedCrossfilter/sort";
function fillRange(arr, start = 0) {
const larr = arr;
for (let i = 0, len = larr.length; i < len; i += 1) {
larr[i] = i + start;
}
return larr;
}
function fillRand(arr) {
for (let i = 0, len = arr.length; i < len; i += 1) {
arr[i] = Math.random();
}
return arr;
}
describe("sort", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
test(Type.name, () => {
expect(sort(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject(
Type.from([0, 1, 2, 3, 4, 5, 6])
);
expect(sort(Type.from([6, 5, 4, 3, 2, 1]))).toMatchObject(
Type.from([1, 2, 3, 4, 5, 6])
);
const source = fillRand(new Type(1000));
expect(sort(Type.from(source))).toMatchObject(Type.from(source).sort());
})
);
});
describe("sortIndex", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
test(Type.name, () => {
const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]);
const index1 = fillRange(new Uint32Array(source1.length));
expect(sortIndex(index1, source1)).toMatchObject(
index1.sort((a, b) => source1[a] - source1[b])
);
const source2 = Type.from([6, 5, 4, 3, 2, 1]);
const index2 = fillRange(new Uint32Array(source2.length));
expect(sortIndex(index2, source2)).toMatchObject(
index2.sort((a, b) => source1[a] - source1[b])
);
const source3 = fillRand(new Type(1000));
const index3 = fillRange(new Uint32Array(source3.length));
expect(sortIndex(index3, source3)).toMatchObject(
index3.sort((a, b) => source1[a] - source1[b])
);
})
);
});

View File

@@ -0,0 +1,100 @@
const SmallArray = 32;
function insertionsort(a, lo, hi) {
for (let i = lo + 1; i < hi + 1; i += 1) {
const x = a[i];
let j;
for (j = i; j > lo && a[j - 1] > x; j -= 1) {
a[j] = a[j - 1];
}
a[j] = x;
}
return a;
}
function insertionsortIndirect(a, s, lo, hi) {
for (let i = lo + 1; i < hi + 1; i += 1) {
const x = a[i];
const t = s[x];
let j;
for (j = i; j > lo && s[a[j - 1]] > t; j -= 1) {
a[j] = a[j - 1];
}
a[j] = x;
}
return a;
}
function quicksort(a, lo, hi) {
if (hi - lo < SmallArray) {
return insertionsort(a, lo, hi);
}
if (lo < hi) {
// partition
const mid = Math.floor((lo + hi) / 2);
const p = a[mid];
let i = lo - 1;
let j = hi + 1;
while (i < j) {
do {
i += 1;
} while (a[i] < p);
do {
j -= 1;
} while (a[j] > p);
if (i < j) {
const tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
// sort
quicksort(a, lo, j);
quicksort(a, j + 1, hi);
}
return a;
}
function quicksortIndirect(a, s, lo, hi) {
if (hi - lo < SmallArray) {
return insertionsortIndirect(a, s, lo, hi);
}
if (lo < hi) {
// partition
const mid = Math.floor((lo + hi) / 2);
const p = a[mid];
const t = s[p];
let i = lo - 1;
let j = hi + 1;
while (i < j) {
do {
i += 1;
} while (s[a[i]] < t);
do {
j -= 1;
} while (s[a[j]] > t);
if (i < j) {
const tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
// sort
quicksortIndirect(a, s, lo, j);
quicksortIndirect(a, s, j + 1, hi);
}
return a;
}
// Convenience wrappers
export function sort(arr, comparator = undefined) {
if (comparator !== undefined) {
// XXX for now
return arr.sort(arr, comparator);
}
return quicksort(arr, 0, arr.length - 1);
}
export function sortIndex(index, source) {
return quicksortIndirect(index, source, 0, index.length - 1);
}

View File

@@ -1,6 +1,8 @@
// jshint esversion: 6
/* eslint no-bitwise: "off" */
import { sortIndex } from "./sort";
/*
Utility functions, private to this module.
*/
@@ -31,7 +33,7 @@ export function sliceByIndex(src, index) {
export function makeSortIndex(src) {
const index = fillRange(new Uint32Array(src.length));
index.sort((a, b) => src[a] - src[b]);
sortIndex(index, src);
return index;
}