diff --git a/index.js b/index.js index 30676c7..1efd613 100644 --- a/index.js +++ b/index.js @@ -343,7 +343,22 @@ function upperBound(value, arr) { function sort(values, boxes, indices, left, right, nodeSize) { if (Math.floor(left / nodeSize) >= Math.floor(right / nodeSize)) return; - const pivot = values[(left + right) >> 1]; + // apply median of three method + const start = values[left]; + const mid = values[(left + right) >> 1]; + const end = values[right]; + + let pivot = end; + + const x = Math.max(start, mid); + if (end > x) { + pivot = x; + } else if (x === start) { + pivot = Math.max(mid, end); + } else if (x === mid) { + pivot = Math.max(start, end); + } + let i = left - 1; let j = right + 1; diff --git a/test.js b/test.js index 1465bec..fc3dca0 100644 --- a/test.js +++ b/test.js @@ -222,4 +222,32 @@ test('reconstructs an index from SharedArrayBuffer', () => { assert.deepEqual(index, index2); }); +test('quicksort should work with an inbalanced dataset', () => { + const n = 15000; + const index = new Flatbush(2 * n); + + function linspace(start, stop, num, endpoint = true) { + const div = endpoint ? (num - 1) : num; + const step = (stop - start) / div; + return Array.from({length: num}, (_, i) => start + step * i); + } + + const items = linspace(0, 1000, n); + const items2 = linspace(0, 1000, n); + + for (const p of items) { + index.add(p, 0, p, 0); + } + + for (const p of items2) { + index.add(p, 0, p, 0); + } + + index.finish(); + + assert.doesNotThrow(() => { + index.search(-100, -1, 15000, 1); + }); +}); + function compare(a, b) { return a - b; }