mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-05 10:48:12 +08:00
use quicksort instead of default system sort (#413)
This commit is contained in:
@@ -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])
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
/* eslint no-bitwise: "off" */
|
/* eslint no-bitwise: "off" */
|
||||||
|
|
||||||
|
import { sortIndex } from "./sort";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Utility functions, private to this module.
|
Utility functions, private to this module.
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +33,7 @@ export function sliceByIndex(src, index) {
|
|||||||
|
|
||||||
export function makeSortIndex(src) {
|
export function makeSortIndex(src) {
|
||||||
const index = fillRange(new Uint32Array(src.length));
|
const index = fillRange(new Uint32Array(src.length));
|
||||||
index.sort((a, b) => src[a] - src[b]);
|
sortIndex(index, src);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user