133 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			133 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/* -*- Mode: js; js-indent-level: 2; -*- */
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright 2011 Mozilla Foundation and contributors
							 | 
						||
| 
								 | 
							
								 * Licensed under the New BSD license. See LICENSE or:
							 | 
						||
| 
								 | 
							
								 * http://opensource.org/licenses/BSD-3-Clause
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// It turns out that some (most?) JavaScript engines don't self-host
							 | 
						||
| 
								 | 
							
								// `Array.prototype.sort`. This makes sense because C++ will likely remain
							 | 
						||
| 
								 | 
							
								// faster than JS when doing raw CPU-intensive sorting. However, when using a
							 | 
						||
| 
								 | 
							
								// custom comparator function, calling back and forth between the VM's C++ and
							 | 
						||
| 
								 | 
							
								// JIT'd JS is rather slow *and* loses JIT type information, resulting in
							 | 
						||
| 
								 | 
							
								// worse generated code for the comparator function than would be optimal. In
							 | 
						||
| 
								 | 
							
								// fact, when sorting with a comparator, these costs outweigh the benefits of
							 | 
						||
| 
								 | 
							
								// sorting in C++. By using our own JS-implemented Quick Sort (below), we get
							 | 
						||
| 
								 | 
							
								// a ~3500ms mean speed-up in `bench/bench.html`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function SortTemplate(comparator) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Swap the elements indexed by `x` and `y` in the array `ary`.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Array} ary
							 | 
						||
| 
								 | 
							
								 *        The array.
							 | 
						||
| 
								 | 
							
								 * @param {Number} x
							 | 
						||
| 
								 | 
							
								 *        The index of the first item.
							 | 
						||
| 
								 | 
							
								 * @param {Number} y
							 | 
						||
| 
								 | 
							
								 *        The index of the second item.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function swap(ary, x, y) {
							 | 
						||
| 
								 | 
							
								  var temp = ary[x];
							 | 
						||
| 
								 | 
							
								  ary[x] = ary[y];
							 | 
						||
| 
								 | 
							
								  ary[y] = temp;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Returns a random integer within the range `low .. high` inclusive.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Number} low
							 | 
						||
| 
								 | 
							
								 *        The lower bound on the range.
							 | 
						||
| 
								 | 
							
								 * @param {Number} high
							 | 
						||
| 
								 | 
							
								 *        The upper bound on the range.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function randomIntInRange(low, high) {
							 | 
						||
| 
								 | 
							
								  return Math.round(low + (Math.random() * (high - low)));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * The Quick Sort algorithm.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Array} ary
							 | 
						||
| 
								 | 
							
								 *        An array to sort.
							 | 
						||
| 
								 | 
							
								 * @param {function} comparator
							 | 
						||
| 
								 | 
							
								 *        Function to use to compare two items.
							 | 
						||
| 
								 | 
							
								 * @param {Number} p
							 | 
						||
| 
								 | 
							
								 *        Start index of the array
							 | 
						||
| 
								 | 
							
								 * @param {Number} r
							 | 
						||
| 
								 | 
							
								 *        End index of the array
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function doQuickSort(ary, comparator, p, r) {
							 | 
						||
| 
								 | 
							
								  // If our lower bound is less than our upper bound, we (1) partition the
							 | 
						||
| 
								 | 
							
								  // array into two pieces and (2) recurse on each half. If it is not, this is
							 | 
						||
| 
								 | 
							
								  // the empty array and our base case.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (p < r) {
							 | 
						||
| 
								 | 
							
								    // (1) Partitioning.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    // The partitioning chooses a pivot between `p` and `r` and moves all
							 | 
						||
| 
								 | 
							
								    // elements that are less than or equal to the pivot to the before it, and
							 | 
						||
| 
								 | 
							
								    // all the elements that are greater than it after it. The effect is that
							 | 
						||
| 
								 | 
							
								    // once partition is done, the pivot is in the exact place it will be when
							 | 
						||
| 
								 | 
							
								    // the array is put in sorted order, and it will not need to be moved
							 | 
						||
| 
								 | 
							
								    // again. This runs in O(n) time.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Always choose a random pivot so that an input array which is reverse
							 | 
						||
| 
								 | 
							
								    // sorted does not cause O(n^2) running time.
							 | 
						||
| 
								 | 
							
								    var pivotIndex = randomIntInRange(p, r);
							 | 
						||
| 
								 | 
							
								    var i = p - 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    swap(ary, pivotIndex, r);
							 | 
						||
| 
								 | 
							
								    var pivot = ary[r];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Immediately after `j` is incremented in this loop, the following hold
							 | 
						||
| 
								 | 
							
								    // true:
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
							 | 
						||
| 
								 | 
							
								    //
							 | 
						||
| 
								 | 
							
								    //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
							 | 
						||
| 
								 | 
							
								    for (var j = p; j < r; j++) {
							 | 
						||
| 
								 | 
							
								      if (comparator(ary[j], pivot, false) <= 0) {
							 | 
						||
| 
								 | 
							
								        i += 1;
							 | 
						||
| 
								 | 
							
								        swap(ary, i, j);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    swap(ary, i + 1, j);
							 | 
						||
| 
								 | 
							
								    var q = i + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // (2) Recurse on each half.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    doQuickSort(ary, comparator, p, q - 1);
							 | 
						||
| 
								 | 
							
								    doQuickSort(ary, comparator, q + 1, r);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return doQuickSort;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function cloneSort(comparator) {
							 | 
						||
| 
								 | 
							
								  let template = SortTemplate.toString();
							 | 
						||
| 
								 | 
							
								  let templateFn = new Function(`return ${template}`)();
							 | 
						||
| 
								 | 
							
								  return templateFn(comparator);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Sort the given array in-place with the given comparator function.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Array} ary
							 | 
						||
| 
								 | 
							
								 *        An array to sort.
							 | 
						||
| 
								 | 
							
								 * @param {function} comparator
							 | 
						||
| 
								 | 
							
								 *        Function to use to compare two items.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let sortCache = new WeakMap();
							 | 
						||
| 
								 | 
							
								exports.quickSort = function (ary, comparator, start = 0) {
							 | 
						||
| 
								 | 
							
								  let doQuickSort = sortCache.get(comparator);
							 | 
						||
| 
								 | 
							
								  if (doQuickSort === void 0) {
							 | 
						||
| 
								 | 
							
								    doQuickSort = cloneSort(comparator);
							 | 
						||
| 
								 | 
							
								    sortCache.set(comparator, doQuickSort);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  doQuickSort(ary, comparator, start, ary.length - 1);
							 | 
						||
| 
								 | 
							
								};
							 |