timsort.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /**
  2. * Default minimum size of a run.
  3. */
  4. const DEFAULT_MIN_MERGE = 32;
  5. /**
  6. * Minimum ordered subsequece required to do galloping.
  7. */
  8. const DEFAULT_MIN_GALLOPING = 7;
  9. /**
  10. * Default tmp storage length. Can increase depending on the size of the
  11. * smallest run to merge.
  12. */
  13. const DEFAULT_TMP_STORAGE_LENGTH = 256;
  14. /**
  15. * Pre-computed powers of 10 for efficient lexicographic comparison of
  16. * small integers.
  17. */
  18. const POWERS_OF_TEN = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]
  19. /**
  20. * Estimate the logarithm base 10 of a small integer.
  21. *
  22. * @param {number} x - The integer to estimate the logarithm of.
  23. * @return {number} - The estimated logarithm of the integer.
  24. */
  25. function log10(x) {
  26. if (x < 1e5) {
  27. if (x < 1e2) {
  28. return x < 1e1 ? 0 : 1;
  29. }
  30. if (x < 1e4) {
  31. return x < 1e3 ? 2 : 3;
  32. }
  33. return 4;
  34. }
  35. if (x < 1e7) {
  36. return x < 1e6 ? 5 : 6;
  37. }
  38. if (x < 1e9) {
  39. return x < 1e8 ? 7 : 8;
  40. }
  41. return 9;
  42. }
  43. /**
  44. * Default alphabetical comparison of items.
  45. *
  46. * @param {string|object|number} a - First element to compare.
  47. * @param {string|object|number} b - Second element to compare.
  48. * @return {number} - A positive number if a.toString() > b.toString(), a
  49. * negative number if .toString() < b.toString(), 0 otherwise.
  50. */
  51. function alphabeticalCompare(a, b) {
  52. if (a === b) {
  53. return 0;
  54. }
  55. if (~~a === a && ~~b === b) {
  56. if (a === 0 || b === 0) {
  57. return a < b ? -1 : 1;
  58. }
  59. if (a < 0 || b < 0) {
  60. if (b >= 0) {
  61. return -1;
  62. }
  63. if (a >= 0) {
  64. return 1;
  65. }
  66. a = -a;
  67. b = -b;
  68. }
  69. const al = log10(a);
  70. const bl = log10(b);
  71. let t = 0;
  72. if (al < bl) {
  73. a *= POWERS_OF_TEN[bl - al - 1];
  74. b /= 10;
  75. t = -1;
  76. } else if (al > bl) {
  77. b *= POWERS_OF_TEN[al - bl - 1];
  78. a /= 10;
  79. t = 1;
  80. }
  81. if (a === b) {
  82. return t;
  83. }
  84. return a < b ? -1 : 1;
  85. }
  86. let aStr = String(a);
  87. let bStr = String(b);
  88. if (aStr === bStr) {
  89. return 0;
  90. }
  91. return aStr < bStr ? -1 : 1;
  92. }
  93. /**
  94. * Compute minimum run length for TimSort
  95. *
  96. * @param {number} n - The size of the array to sort.
  97. */
  98. function minRunLength(n) {
  99. let r = 0;
  100. while (n >= DEFAULT_MIN_MERGE) {
  101. r |= (n & 1);
  102. n >>= 1;
  103. }
  104. return n + r;
  105. }
  106. /**
  107. * Counts the length of a monotonically ascending or strictly monotonically
  108. * descending sequence (run) starting at array[lo] in the range [lo, hi). If
  109. * the run is descending it is made ascending.
  110. *
  111. * @param {array} array - The array to reverse.
  112. * @param {number} lo - First element in the range (inclusive).
  113. * @param {number} hi - Last element in the range.
  114. * @param {function} compare - Item comparison function.
  115. * @return {number} - The length of the run.
  116. */
  117. function makeAscendingRun(array, lo, hi, compare) {
  118. let runHi = lo + 1;
  119. if (runHi === hi) {
  120. return 1;
  121. }
  122. // Descending
  123. if (compare(array[runHi++], array[lo]) < 0) {
  124. while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
  125. runHi++;
  126. }
  127. reverseRun(array, lo, runHi);
  128. // Ascending
  129. } else {
  130. while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
  131. runHi++;
  132. }
  133. }
  134. return runHi - lo;
  135. }
  136. /**
  137. * Reverse an array in the range [lo, hi).
  138. *
  139. * @param {array} array - The array to reverse.
  140. * @param {number} lo - First element in the range (inclusive).
  141. * @param {number} hi - Last element in the range.
  142. */
  143. function reverseRun(array, lo, hi) {
  144. hi--;
  145. while (lo < hi) {
  146. let t = array[lo];
  147. array[lo++] = array[hi];
  148. array[hi--] = t;
  149. }
  150. }
  151. /**
  152. * Perform the binary sort of the array in the range [lo, hi) where start is
  153. * the first element possibly out of order.
  154. *
  155. * @param {array} array - The array to sort.
  156. * @param {number} lo - First element in the range (inclusive).
  157. * @param {number} hi - Last element in the range.
  158. * @param {number} start - First element possibly out of order.
  159. * @param {function} compare - Item comparison function.
  160. */
  161. function binaryInsertionSort(array, lo, hi, start, compare) {
  162. if (start === lo) {
  163. start++;
  164. }
  165. for (; start < hi; start++) {
  166. let pivot = array[start];
  167. // Ranges of the array where pivot belongs
  168. let left = lo;
  169. let right = start;
  170. /*
  171. * pivot >= array[i] for i in [lo, left)
  172. * pivot < array[i] for i in in [right, start)
  173. */
  174. while (left < right) {
  175. let mid = (left + right) >>> 1;
  176. if (compare(pivot, array[mid]) < 0) {
  177. right = mid;
  178. } else {
  179. left = mid + 1;
  180. }
  181. }
  182. /*
  183. * Move elements right to make room for the pivot. If there are elements
  184. * equal to pivot, left points to the first slot after them: this is also
  185. * a reason for which TimSort is stable
  186. */
  187. let n = start - left;
  188. // Switch is just an optimization for small arrays
  189. switch (n) {
  190. case 3:
  191. array[left + 3] = array[left + 2];
  192. /* falls through */
  193. case 2:
  194. array[left + 2] = array[left + 1];
  195. /* falls through */
  196. case 1:
  197. array[left + 1] = array[left];
  198. break;
  199. default:
  200. while (n > 0) {
  201. array[left + n] = array[left + n - 1];
  202. n--;
  203. }
  204. }
  205. array[left] = pivot;
  206. }
  207. }
  208. /**
  209. * Find the position at which to insert a value in a sorted range. If the range
  210. * contains elements equal to the value the leftmost element index is returned
  211. * (for stability).
  212. *
  213. * @param {number} value - Value to insert.
  214. * @param {array} array - The array in which to insert value.
  215. * @param {number} start - First element in the range.
  216. * @param {number} length - Length of the range.
  217. * @param {number} hint - The index at which to begin the search.
  218. * @param {function} compare - Item comparison function.
  219. * @return {number} - The index where to insert value.
  220. */
  221. function gallopLeft(value, array, start, length, hint, compare) {
  222. let lastOffset = 0;
  223. let maxOffset = 0;
  224. let offset = 1;
  225. if (compare(value, array[start + hint]) > 0) {
  226. maxOffset = length - hint;
  227. while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
  228. lastOffset = offset;
  229. offset = (offset << 1) + 1;
  230. if (offset <= 0) {
  231. offset = maxOffset;
  232. }
  233. }
  234. if (offset > maxOffset) {
  235. offset = maxOffset;
  236. }
  237. // Make offsets relative to start
  238. lastOffset += hint;
  239. offset += hint;
  240. // value <= array[start + hint]
  241. } else {
  242. maxOffset = hint + 1;
  243. while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
  244. lastOffset = offset;
  245. offset = (offset << 1) + 1;
  246. if (offset <= 0) {
  247. offset = maxOffset;
  248. }
  249. }
  250. if (offset > maxOffset) {
  251. offset = maxOffset;
  252. }
  253. // Make offsets relative to start
  254. let tmp = lastOffset;
  255. lastOffset = hint - offset;
  256. offset = hint - tmp;
  257. }
  258. /*
  259. * Now array[start+lastOffset] < value <= array[start+offset], so value
  260. * belongs somewhere in the range (start + lastOffset, start + offset]. Do a
  261. * binary search, with invariant array[start + lastOffset - 1] < value <=
  262. * array[start + offset].
  263. */
  264. lastOffset++;
  265. while (lastOffset < offset) {
  266. let m = lastOffset + ((offset - lastOffset) >>> 1);
  267. if (compare(value, array[start + m]) > 0) {
  268. lastOffset = m + 1;
  269. } else {
  270. offset = m;
  271. }
  272. }
  273. return offset;
  274. }
  275. /**
  276. * Find the position at which to insert a value in a sorted range. If the range
  277. * contains elements equal to the value the rightmost element index is returned
  278. * (for stability).
  279. *
  280. * @param {number} value - Value to insert.
  281. * @param {array} array - The array in which to insert value.
  282. * @param {number} start - First element in the range.
  283. * @param {number} length - Length of the range.
  284. * @param {number} hint - The index at which to begin the search.
  285. * @param {function} compare - Item comparison function.
  286. * @return {number} - The index where to insert value.
  287. */
  288. function gallopRight(value, array, start, length, hint, compare) {
  289. let lastOffset = 0;
  290. let maxOffset = 0;
  291. let offset = 1;
  292. if (compare(value, array[start + hint]) < 0) {
  293. maxOffset = hint + 1;
  294. while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
  295. lastOffset = offset;
  296. offset = (offset << 1) + 1;
  297. if (offset <= 0) {
  298. offset = maxOffset;
  299. }
  300. }
  301. if (offset > maxOffset) {
  302. offset = maxOffset;
  303. }
  304. // Make offsets relative to start
  305. let tmp = lastOffset;
  306. lastOffset = hint - offset;
  307. offset = hint - tmp;
  308. // value >= array[start + hint]
  309. } else {
  310. maxOffset = length - hint;
  311. while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
  312. lastOffset = offset;
  313. offset = (offset << 1) + 1;
  314. if (offset <= 0) {
  315. offset = maxOffset;
  316. }
  317. }
  318. if (offset > maxOffset) {
  319. offset = maxOffset;
  320. }
  321. // Make offsets relative to start
  322. lastOffset += hint;
  323. offset += hint;
  324. }
  325. /*
  326. * Now array[start+lastOffset] < value <= array[start+offset], so value
  327. * belongs somewhere in the range (start + lastOffset, start + offset]. Do a
  328. * binary search, with invariant array[start + lastOffset - 1] < value <=
  329. * array[start + offset].
  330. */
  331. lastOffset++;
  332. while (lastOffset < offset) {
  333. let m = lastOffset + ((offset - lastOffset) >>> 1);
  334. if (compare(value, array[start + m]) < 0) {
  335. offset = m;
  336. } else {
  337. lastOffset = m + 1;
  338. }
  339. }
  340. return offset;
  341. }
  342. class TimSort {
  343. array = null;
  344. compare = null;
  345. minGallop = DEFAULT_MIN_GALLOPING;
  346. length = 0;
  347. tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH;
  348. stackLength = 0;
  349. runStart = null;
  350. runLength = null;
  351. stackSize = 0;
  352. constructor(array, compare) {
  353. this.array = array;
  354. this.compare = compare;
  355. this.length = array.length;
  356. if (this.length < 2 * DEFAULT_TMP_STORAGE_LENGTH) {
  357. this.tmpStorageLength = this.length >>> 1;
  358. }
  359. this.tmp = new Array(this.tmpStorageLength);
  360. this.stackLength =
  361. (this.length < 120 ? 5 :
  362. this.length < 1542 ? 10 :
  363. this.length < 119151 ? 19 : 40);
  364. this.runStart = new Array(this.stackLength);
  365. this.runLength = new Array(this.stackLength);
  366. }
  367. /**
  368. * Push a new run on TimSort's stack.
  369. *
  370. * @param {number} runStart - Start index of the run in the original array.
  371. * @param {number} runLength - Length of the run;
  372. */
  373. pushRun(runStart, runLength) {
  374. this.runStart[this.stackSize] = runStart;
  375. this.runLength[this.stackSize] = runLength;
  376. this.stackSize += 1;
  377. }
  378. /**
  379. * Merge runs on TimSort's stack so that the following holds for all i:
  380. * 1) runLength[i - 3] > runLength[i - 2] + runLength[i - 1]
  381. * 2) runLength[i - 2] > runLength[i - 1]
  382. */
  383. mergeRuns() {
  384. while (this.stackSize > 1) {
  385. let n = this.stackSize - 2;
  386. if ((n >= 1 &&
  387. this.runLength[n - 1] <= this.runLength[n] + this.runLength[n + 1]) ||
  388. (n >= 2 &&
  389. this.runLength[n - 2] <= this.runLength[n] + this.runLength[n - 1])) {
  390. if (this.runLength[n - 1] < this.runLength[n + 1]) {
  391. n--;
  392. }
  393. } else if (this.runLength[n] > this.runLength[n + 1]) {
  394. break;
  395. }
  396. this.mergeAt(n);
  397. }
  398. }
  399. /**
  400. * Merge all runs on TimSort's stack until only one remains.
  401. */
  402. forceMergeRuns() {
  403. while (this.stackSize > 1) {
  404. let n = this.stackSize - 2;
  405. if (n > 0 && this.runLength[n - 1] < this.runLength[n + 1]) {
  406. n--;
  407. }
  408. this.mergeAt(n);
  409. }
  410. }
  411. /**
  412. * Merge the runs on the stack at positions i and i+1. Must be always be called
  413. * with i=stackSize-2 or i=stackSize-3 (that is, we merge on top of the stack).
  414. *
  415. * @param {number} i - Index of the run to merge in TimSort's stack.
  416. */
  417. mergeAt(i) {
  418. let compare = this.compare;
  419. let array = this.array;
  420. let start1 = this.runStart[i];
  421. let length1 = this.runLength[i];
  422. let start2 = this.runStart[i + 1];
  423. let length2 = this.runLength[i + 1];
  424. this.runLength[i] = length1 + length2;
  425. if (i === this.stackSize - 3) {
  426. this.runStart[i + 1] = this.runStart[i + 2];
  427. this.runLength[i + 1] = this.runLength[i + 2];
  428. }
  429. this.stackSize--;
  430. /*
  431. * Find where the first element in the second run goes in run1. Previous
  432. * elements in run1 are already in place
  433. */
  434. let k = gallopRight(array[start2], array, start1, length1, 0, compare);
  435. start1 += k;
  436. length1 -= k;
  437. if (length1 === 0) {
  438. return;
  439. }
  440. /*
  441. * Find where the last element in the first run goes in run2. Next elements
  442. * in run2 are already in place
  443. */
  444. length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
  445. if (length2 === 0) {
  446. return;
  447. }
  448. /*
  449. * Merge remaining runs. A tmp array with length = min(length1, length2) is
  450. * used
  451. */
  452. if (length1 <= length2) {
  453. this.mergeLow(start1, length1, start2, length2);
  454. } else {
  455. this.mergeHigh(start1, length1, start2, length2);
  456. }
  457. }
  458. /**
  459. * Merge two adjacent runs in a stable way. The runs must be such that the
  460. * first element of run1 is bigger than the first element in run2 and the
  461. * last element of run1 is greater than all the elements in run2.
  462. * The method should be called when run1.length <= run2.length as it uses
  463. * TimSort temporary array to store run1. Use mergeHigh if run1.length >
  464. * run2.length.
  465. *
  466. * @param {number} start1 - First element in run1.
  467. * @param {number} length1 - Length of run1.
  468. * @param {number} start2 - First element in run2.
  469. * @param {number} length2 - Length of run2.
  470. */
  471. mergeLow(start1, length1, start2, length2) {
  472. let compare = this.compare;
  473. let array = this.array;
  474. let tmp = this.tmp;
  475. let i = 0;
  476. for (i = 0; i < length1; i++) {
  477. tmp[i] = array[start1 + i];
  478. }
  479. let cursor1 = 0;
  480. let cursor2 = start2;
  481. let dest = start1;
  482. array[dest++] = array[cursor2++];
  483. if (--length2 === 0) {
  484. for (i = 0; i < length1; i++) {
  485. array[dest + i] = tmp[cursor1 + i];
  486. }
  487. return;
  488. }
  489. if (length1 === 1) {
  490. for (i = 0; i < length2; i++) {
  491. array[dest + i] = array[cursor2 + i];
  492. }
  493. array[dest + length2] = tmp[cursor1];
  494. return;
  495. }
  496. let minGallop = this.minGallop;
  497. while (true) {
  498. let count1 = 0;
  499. let count2 = 0;
  500. let exit = false;
  501. do {
  502. if (compare(array[cursor2], tmp[cursor1]) < 0) {
  503. array[dest++] = array[cursor2++];
  504. count2++;
  505. count1 = 0;
  506. if (--length2 === 0) {
  507. exit = true;
  508. break;
  509. }
  510. } else {
  511. array[dest++] = tmp[cursor1++];
  512. count1++;
  513. count2 = 0;
  514. if (--length1 === 1) {
  515. exit = true;
  516. break;
  517. }
  518. }
  519. } while ((count1 | count2) < minGallop);
  520. if (exit) {
  521. break;
  522. }
  523. do {
  524. count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
  525. if (count1 !== 0) {
  526. for (i = 0; i < count1; i++) {
  527. array[dest + i] = tmp[cursor1 + i];
  528. }
  529. dest += count1;
  530. cursor1 += count1;
  531. length1 -= count1;
  532. if (length1 <= 1) {
  533. exit = true;
  534. break;
  535. }
  536. }
  537. array[dest++] = array[cursor2++];
  538. if (--length2 === 0) {
  539. exit = true;
  540. break;
  541. }
  542. count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
  543. if (count2 !== 0) {
  544. for (i = 0; i < count2; i++) {
  545. array[dest + i] = array[cursor2 + i];
  546. }
  547. dest += count2;
  548. cursor2 += count2;
  549. length2 -= count2;
  550. if (length2 === 0) {
  551. exit = true;
  552. break;
  553. }
  554. }
  555. array[dest++] = tmp[cursor1++];
  556. if (--length1 === 1) {
  557. exit = true;
  558. break;
  559. }
  560. minGallop--;
  561. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  562. if (exit) {
  563. break;
  564. }
  565. if (minGallop < 0) {
  566. minGallop = 0;
  567. }
  568. minGallop += 2;
  569. }
  570. this.minGallop = minGallop;
  571. if (minGallop < 1) {
  572. this.minGallop = 1;
  573. }
  574. if (length1 === 1) {
  575. for (i = 0; i < length2; i++) {
  576. array[dest + i] = array[cursor2 + i];
  577. }
  578. array[dest + length2] = tmp[cursor1];
  579. } else if (length1 === 0) {
  580. throw new Error('mergeLow preconditions were not respected');
  581. } else {
  582. for (i = 0; i < length1; i++) {
  583. array[dest + i] = tmp[cursor1 + i];
  584. }
  585. }
  586. }
  587. /**
  588. * Merge two adjacent runs in a stable way. The runs must be such that the
  589. * first element of run1 is bigger than the first element in run2 and the
  590. * last element of run1 is greater than all the elements in run2.
  591. * The method should be called when run1.length > run2.length as it uses
  592. * TimSort temporary array to store run2. Use mergeLow if run1.length <=
  593. * run2.length.
  594. *
  595. * @param {number} start1 - First element in run1.
  596. * @param {number} length1 - Length of run1.
  597. * @param {number} start2 - First element in run2.
  598. * @param {number} length2 - Length of run2.
  599. */
  600. mergeHigh(start1, length1, start2, length2) {
  601. let compare = this.compare;
  602. let array = this.array;
  603. let tmp = this.tmp;
  604. let i = 0;
  605. for (i = 0; i < length2; i++) {
  606. tmp[i] = array[start2 + i];
  607. }
  608. let cursor1 = start1 + length1 - 1;
  609. let cursor2 = length2 - 1;
  610. let dest = start2 + length2 - 1;
  611. let customCursor = 0;
  612. let customDest = 0;
  613. array[dest--] = array[cursor1--];
  614. if (--length1 === 0) {
  615. customCursor = dest - (length2 - 1);
  616. for (i = 0; i < length2; i++) {
  617. array[customCursor + i] = tmp[i];
  618. }
  619. return;
  620. }
  621. if (length2 === 1) {
  622. dest -= length1;
  623. cursor1 -= length1;
  624. customDest = dest + 1;
  625. customCursor = cursor1 + 1;
  626. for (i = length1 - 1; i >= 0; i--) {
  627. array[customDest + i] = array[customCursor + i];
  628. }
  629. array[dest] = tmp[cursor2];
  630. return;
  631. }
  632. let minGallop = this.minGallop;
  633. while (true) {
  634. let count1 = 0;
  635. let count2 = 0;
  636. let exit = false;
  637. do {
  638. if (compare(tmp[cursor2], array[cursor1]) < 0) {
  639. array[dest--] = array[cursor1--];
  640. count1++;
  641. count2 = 0;
  642. if (--length1 === 0) {
  643. exit = true;
  644. break;
  645. }
  646. } else {
  647. array[dest--] = tmp[cursor2--];
  648. count2++;
  649. count1 = 0;
  650. if (--length2 === 1) {
  651. exit = true;
  652. break;
  653. }
  654. }
  655. } while ((count1 | count2) < minGallop);
  656. if (exit) {
  657. break;
  658. }
  659. do {
  660. count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
  661. if (count1 !== 0) {
  662. dest -= count1;
  663. cursor1 -= count1;
  664. length1 -= count1;
  665. customDest = dest + 1;
  666. customCursor = cursor1 + 1;
  667. for (i = count1 - 1; i >= 0; i--) {
  668. array[customDest + i] = array[customCursor + i];
  669. }
  670. if (length1 === 0) {
  671. exit = true;
  672. break;
  673. }
  674. }
  675. array[dest--] = tmp[cursor2--];
  676. if (--length2 === 1) {
  677. exit = true;
  678. break;
  679. }
  680. count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
  681. if (count2 !== 0) {
  682. dest -= count2;
  683. cursor2 -= count2;
  684. length2 -= count2;
  685. customDest = dest + 1;
  686. customCursor = cursor2 + 1;
  687. for (i = 0; i < count2; i++) {
  688. array[customDest + i] = tmp[customCursor + i];
  689. }
  690. if (length2 <= 1) {
  691. exit = true;
  692. break;
  693. }
  694. }
  695. array[dest--] = array[cursor1--];
  696. if (--length1 === 0) {
  697. exit = true;
  698. break;
  699. }
  700. minGallop--;
  701. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  702. if (exit) {
  703. break;
  704. }
  705. if (minGallop < 0) {
  706. minGallop = 0;
  707. }
  708. minGallop += 2;
  709. }
  710. this.minGallop = minGallop;
  711. if (minGallop < 1) {
  712. this.minGallop = 1;
  713. }
  714. if (length2 === 1) {
  715. dest -= length1;
  716. cursor1 -= length1;
  717. customDest = dest + 1;
  718. customCursor = cursor1 + 1;
  719. for (i = length1 - 1; i >= 0; i--) {
  720. array[customDest + i] = array[customCursor + i];
  721. }
  722. array[dest] = tmp[cursor2];
  723. } else if (length2 === 0) {
  724. throw new Error('mergeHigh preconditions were not respected');
  725. } else {
  726. customCursor = dest - (length2 - 1);
  727. for (i = 0; i < length2; i++) {
  728. array[customCursor + i] = tmp[i];
  729. }
  730. }
  731. }
  732. }
  733. /**
  734. * Sort an array in the range [lo, hi) using TimSort.
  735. *
  736. * @param {array} array - The array to sort.
  737. * @param {function=} compare - Item comparison function. Default is
  738. * alphabetical
  739. * @param {number} lo - First element in the range (inclusive).
  740. * @param {number} hi - Last element in the range.
  741. * comparator.
  742. */
  743. export function sort(array, compare, lo, hi) {
  744. if (!Array.isArray(array)) {
  745. throw new TypeError('Can only sort arrays');
  746. }
  747. /*
  748. * Handle the case where a comparison function is not provided. We do
  749. * lexicographic sorting
  750. */
  751. if (!compare) {
  752. compare = alphabeticalCompare;
  753. } else if (typeof compare !== 'function') {
  754. hi = lo;
  755. lo = compare;
  756. compare = alphabeticalCompare;
  757. }
  758. if (!lo) {
  759. lo = 0;
  760. }
  761. if (!hi) {
  762. hi = array.length;
  763. }
  764. let remaining = hi - lo;
  765. // The array is already sorted
  766. if (remaining < 2) {
  767. return;
  768. }
  769. let runLength = 0;
  770. // On small arrays binary sort can be used directly
  771. if (remaining < DEFAULT_MIN_MERGE) {
  772. runLength = makeAscendingRun(array, lo, hi, compare);
  773. binaryInsertionSort(array, lo, hi, lo + runLength, compare);
  774. return;
  775. }
  776. let ts = new TimSort(array, compare);
  777. let minRun = minRunLength(remaining);
  778. do {
  779. runLength = makeAscendingRun(array, lo, hi, compare);
  780. if (runLength < minRun) {
  781. let force = remaining;
  782. if (force > minRun) {
  783. force = minRun;
  784. }
  785. binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
  786. runLength = force;
  787. }
  788. // Push new run and merge if necessary
  789. ts.pushRun(lo, runLength);
  790. ts.mergeRuns();
  791. // Go find next run
  792. remaining -= runLength;
  793. lo += runLength;
  794. } while (remaining !== 0);
  795. // Force merging of remaining runs
  796. ts.forceMergeRuns();
  797. }