timsort.ts 22 KB

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