1/** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package std.core; 17 18// NOTE: autogenerated file 19 20function bubbleSort(arr: boolean[], startIndex: int, endIndex: int): void { 21 let was = true 22 while (was) { 23 was = false 24 for (let i = startIndex; i < endIndex - 1; i++) { 25 if (((arr[i + 1]) ? 1 : 0) < ((arr[i]) ? 1 : 0)) { 26 const tmp = arr[i + 1] 27 arr[i + 1] = arr[i] 28 arr[i] = tmp 29 was = true 30 } 31 } 32 } 33} 34 35function insertionSort(arr: boolean[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 36 if (startIndex != initIndex) { 37 // arr[startIndex - 1] exists and is less than or equal to all elements in range 38 for (let i = startIndex + 1; i < endIndex; i++) { 39 const tmp = arr[i] 40 let pos = i 41 while (((tmp) ? 1 : 0) < ((arr[pos - 1]) ? 1 : 0)) { 42 arr[pos] = arr[pos - 1] 43 pos-- 44 } 45 arr[pos] = tmp 46 } 47 return 48 } 49 for (let i = startIndex + 1; i < endIndex; i++) { 50 const tmp = arr[i] 51 if (((tmp) ? 1 : 0) < ((arr[startIndex]) ? 1 : 0)) { 52 for (let j = i; j > startIndex; j--) { 53 arr[j] = arr[j - 1] 54 } 55 arr[startIndex] = tmp 56 } else { 57 let pos = i 58 while (((tmp) ? 1 : 0) < ((arr[pos - 1]) ? 1 : 0)) { 59 arr[pos] = arr[pos - 1] 60 pos-- 61 } 62 arr[pos] = tmp 63 } 64 } 65} 66 67/** 68 * sorts arr in-place 69 * 70 * @param arr an array to sort 71 * 72 * @param startIndex an index to start sorting with, inclusive 73 * 74 * @param endIndex an index to end sorting, exclusive 75 * 76 * @example: sort array arr 77 * ``` 78 * sort(arr, 0, arr.length) 79 * ``` 80 */ 81export function sort(arr: boolean[], startIndex: int, endIndex: int): void { 82 if (!checkRange(arr.length, startIndex, endIndex)) { 83 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 84 } 85 86 countSortBools(arr, startIndex, endIndex) 87} 88 89function bubbleSort(arr: boolean[], startIndex: int, endIndex: int, mustPrecede: (lhs: boolean, rhs: boolean) => boolean): void { 90 let was = true 91 while (was) { 92 was = false 93 for (let i = startIndex; i < endIndex - 1; i++) { 94 if (mustPrecede(arr[i + 1], arr[i])) { 95 const tmp = arr[i + 1] 96 arr[i + 1] = arr[i] 97 arr[i] = tmp 98 was = true 99 } 100 } 101 } 102} 103 104function insertionSort(arr: boolean[], startIndex: int, endIndex: int, mustPrecede: (lhs: boolean, rhs: boolean) => boolean, initIndex: int = startIndex): void { 105 if (startIndex != initIndex) { 106 // arr[startIndex - 1] exists and is less than or equal to all elements in range 107 for (let i = startIndex + 1; i < endIndex; i++) { 108 const tmp = arr[i] 109 let pos = i 110 while (mustPrecede(tmp, arr[pos - 1])) { 111 arr[pos] = arr[pos - 1] 112 pos-- 113 } 114 arr[pos] = tmp 115 } 116 return 117 } 118 for (let i = startIndex + 1; i < endIndex; i++) { 119 const tmp = arr[i] 120 if (mustPrecede(tmp, arr[startIndex])) { 121 for (let j = i; j > startIndex; j--) { 122 arr[j] = arr[j - 1] 123 } 124 arr[startIndex] = tmp 125 } else { 126 let pos = i 127 while (mustPrecede(tmp, arr[pos - 1])) { 128 arr[pos] = arr[pos - 1] 129 pos-- 130 } 131 arr[pos] = tmp 132 } 133 } 134} 135 136/** 137 * sorts arr in-place 138 * 139 * @param arr an array to sort 140 * 141 * @param startIndex an index to start sorting with, inclusive 142 * 143 * @param endIndex an index to end sorting, exclusive 144 * 145 * @example: sort array arr 146 * ``` 147 * sort(arr, 0, arr.length) 148 * ``` 149 */ 150export function sort_subarray(arr: boolean[], startIndex: int, endIndex: int, mustPrecede: (lhs: boolean, rhs: boolean) => boolean): void { 151 if (!checkRange(arr.length, startIndex, endIndex)) { 152 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 153 } 154 155 if (mustPrecede(false, true)) { 156 countSortBools(arr, startIndex, endIndex) 157 } else { 158 countSortBoolsInv(arr, startIndex, endIndex) 159 } 160} 161 162/** 163 * sorts arr in-place 164 * 165 * @param arr an array to sort 166 */ 167export function sort_subarray(arr: boolean[], mustPrecede: (lhs: boolean, rhs: boolean) => boolean): void { 168 sort_subarray(arr, 0, arr.length, mustPrecede); 169} 170 171export function sort_subarray(arr: boolean[], startIndex: int, mustPrecede: (lhs: boolean, rhs: boolean) => boolean): void { 172 sort_subarray(arr, startIndex, arr.length, mustPrecede) 173} 174 175function countSortTruthCnt(arr: boolean[], startIndex: int, endIndex: int): int { 176 let truthCnt = 0 177 for (let i = startIndex; i < endIndex; i++) { 178 if (arr[i]) { 179 truthCnt++ 180 } 181 } 182 return truthCnt 183} 184 185function countSortBools(arr: boolean[], startIndex: int, endIndex: int): void { 186 const truthCnt = countSortTruthCnt(arr, startIndex, endIndex) 187 for (let i = startIndex; i < endIndex - truthCnt; i++) { 188 arr[i] = false 189 } 190 for (let i = 0; i < truthCnt; i++) { 191 arr[endIndex - truthCnt + i] = true 192 } 193} 194function countSortBoolsInv(arr: boolean[], startIndex: int, endIndex: int): void { 195 const truthCnt = countSortTruthCnt(arr, startIndex, endIndex) 196 for (let i = 0; i < truthCnt; i++) { 197 arr[startIndex + i] = true 198 } 199 for (let i = startIndex + truthCnt; i < endIndex; i++) { 200 arr[i] = false 201 } 202} 203 204export function sort(arr: boolean[], startIndex: int): void { 205 sort(arr, startIndex, arr.length) 206} 207 208// ======== tests section ======== 209 210/** note: used for tests, {@link test_sortAllOn} */ 211class test_SortDataboolean { 212 name: string 213 arr: boolean[] 214 215 constructor(name: string, arr: boolean[]) { 216 this.name = name 217 this.arr = arr 218 } 219} 220 221/** note: used for tests, {@link test_sortAllOn} */ 222function test_sortCopy(arr: boolean[]): boolean[] { 223 let c = new boolean[arr.length] 224 for (let i = 0; i < arr.length; i++) { 225 c[i] = arr[i] 226 } 227 return c 228} 229 230/** note: used for tests, {@link test_sortAllOn} */ 231function test_sortAllOnCmpFwd_boolean(l: boolean, r: boolean): boolean { 232 return ((l) ? 1 : 0) < ((r) ? 1 : 0) 233} 234 235/** note: used for tests, {@link test_sortAllOn} */ 236function test_sortAllOnCmpInv_boolean(l: boolean, r: boolean): boolean { 237 return ((r) ? 1 : 0) < ((l) ? 1 : 0) 238} 239 240/** note: used for tests, {@link test_sortAllOn} */ 241function test_printArr(arr: boolean[], startIndex: int, endIndex: int) { 242 for (let i = startIndex; i < endIndex; i++) { 243 console.print(arr[i] + ' ') 244 } 245 console.println('') 246} 247 248/** 249 * Function used to test sorting in standard library tests. 250 * There is only one exported function: `sort`, but for testing 251 * we need access to all sub sorts that are used. Hence this part of "tests" 252 * is located here. All related entities are prefixed whith `test_` to stand out 253 */ 254export function test_sortAllOn(arr: boolean[]) { 255 // block for comparator `` 256 if (true) { 257 const sorts = new Array<test_SortDataboolean>() 258 const bubbled = test_sortCopy(arr) 259 bubbleSort(bubbled, 0, bubbled.length) 260 sorts.push(new test_SortDataboolean("bubble", bubbled)) 261 262 const insertion = test_sortCopy(arr) 263 insertionSort(insertion, 0, insertion.length) 264 sorts.push(new test_SortDataboolean("insertion", insertion)) 265 266 const bcnt = test_sortCopy(arr) 267 countSortBools(bcnt, 0, bcnt.length) 268 sorts.push(new test_SortDataboolean("bools cnt()", bcnt)) 269 270 const just = test_sortCopy(arr) 271 sort(just) 272 sorts.push(new test_SortDataboolean("sort()", just)) 273 274 let sort0 = sorts.at(0)! 275 for (let s = 1; s < sorts.length; s++) { 276 let sortS = sorts.at(s)! 277 for (let i = 0; i < arr.length; i++) { 278 if (sort0.arr[i] != sortS.arr[i]) { 279 console.println(sort0.name + ': ') 280 test_printArr(sort0.arr, 0, arr.length) 281 console.println(sortS.name + ': ') 282 test_printArr(sortS.arr, 0, arr.length) 283 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for boolean') 284 } 285 } 286 } 287 } 288 // block for comparator `est_sortAllOnCmpFwd_boolean` 289 if (true) { 290 const sorts = new Array<test_SortDataboolean>() 291 const bubbled = test_sortCopy(arr) 292 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_boolean) 293 sorts.push(new test_SortDataboolean("bubble", bubbled)) 294 295 const insertion = test_sortCopy(arr) 296 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_boolean) 297 sorts.push(new test_SortDataboolean("insertion", insertion)) 298 299 const bcnt = test_sortCopy(arr) 300 countSortBools(bcnt, 0, bcnt.length) 301 sorts.push(new test_SortDataboolean("bools cnt()", bcnt)) 302 303 const just = test_sortCopy(arr) 304 sort_subarray(just, test_sortAllOnCmpFwd_boolean) 305 sorts.push(new test_SortDataboolean("sort()", just)) 306 307 let sort0 = sorts.at(0)! 308 for (let s = 1; s < sorts.length; s++) { 309 let sortS = sorts.at(s)! 310 for (let i = 0; i < arr.length; i++) { 311 if (sort0.arr[i] != sortS.arr[i]) { 312 console.println(sort0.name + ': ') 313 test_printArr(sort0.arr, 0, arr.length) 314 console.println(sortS.name + ': ') 315 test_printArr(sortS.arr, 0, arr.length) 316 throw new Error("sorts est_sortAllOnCmpFwd_boolean are not equal: " + sort0.name + ' ' + sortS.name + ' for boolean') 317 } 318 } 319 } 320 } 321 // block for comparator `est_sortAllOnCmpInv_boolean` 322 if (true) { 323 const sorts = new Array<test_SortDataboolean>() 324 const bubbled = test_sortCopy(arr) 325 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_boolean) 326 sorts.push(new test_SortDataboolean("bubble", bubbled)) 327 328 const insertion = test_sortCopy(arr) 329 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_boolean) 330 sorts.push(new test_SortDataboolean("insertion", insertion)) 331 332 const bcnt = test_sortCopy(arr) 333 countSortBoolsInv(bcnt, 0, bcnt.length) 334 sorts.push(new test_SortDataboolean("bools cnt()", bcnt)) 335 336 const just = test_sortCopy(arr) 337 sort_subarray(just, test_sortAllOnCmpInv_boolean) 338 sorts.push(new test_SortDataboolean("sort()", just)) 339 340 let sort0 = sorts.at(0)! 341 for (let s = 1; s < sorts.length; s++) { 342 let sortS = sorts.at(s)! 343 for (let i = 0; i < arr.length; i++) { 344 if (sort0.arr[i] != sortS.arr[i]) { 345 console.println(sort0.name + ': ') 346 test_printArr(sort0.arr, 0, arr.length) 347 console.println(sortS.name + ': ') 348 test_printArr(sortS.arr, 0, arr.length) 349 throw new Error("sorts est_sortAllOnCmpInv_boolean are not equal: " + sort0.name + ' ' + sortS.name + ' for boolean') 350 } 351 } 352 } 353 } 354} 355// ======== end of tests section ======== 356 357function bubbleSort(arr: byte[], startIndex: int, endIndex: int): void { 358 let was = true 359 while (was) { 360 was = false 361 for (let i = startIndex; i < endIndex - 1; i++) { 362 if ((arr[i + 1] < arr[i])) { 363 const tmp = arr[i + 1] 364 arr[i + 1] = arr[i] 365 arr[i] = tmp 366 was = true 367 } 368 } 369 } 370} 371 372function insertionSort(arr: byte[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 373 if (startIndex != initIndex) { 374 // arr[startIndex - 1] exists and is less than or equal to all elements in range 375 for (let i = startIndex + 1; i < endIndex; i++) { 376 const tmp = arr[i] 377 let pos = i 378 while ((tmp < arr[pos - 1])) { 379 arr[pos] = arr[pos - 1] 380 pos-- 381 } 382 arr[pos] = tmp 383 } 384 return 385 } 386 for (let i = startIndex + 1; i < endIndex; i++) { 387 const tmp = arr[i] 388 if ((tmp < arr[startIndex])) { 389 for (let j = i; j > startIndex; j--) { 390 arr[j] = arr[j - 1] 391 } 392 arr[startIndex] = tmp 393 } else { 394 let pos = i 395 while ((tmp < arr[pos - 1])) { 396 arr[pos] = arr[pos - 1] 397 pos-- 398 } 399 arr[pos] = tmp 400 } 401 } 402} 403function heapSortUp(arr: byte[], idxFromStart: int, startIndex: int, heapRoot: int): void { 404 const tmp = arr[startIndex + idxFromStart] 405 while (startIndex + idxFromStart > heapRoot) { 406 const p = (idxFromStart - 1) / 2 407 if (!(arr[startIndex + p] < tmp)) { 408 break 409 } 410 arr[startIndex + idxFromStart] = arr[startIndex + p] 411 idxFromStart = p 412 } 413 arr[startIndex + idxFromStart] = tmp 414} 415 416// Build max heap with root in startIndex given its children are roots of valid heaps 417function heapSortDown(arr: byte[], idxFromStart: int, startIndex: int, endIndex: int): void { 418 let heapRoot = startIndex + idxFromStart 419 let arrIndex = heapRoot 420 let childIndex = startIndex + idxFromStart * 2 + 1 421 const tmp = arr[arrIndex] 422 // Walk heap to bottom and pull max child up on each level 423 while (childIndex + 1 < endIndex) { 424 if ((arr[childIndex] < arr[childIndex + 1])) { 425 childIndex++ 426 } 427 arr[arrIndex] = arr[childIndex] 428 arrIndex = childIndex 429 childIndex = childIndex * 2 - startIndex + 1 430 } 431 if (childIndex < endIndex) { 432 arr[arrIndex] = arr[childIndex] 433 arrIndex = childIndex 434 } 435 arr[arrIndex] = tmp 436 // Now heap is valid in all positions but arrIndex 437 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 438} 439 440export function heapSort(arr: byte[], startIndex: int, endIndex: int): void { 441 let len = endIndex - startIndex 442 for (let i = len / 2 - 1; i >= 0; i--) { 443 heapSortDown(arr, i, startIndex, endIndex) 444 } 445 446 for (let i = endIndex - 1; i > startIndex; i--) { 447 // move max element to the end of range 448 const tmp = arr[i] 449 arr[i] = arr[startIndex] 450 arr[startIndex] = tmp 451 heapSortDown(arr, 0, startIndex, i) 452 } 453} 454 455// Put median of three array elements to arr[index1] 456function median3(arr: byte[], index1: int, index2: int, index3: int): void { 457 let swap_idx = index2 458 if ((arr[index1] < arr[index2])) { 459 if ((arr[index3] < arr[index1])) { 460 return 461 } 462 if ((arr[index3] < arr[index2])) { 463 swap_idx = index3 464 } 465 } else { 466 if (!(arr[index3] < arr[index1])) { 467 return 468 } 469 if ((arr[index2] < arr[index3])) { 470 swap_idx = index3 471 } 472 } 473 let tmp = arr[index1] 474 arr[index1] = arr[swap_idx] 475 arr[swap_idx] = tmp 476} 477 478// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 479// Elements equal to pivot go to the right 480function quickSortSplit(arr: byte[], startIndex: int, endIndex: int): int { 481 const pivot = arr[startIndex] 482 let i = startIndex + 1 483 let j = endIndex - 1 484 // No bounds check because pivot is median of three elements 485 while ((arr[i] < pivot)) { 486 i++ 487 } 488 if (i == startIndex + 1) { 489 while (i < j && !(arr[j] < pivot)) { 490 j-- 491 } 492 } else { 493 while (!(arr[j] < pivot)) { 494 j-- 495 } 496 } 497 while (i < j) { 498 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 499 let tmp = arr[i] 500 arr[i] = arr[j] 501 arr[j] = tmp 502 while ((arr[++i] < pivot)) {} 503 while (!(arr[--j] < pivot)) {} 504 } 505 let pivotIndex = i - 1 506 arr[startIndex] = arr[pivotIndex] 507 arr[pivotIndex] = pivot 508 509 return pivotIndex 510} 511 512// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 513// Elements equal to pivot go to the left 514function quickSortSplitLeft(arr: byte[], startIndex: int, endIndex: int): int { 515 const pivot = arr[startIndex] 516 let i = startIndex + 1 517 let j = endIndex - 1 518 // No bounds check because pivot is median of three elements 519 while ((pivot < arr[j])) { 520 j-- 521 } 522 if (j + 1 == endIndex) { 523 while (i < j && !(pivot < arr[i])) { 524 i++ 525 } 526 } else { 527 while (!(pivot < arr[i])) { 528 i++ 529 } 530 } 531 while (i < j) { 532 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 533 let tmp = arr[i] 534 arr[i] = arr[j] 535 arr[j] = tmp 536 while (!(pivot < arr[++i])) {} 537 while ((pivot < arr[--j])) {} 538 } 539 arr[startIndex] = arr[j] 540 arr[j] = pivot 541 542 return j 543} 544 545function quickSortImpl3(arr: byte[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 546 while (endIndex - startIndex > 3) { 547 if (--maxDepth == 0) { 548 heapSort(arr, startIndex, endIndex) 549 return 550 } 551 // Here we assume that current interval is not the most left in the sorted range 552 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 553 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 554 // after that only the right part needs to be sorted 555 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 556 // with many occurencies of the smallest element 557 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 558 continue 559 } 560 561 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 562 let p = quickSortSplit(arr, startIndex, endIndex) 563 // make a call for the smaller part of array and continue processing the larger part in the loop 564 if (p - startIndex < endIndex - p) { 565 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 566 startIndex = p + 1 567 } else { 568 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 569 endIndex = p 570 } 571 } 572 insertionSort(arr, startIndex, endIndex, initIndex) 573} 574function quickSortImpl40(arr: byte[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 575 while (endIndex - startIndex > 40) { 576 if (--maxDepth == 0) { 577 heapSort(arr, startIndex, endIndex) 578 return 579 } 580 // Here we assume that current interval is not the most left in the sorted range 581 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 582 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 583 // after that only the right part needs to be sorted 584 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 585 // with many occurencies of the smallest element 586 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 587 continue 588 } 589 590 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 591 let p = quickSortSplit(arr, startIndex, endIndex) 592 // make a call for the smaller part of array and continue processing the larger part in the loop 593 if (p - startIndex < endIndex - p) { 594 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 595 startIndex = p + 1 596 } else { 597 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 598 endIndex = p 599 } 600 } 601 insertionSort(arr, startIndex, endIndex, initIndex) 602} 603 604function quickSort(arr: byte[], startIndex: int, endIndex: int): void { 605 let size = endIndex - startIndex 606 if (size <= 1) { 607 return 608 } 609 // find log of length to fall back into determenistic O(n logn) sort 610 let bits = 32 611 for (let i = 2; i < 31; i++) { 612 if ((size >> i) == 0) { 613 bits = i 614 break 615 } 616 } 617 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 618} 619 620/** 621 * sorts arr in-place 622 * 623 * @param arr an array to sort 624 * 625 * @param startIndex an index to start sorting with, inclusive 626 * 627 * @param endIndex an index to end sorting, exclusive 628 * 629 * @example: sort array arr 630 * ``` 631 * sort(arr, 0, arr.length) 632 * ``` 633 */ 634export function sort(arr: byte[], startIndex: int, endIndex: int): void { 635 if (!checkRange(arr.length, startIndex, endIndex)) { 636 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 637 } 638 639 if (endIndex - startIndex > 1024) { 640 countSort(arr, startIndex, endIndex) 641 } 642 quickSort(arr, startIndex, endIndex); 643} 644 645function bubbleSort(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 646 let was = true 647 while (was) { 648 was = false 649 for (let i = startIndex; i < endIndex - 1; i++) { 650 if (mustPrecede(arr[i + 1], arr[i])) { 651 const tmp = arr[i + 1] 652 arr[i + 1] = arr[i] 653 arr[i] = tmp 654 was = true 655 } 656 } 657 } 658} 659 660function insertionSort(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean, initIndex: int = startIndex): void { 661 if (startIndex != initIndex) { 662 // arr[startIndex - 1] exists and is less than or equal to all elements in range 663 for (let i = startIndex + 1; i < endIndex; i++) { 664 const tmp = arr[i] 665 let pos = i 666 while (mustPrecede(tmp, arr[pos - 1])) { 667 arr[pos] = arr[pos - 1] 668 pos-- 669 } 670 arr[pos] = tmp 671 } 672 return 673 } 674 for (let i = startIndex + 1; i < endIndex; i++) { 675 const tmp = arr[i] 676 if (mustPrecede(tmp, arr[startIndex])) { 677 for (let j = i; j > startIndex; j--) { 678 arr[j] = arr[j - 1] 679 } 680 arr[startIndex] = tmp 681 } else { 682 let pos = i 683 while (mustPrecede(tmp, arr[pos - 1])) { 684 arr[pos] = arr[pos - 1] 685 pos-- 686 } 687 arr[pos] = tmp 688 } 689 } 690} 691function heapSortUp(arr: byte[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 692 const tmp = arr[startIndex + idxFromStart] 693 while (startIndex + idxFromStart > heapRoot) { 694 const p = (idxFromStart - 1) / 2 695 if (!mustPrecede(arr[startIndex + p], tmp)) { 696 break 697 } 698 arr[startIndex + idxFromStart] = arr[startIndex + p] 699 idxFromStart = p 700 } 701 arr[startIndex + idxFromStart] = tmp 702} 703 704// Build max heap with root in startIndex given its children are roots of valid heaps 705function heapSortDown(arr: byte[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 706 let heapRoot = startIndex + idxFromStart 707 let arrIndex = heapRoot 708 let childIndex = startIndex + idxFromStart * 2 + 1 709 const tmp = arr[arrIndex] 710 // Walk heap to bottom and pull max child up on each level 711 while (childIndex + 1 < endIndex) { 712 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 713 childIndex++ 714 } 715 arr[arrIndex] = arr[childIndex] 716 arrIndex = childIndex 717 childIndex = childIndex * 2 - startIndex + 1 718 } 719 if (childIndex < endIndex) { 720 arr[arrIndex] = arr[childIndex] 721 arrIndex = childIndex 722 } 723 arr[arrIndex] = tmp 724 // Now heap is valid in all positions but arrIndex 725 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 726} 727 728export function heapSort(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 729 let len = endIndex - startIndex 730 for (let i = len / 2 - 1; i >= 0; i--) { 731 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 732 } 733 734 for (let i = endIndex - 1; i > startIndex; i--) { 735 // move max element to the end of range 736 const tmp = arr[i] 737 arr[i] = arr[startIndex] 738 arr[startIndex] = tmp 739 heapSortDown(arr, 0, startIndex, i, mustPrecede) 740 } 741} 742 743// Put median of three array elements to arr[index1] 744function median3(arr: byte[], index1: int, index2: int, index3: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 745 let swap_idx = index2 746 if (mustPrecede(arr[index1], arr[index2])) { 747 if (mustPrecede(arr[index3], arr[index1])) { 748 return 749 } 750 if (mustPrecede(arr[index3], arr[index2])) { 751 swap_idx = index3 752 } 753 } else { 754 if (!mustPrecede(arr[index3], arr[index1])) { 755 return 756 } 757 if (mustPrecede(arr[index2], arr[index3])) { 758 swap_idx = index3 759 } 760 } 761 let tmp = arr[index1] 762 arr[index1] = arr[swap_idx] 763 arr[swap_idx] = tmp 764} 765 766// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 767// Elements equal to pivot go to the right 768function quickSortSplit(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): int { 769 const pivot = arr[startIndex] 770 let i = startIndex + 1 771 let j = endIndex - 1 772 // No bounds check because pivot is median of three elements 773 while (mustPrecede(arr[i], pivot)) { 774 i++ 775 } 776 if (i == startIndex + 1) { 777 while (i < j && !mustPrecede(arr[j], pivot)) { 778 j-- 779 } 780 } else { 781 while (!mustPrecede(arr[j], pivot)) { 782 j-- 783 } 784 } 785 while (i < j) { 786 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 787 let tmp = arr[i] 788 arr[i] = arr[j] 789 arr[j] = tmp 790 while (mustPrecede(arr[++i], pivot)) {} 791 while (!mustPrecede(arr[--j], pivot)) {} 792 } 793 let pivotIndex = i - 1 794 arr[startIndex] = arr[pivotIndex] 795 arr[pivotIndex] = pivot 796 797 return pivotIndex 798} 799 800// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 801// Elements equal to pivot go to the left 802function quickSortSplitLeft(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): int { 803 const pivot = arr[startIndex] 804 let i = startIndex + 1 805 let j = endIndex - 1 806 // No bounds check because pivot is median of three elements 807 while (mustPrecede(pivot, arr[j])) { 808 j-- 809 } 810 if (j + 1 == endIndex) { 811 while (i < j && !mustPrecede(pivot, arr[i])) { 812 i++ 813 } 814 } else { 815 while (!mustPrecede(pivot, arr[i])) { 816 i++ 817 } 818 } 819 while (i < j) { 820 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 821 let tmp = arr[i] 822 arr[i] = arr[j] 823 arr[j] = tmp 824 while (!mustPrecede(pivot, arr[++i])) {} 825 while (mustPrecede(pivot, arr[--j])) {} 826 } 827 arr[startIndex] = arr[j] 828 arr[j] = pivot 829 830 return j 831} 832 833function quickSortImpl3(arr: byte[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 834 while (endIndex - startIndex > 3) { 835 if (--maxDepth == 0) { 836 heapSort(arr, startIndex, endIndex, mustPrecede) 837 return 838 } 839 840 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 841 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 842 // make a call for the smaller part of array and continue processing the larger part in the loop 843 if (p - startIndex < endIndex - p) { 844 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 845 startIndex = p + 1 846 } else { 847 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 848 endIndex = p 849 } 850 } 851 insertionSort(arr, startIndex, endIndex, mustPrecede) 852} 853function quickSortImpl40(arr: byte[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 854 while (endIndex - startIndex > 40) { 855 if (--maxDepth == 0) { 856 heapSort(arr, startIndex, endIndex, mustPrecede) 857 return 858 } 859 860 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 861 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 862 // make a call for the smaller part of array and continue processing the larger part in the loop 863 if (p - startIndex < endIndex - p) { 864 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 865 startIndex = p + 1 866 } else { 867 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 868 endIndex = p 869 } 870 } 871 insertionSort(arr, startIndex, endIndex, mustPrecede) 872} 873 874function quickSort(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 875 let size = endIndex - startIndex 876 if (size <= 1) { 877 return 878 } 879 // find log of length to fall back into determenistic O(n logn) sort 880 let bits = 32 881 for (let i = 2; i < 31; i++) { 882 if ((size >> i) == 0) { 883 bits = i 884 break 885 } 886 } 887 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 888} 889 890/** 891 * sorts arr in-place 892 * 893 * @param arr an array to sort 894 * 895 * @param startIndex an index to start sorting with, inclusive 896 * 897 * @param endIndex an index to end sorting, exclusive 898 * 899 * @example: sort array arr 900 * ``` 901 * sort(arr, 0, arr.length) 902 * ``` 903 */ 904export function sort_subarray(arr: byte[], startIndex: int, endIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 905 if (!checkRange(arr.length, startIndex, endIndex)) { 906 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 907 } 908 909 quickSort(arr, startIndex, endIndex, mustPrecede); 910} 911 912/** 913 * sorts arr in-place 914 * 915 * @param arr an array to sort 916 */ 917export function sort_subarray(arr: byte[], mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 918 sort_subarray(arr, 0, arr.length, mustPrecede); 919} 920 921export function sort_subarray(arr: byte[], startIndex: int, mustPrecede: (lhs: byte, rhs: byte) => boolean): void { 922 sort_subarray(arr, startIndex, arr.length, mustPrecede) 923} 924 925function countSort(arr: byte[], startIndex: int, endIndex: int): void { 926 const cnts = new int[256] 927 for (let i = startIndex; i < endIndex; i++) { 928 cnts[arr[i] + 128]++ 929 } 930 let idx = 0 931 for (let i = 0; i < 256; i++) { 932 for (let j = 0; j < cnts[i]; j++) { 933 arr[startIndex + idx++] = (i - 128) as byte 934 } 935 } 936} 937 938export function sort(arr: byte[], startIndex: int): void { 939 sort(arr, startIndex, arr.length) 940} 941 942// ======== tests section ======== 943 944/** note: used for tests, {@link test_sortAllOn} */ 945class test_SortDatabyte { 946 name: string 947 arr: byte[] 948 949 constructor(name: string, arr: byte[]) { 950 this.name = name 951 this.arr = arr 952 } 953} 954 955/** note: used for tests, {@link test_sortAllOn} */ 956function test_sortCopy(arr: byte[]): byte[] { 957 let c = new byte[arr.length] 958 for (let i = 0; i < arr.length; i++) { 959 c[i] = arr[i] 960 } 961 return c 962} 963 964/** note: used for tests, {@link test_sortAllOn} */ 965function test_sortAllOnCmpFwd_byte(l: byte, r: byte): boolean { 966 return l < r 967} 968 969/** note: used for tests, {@link test_sortAllOn} */ 970function test_sortAllOnCmpInv_byte(l: byte, r: byte): boolean { 971 return r < l 972} 973 974/** note: used for tests, {@link test_sortAllOn} */ 975function test_printArr(arr: byte[], startIndex: int, endIndex: int) { 976 for (let i = startIndex; i < endIndex; i++) { 977 console.print(arr[i] + ' ') 978 } 979 console.println('') 980} 981 982/** 983 * Function used to test sorting in standard library tests. 984 * There is only one exported function: `sort`, but for testing 985 * we need access to all sub sorts that are used. Hence this part of "tests" 986 * is located here. All related entities are prefixed whith `test_` to stand out 987 */ 988export function test_sortAllOn(arr: byte[]) { 989 // block for comparator `` 990 if (true) { 991 const sorts = new Array<test_SortDatabyte>() 992 const bubbled = test_sortCopy(arr) 993 bubbleSort(bubbled, 0, bubbled.length) 994 sorts.push(new test_SortDatabyte("bubble", bubbled)) 995 996 const insertion = test_sortCopy(arr) 997 insertionSort(insertion, 0, insertion.length) 998 sorts.push(new test_SortDatabyte("insertion", insertion)) 999 const cnt = test_sortCopy(arr) 1000 countSort(cnt, 0, cnt.length) 1001 sorts.push(new test_SortDatabyte("cnt()", cnt)) 1002 1003 const heaped = test_sortCopy(arr) 1004 heapSort(heaped, 0, heaped.length) 1005 sorts.push(new test_SortDatabyte("heap", heaped)) 1006 1007 const quicked = test_sortCopy(arr) 1008 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 1009 sorts.push(new test_SortDatabyte("quick", quicked)) 1010 1011 const quickedInsertion = test_sortCopy(arr) 1012 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 1013 sorts.push(new test_SortDatabyte("quick with insertion", quickedInsertion)) 1014 1015 const just = test_sortCopy(arr) 1016 sort(just) 1017 sorts.push(new test_SortDatabyte("sort()", just)) 1018 1019 let sort0 = sorts.at(0)! 1020 for (let s = 1; s < sorts.length; s++) { 1021 let sortS = sorts.at(s)! 1022 for (let i = 0; i < arr.length; i++) { 1023 if (sort0.arr[i] != sortS.arr[i]) { 1024 console.println(sort0.name + ': ') 1025 test_printArr(sort0.arr, 0, arr.length) 1026 console.println(sortS.name + ': ') 1027 test_printArr(sortS.arr, 0, arr.length) 1028 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for byte') 1029 } 1030 } 1031 } 1032 } 1033 // block for comparator `est_sortAllOnCmpFwd_byte` 1034 if (true) { 1035 const sorts = new Array<test_SortDatabyte>() 1036 const bubbled = test_sortCopy(arr) 1037 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_byte) 1038 sorts.push(new test_SortDatabyte("bubble", bubbled)) 1039 1040 const insertion = test_sortCopy(arr) 1041 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_byte) 1042 sorts.push(new test_SortDatabyte("insertion", insertion)) 1043 1044 const heaped = test_sortCopy(arr) 1045 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_byte) 1046 sorts.push(new test_SortDatabyte("heap", heaped)) 1047 1048 const quicked = test_sortCopy(arr) 1049 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_byte) 1050 sorts.push(new test_SortDatabyte("quick", quicked)) 1051 1052 const quickedInsertion = test_sortCopy(arr) 1053 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_byte) 1054 sorts.push(new test_SortDatabyte("quick with insertion", quickedInsertion)) 1055 1056 const just = test_sortCopy(arr) 1057 sort_subarray(just, test_sortAllOnCmpFwd_byte) 1058 sorts.push(new test_SortDatabyte("sort()", just)) 1059 1060 let sort0 = sorts.at(0)! 1061 for (let s = 1; s < sorts.length; s++) { 1062 let sortS = sorts.at(s)! 1063 for (let i = 0; i < arr.length; i++) { 1064 if (sort0.arr[i] != sortS.arr[i]) { 1065 console.println(sort0.name + ': ') 1066 test_printArr(sort0.arr, 0, arr.length) 1067 console.println(sortS.name + ': ') 1068 test_printArr(sortS.arr, 0, arr.length) 1069 throw new Error("sorts est_sortAllOnCmpFwd_byte are not equal: " + sort0.name + ' ' + sortS.name + ' for byte') 1070 } 1071 } 1072 } 1073 } 1074 // block for comparator `est_sortAllOnCmpInv_byte` 1075 if (true) { 1076 const sorts = new Array<test_SortDatabyte>() 1077 const bubbled = test_sortCopy(arr) 1078 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_byte) 1079 sorts.push(new test_SortDatabyte("bubble", bubbled)) 1080 1081 const insertion = test_sortCopy(arr) 1082 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_byte) 1083 sorts.push(new test_SortDatabyte("insertion", insertion)) 1084 1085 const heaped = test_sortCopy(arr) 1086 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_byte) 1087 sorts.push(new test_SortDatabyte("heap", heaped)) 1088 1089 const quicked = test_sortCopy(arr) 1090 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_byte) 1091 sorts.push(new test_SortDatabyte("quick", quicked)) 1092 1093 const quickedInsertion = test_sortCopy(arr) 1094 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_byte) 1095 sorts.push(new test_SortDatabyte("quick with insertion", quickedInsertion)) 1096 1097 const just = test_sortCopy(arr) 1098 sort_subarray(just, test_sortAllOnCmpInv_byte) 1099 sorts.push(new test_SortDatabyte("sort()", just)) 1100 1101 let sort0 = sorts.at(0)! 1102 for (let s = 1; s < sorts.length; s++) { 1103 let sortS = sorts.at(s)! 1104 for (let i = 0; i < arr.length; i++) { 1105 if (sort0.arr[i] != sortS.arr[i]) { 1106 console.println(sort0.name + ': ') 1107 test_printArr(sort0.arr, 0, arr.length) 1108 console.println(sortS.name + ': ') 1109 test_printArr(sortS.arr, 0, arr.length) 1110 throw new Error("sorts est_sortAllOnCmpInv_byte are not equal: " + sort0.name + ' ' + sortS.name + ' for byte') 1111 } 1112 } 1113 } 1114 } 1115} 1116// ======== end of tests section ======== 1117 1118function bubbleSort(arr: short[], startIndex: int, endIndex: int): void { 1119 let was = true 1120 while (was) { 1121 was = false 1122 for (let i = startIndex; i < endIndex - 1; i++) { 1123 if ((arr[i + 1] < arr[i])) { 1124 const tmp = arr[i + 1] 1125 arr[i + 1] = arr[i] 1126 arr[i] = tmp 1127 was = true 1128 } 1129 } 1130 } 1131} 1132 1133function insertionSort(arr: short[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 1134 if (startIndex != initIndex) { 1135 // arr[startIndex - 1] exists and is less than or equal to all elements in range 1136 for (let i = startIndex + 1; i < endIndex; i++) { 1137 const tmp = arr[i] 1138 let pos = i 1139 while ((tmp < arr[pos - 1])) { 1140 arr[pos] = arr[pos - 1] 1141 pos-- 1142 } 1143 arr[pos] = tmp 1144 } 1145 return 1146 } 1147 for (let i = startIndex + 1; i < endIndex; i++) { 1148 const tmp = arr[i] 1149 if ((tmp < arr[startIndex])) { 1150 for (let j = i; j > startIndex; j--) { 1151 arr[j] = arr[j - 1] 1152 } 1153 arr[startIndex] = tmp 1154 } else { 1155 let pos = i 1156 while ((tmp < arr[pos - 1])) { 1157 arr[pos] = arr[pos - 1] 1158 pos-- 1159 } 1160 arr[pos] = tmp 1161 } 1162 } 1163} 1164function heapSortUp(arr: short[], idxFromStart: int, startIndex: int, heapRoot: int): void { 1165 const tmp = arr[startIndex + idxFromStart] 1166 while (startIndex + idxFromStart > heapRoot) { 1167 const p = (idxFromStart - 1) / 2 1168 if (!(arr[startIndex + p] < tmp)) { 1169 break 1170 } 1171 arr[startIndex + idxFromStart] = arr[startIndex + p] 1172 idxFromStart = p 1173 } 1174 arr[startIndex + idxFromStart] = tmp 1175} 1176 1177// Build max heap with root in startIndex given its children are roots of valid heaps 1178function heapSortDown(arr: short[], idxFromStart: int, startIndex: int, endIndex: int): void { 1179 let heapRoot = startIndex + idxFromStart 1180 let arrIndex = heapRoot 1181 let childIndex = startIndex + idxFromStart * 2 + 1 1182 const tmp = arr[arrIndex] 1183 // Walk heap to bottom and pull max child up on each level 1184 while (childIndex + 1 < endIndex) { 1185 if ((arr[childIndex] < arr[childIndex + 1])) { 1186 childIndex++ 1187 } 1188 arr[arrIndex] = arr[childIndex] 1189 arrIndex = childIndex 1190 childIndex = childIndex * 2 - startIndex + 1 1191 } 1192 if (childIndex < endIndex) { 1193 arr[arrIndex] = arr[childIndex] 1194 arrIndex = childIndex 1195 } 1196 arr[arrIndex] = tmp 1197 // Now heap is valid in all positions but arrIndex 1198 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 1199} 1200 1201export function heapSort(arr: short[], startIndex: int, endIndex: int): void { 1202 let len = endIndex - startIndex 1203 for (let i = len / 2 - 1; i >= 0; i--) { 1204 heapSortDown(arr, i, startIndex, endIndex) 1205 } 1206 1207 for (let i = endIndex - 1; i > startIndex; i--) { 1208 // move max element to the end of range 1209 const tmp = arr[i] 1210 arr[i] = arr[startIndex] 1211 arr[startIndex] = tmp 1212 heapSortDown(arr, 0, startIndex, i) 1213 } 1214} 1215 1216// Put median of three array elements to arr[index1] 1217function median3(arr: short[], index1: int, index2: int, index3: int): void { 1218 let swap_idx = index2 1219 if ((arr[index1] < arr[index2])) { 1220 if ((arr[index3] < arr[index1])) { 1221 return 1222 } 1223 if ((arr[index3] < arr[index2])) { 1224 swap_idx = index3 1225 } 1226 } else { 1227 if (!(arr[index3] < arr[index1])) { 1228 return 1229 } 1230 if ((arr[index2] < arr[index3])) { 1231 swap_idx = index3 1232 } 1233 } 1234 let tmp = arr[index1] 1235 arr[index1] = arr[swap_idx] 1236 arr[swap_idx] = tmp 1237} 1238 1239// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 1240// Elements equal to pivot go to the right 1241function quickSortSplit(arr: short[], startIndex: int, endIndex: int): int { 1242 const pivot = arr[startIndex] 1243 let i = startIndex + 1 1244 let j = endIndex - 1 1245 // No bounds check because pivot is median of three elements 1246 while ((arr[i] < pivot)) { 1247 i++ 1248 } 1249 if (i == startIndex + 1) { 1250 while (i < j && !(arr[j] < pivot)) { 1251 j-- 1252 } 1253 } else { 1254 while (!(arr[j] < pivot)) { 1255 j-- 1256 } 1257 } 1258 while (i < j) { 1259 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 1260 let tmp = arr[i] 1261 arr[i] = arr[j] 1262 arr[j] = tmp 1263 while ((arr[++i] < pivot)) {} 1264 while (!(arr[--j] < pivot)) {} 1265 } 1266 let pivotIndex = i - 1 1267 arr[startIndex] = arr[pivotIndex] 1268 arr[pivotIndex] = pivot 1269 1270 return pivotIndex 1271} 1272 1273// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 1274// Elements equal to pivot go to the left 1275function quickSortSplitLeft(arr: short[], startIndex: int, endIndex: int): int { 1276 const pivot = arr[startIndex] 1277 let i = startIndex + 1 1278 let j = endIndex - 1 1279 // No bounds check because pivot is median of three elements 1280 while ((pivot < arr[j])) { 1281 j-- 1282 } 1283 if (j + 1 == endIndex) { 1284 while (i < j && !(pivot < arr[i])) { 1285 i++ 1286 } 1287 } else { 1288 while (!(pivot < arr[i])) { 1289 i++ 1290 } 1291 } 1292 while (i < j) { 1293 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 1294 let tmp = arr[i] 1295 arr[i] = arr[j] 1296 arr[j] = tmp 1297 while (!(pivot < arr[++i])) {} 1298 while ((pivot < arr[--j])) {} 1299 } 1300 arr[startIndex] = arr[j] 1301 arr[j] = pivot 1302 1303 return j 1304} 1305 1306function quickSortImpl3(arr: short[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 1307 while (endIndex - startIndex > 3) { 1308 if (--maxDepth == 0) { 1309 heapSort(arr, startIndex, endIndex) 1310 return 1311 } 1312 // Here we assume that current interval is not the most left in the sorted range 1313 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 1314 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 1315 // after that only the right part needs to be sorted 1316 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 1317 // with many occurencies of the smallest element 1318 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 1319 continue 1320 } 1321 1322 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 1323 let p = quickSortSplit(arr, startIndex, endIndex) 1324 // make a call for the smaller part of array and continue processing the larger part in the loop 1325 if (p - startIndex < endIndex - p) { 1326 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 1327 startIndex = p + 1 1328 } else { 1329 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 1330 endIndex = p 1331 } 1332 } 1333 insertionSort(arr, startIndex, endIndex, initIndex) 1334} 1335function quickSortImpl40(arr: short[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 1336 while (endIndex - startIndex > 40) { 1337 if (--maxDepth == 0) { 1338 heapSort(arr, startIndex, endIndex) 1339 return 1340 } 1341 // Here we assume that current interval is not the most left in the sorted range 1342 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 1343 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 1344 // after that only the right part needs to be sorted 1345 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 1346 // with many occurencies of the smallest element 1347 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 1348 continue 1349 } 1350 1351 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 1352 let p = quickSortSplit(arr, startIndex, endIndex) 1353 // make a call for the smaller part of array and continue processing the larger part in the loop 1354 if (p - startIndex < endIndex - p) { 1355 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 1356 startIndex = p + 1 1357 } else { 1358 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 1359 endIndex = p 1360 } 1361 } 1362 insertionSort(arr, startIndex, endIndex, initIndex) 1363} 1364 1365function quickSort(arr: short[], startIndex: int, endIndex: int): void { 1366 let size = endIndex - startIndex 1367 if (size <= 1) { 1368 return 1369 } 1370 // find log of length to fall back into determenistic O(n logn) sort 1371 let bits = 32 1372 for (let i = 2; i < 31; i++) { 1373 if ((size >> i) == 0) { 1374 bits = i 1375 break 1376 } 1377 } 1378 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 1379} 1380 1381/** 1382 * sorts arr in-place 1383 * 1384 * @param arr an array to sort 1385 * 1386 * @param startIndex an index to start sorting with, inclusive 1387 * 1388 * @param endIndex an index to end sorting, exclusive 1389 * 1390 * @example: sort array arr 1391 * ``` 1392 * sort(arr, 0, arr.length) 1393 * ``` 1394 */ 1395export function sort(arr: short[], startIndex: int, endIndex: int): void { 1396 if (!checkRange(arr.length, startIndex, endIndex)) { 1397 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 1398 } 1399 1400 quickSort(arr, startIndex, endIndex); 1401} 1402 1403function bubbleSort(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1404 let was = true 1405 while (was) { 1406 was = false 1407 for (let i = startIndex; i < endIndex - 1; i++) { 1408 if (mustPrecede(arr[i + 1], arr[i])) { 1409 const tmp = arr[i + 1] 1410 arr[i + 1] = arr[i] 1411 arr[i] = tmp 1412 was = true 1413 } 1414 } 1415 } 1416} 1417 1418function insertionSort(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean, initIndex: int = startIndex): void { 1419 if (startIndex != initIndex) { 1420 // arr[startIndex - 1] exists and is less than or equal to all elements in range 1421 for (let i = startIndex + 1; i < endIndex; i++) { 1422 const tmp = arr[i] 1423 let pos = i 1424 while (mustPrecede(tmp, arr[pos - 1])) { 1425 arr[pos] = arr[pos - 1] 1426 pos-- 1427 } 1428 arr[pos] = tmp 1429 } 1430 return 1431 } 1432 for (let i = startIndex + 1; i < endIndex; i++) { 1433 const tmp = arr[i] 1434 if (mustPrecede(tmp, arr[startIndex])) { 1435 for (let j = i; j > startIndex; j--) { 1436 arr[j] = arr[j - 1] 1437 } 1438 arr[startIndex] = tmp 1439 } else { 1440 let pos = i 1441 while (mustPrecede(tmp, arr[pos - 1])) { 1442 arr[pos] = arr[pos - 1] 1443 pos-- 1444 } 1445 arr[pos] = tmp 1446 } 1447 } 1448} 1449function heapSortUp(arr: short[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1450 const tmp = arr[startIndex + idxFromStart] 1451 while (startIndex + idxFromStart > heapRoot) { 1452 const p = (idxFromStart - 1) / 2 1453 if (!mustPrecede(arr[startIndex + p], tmp)) { 1454 break 1455 } 1456 arr[startIndex + idxFromStart] = arr[startIndex + p] 1457 idxFromStart = p 1458 } 1459 arr[startIndex + idxFromStart] = tmp 1460} 1461 1462// Build max heap with root in startIndex given its children are roots of valid heaps 1463function heapSortDown(arr: short[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1464 let heapRoot = startIndex + idxFromStart 1465 let arrIndex = heapRoot 1466 let childIndex = startIndex + idxFromStart * 2 + 1 1467 const tmp = arr[arrIndex] 1468 // Walk heap to bottom and pull max child up on each level 1469 while (childIndex + 1 < endIndex) { 1470 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 1471 childIndex++ 1472 } 1473 arr[arrIndex] = arr[childIndex] 1474 arrIndex = childIndex 1475 childIndex = childIndex * 2 - startIndex + 1 1476 } 1477 if (childIndex < endIndex) { 1478 arr[arrIndex] = arr[childIndex] 1479 arrIndex = childIndex 1480 } 1481 arr[arrIndex] = tmp 1482 // Now heap is valid in all positions but arrIndex 1483 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 1484} 1485 1486export function heapSort(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1487 let len = endIndex - startIndex 1488 for (let i = len / 2 - 1; i >= 0; i--) { 1489 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 1490 } 1491 1492 for (let i = endIndex - 1; i > startIndex; i--) { 1493 // move max element to the end of range 1494 const tmp = arr[i] 1495 arr[i] = arr[startIndex] 1496 arr[startIndex] = tmp 1497 heapSortDown(arr, 0, startIndex, i, mustPrecede) 1498 } 1499} 1500 1501// Put median of three array elements to arr[index1] 1502function median3(arr: short[], index1: int, index2: int, index3: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1503 let swap_idx = index2 1504 if (mustPrecede(arr[index1], arr[index2])) { 1505 if (mustPrecede(arr[index3], arr[index1])) { 1506 return 1507 } 1508 if (mustPrecede(arr[index3], arr[index2])) { 1509 swap_idx = index3 1510 } 1511 } else { 1512 if (!mustPrecede(arr[index3], arr[index1])) { 1513 return 1514 } 1515 if (mustPrecede(arr[index2], arr[index3])) { 1516 swap_idx = index3 1517 } 1518 } 1519 let tmp = arr[index1] 1520 arr[index1] = arr[swap_idx] 1521 arr[swap_idx] = tmp 1522} 1523 1524// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 1525// Elements equal to pivot go to the right 1526function quickSortSplit(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): int { 1527 const pivot = arr[startIndex] 1528 let i = startIndex + 1 1529 let j = endIndex - 1 1530 // No bounds check because pivot is median of three elements 1531 while (mustPrecede(arr[i], pivot)) { 1532 i++ 1533 } 1534 if (i == startIndex + 1) { 1535 while (i < j && !mustPrecede(arr[j], pivot)) { 1536 j-- 1537 } 1538 } else { 1539 while (!mustPrecede(arr[j], pivot)) { 1540 j-- 1541 } 1542 } 1543 while (i < j) { 1544 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 1545 let tmp = arr[i] 1546 arr[i] = arr[j] 1547 arr[j] = tmp 1548 while (mustPrecede(arr[++i], pivot)) {} 1549 while (!mustPrecede(arr[--j], pivot)) {} 1550 } 1551 let pivotIndex = i - 1 1552 arr[startIndex] = arr[pivotIndex] 1553 arr[pivotIndex] = pivot 1554 1555 return pivotIndex 1556} 1557 1558// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 1559// Elements equal to pivot go to the left 1560function quickSortSplitLeft(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): int { 1561 const pivot = arr[startIndex] 1562 let i = startIndex + 1 1563 let j = endIndex - 1 1564 // No bounds check because pivot is median of three elements 1565 while (mustPrecede(pivot, arr[j])) { 1566 j-- 1567 } 1568 if (j + 1 == endIndex) { 1569 while (i < j && !mustPrecede(pivot, arr[i])) { 1570 i++ 1571 } 1572 } else { 1573 while (!mustPrecede(pivot, arr[i])) { 1574 i++ 1575 } 1576 } 1577 while (i < j) { 1578 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 1579 let tmp = arr[i] 1580 arr[i] = arr[j] 1581 arr[j] = tmp 1582 while (!mustPrecede(pivot, arr[++i])) {} 1583 while (mustPrecede(pivot, arr[--j])) {} 1584 } 1585 arr[startIndex] = arr[j] 1586 arr[j] = pivot 1587 1588 return j 1589} 1590 1591function quickSortImpl3(arr: short[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1592 while (endIndex - startIndex > 3) { 1593 if (--maxDepth == 0) { 1594 heapSort(arr, startIndex, endIndex, mustPrecede) 1595 return 1596 } 1597 1598 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 1599 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 1600 // make a call for the smaller part of array and continue processing the larger part in the loop 1601 if (p - startIndex < endIndex - p) { 1602 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 1603 startIndex = p + 1 1604 } else { 1605 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 1606 endIndex = p 1607 } 1608 } 1609 insertionSort(arr, startIndex, endIndex, mustPrecede) 1610} 1611function quickSortImpl40(arr: short[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1612 while (endIndex - startIndex > 40) { 1613 if (--maxDepth == 0) { 1614 heapSort(arr, startIndex, endIndex, mustPrecede) 1615 return 1616 } 1617 1618 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 1619 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 1620 // make a call for the smaller part of array and continue processing the larger part in the loop 1621 if (p - startIndex < endIndex - p) { 1622 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 1623 startIndex = p + 1 1624 } else { 1625 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 1626 endIndex = p 1627 } 1628 } 1629 insertionSort(arr, startIndex, endIndex, mustPrecede) 1630} 1631 1632function quickSort(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1633 let size = endIndex - startIndex 1634 if (size <= 1) { 1635 return 1636 } 1637 // find log of length to fall back into determenistic O(n logn) sort 1638 let bits = 32 1639 for (let i = 2; i < 31; i++) { 1640 if ((size >> i) == 0) { 1641 bits = i 1642 break 1643 } 1644 } 1645 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 1646} 1647 1648/** 1649 * sorts arr in-place 1650 * 1651 * @param arr an array to sort 1652 * 1653 * @param startIndex an index to start sorting with, inclusive 1654 * 1655 * @param endIndex an index to end sorting, exclusive 1656 * 1657 * @example: sort array arr 1658 * ``` 1659 * sort(arr, 0, arr.length) 1660 * ``` 1661 */ 1662export function sort_subarray(arr: short[], startIndex: int, endIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1663 if (!checkRange(arr.length, startIndex, endIndex)) { 1664 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 1665 } 1666 1667 quickSort(arr, startIndex, endIndex, mustPrecede); 1668} 1669 1670/** 1671 * sorts arr in-place 1672 * 1673 * @param arr an array to sort 1674 */ 1675export function sort_subarray(arr: short[], mustPrecede: (lhs: short, rhs: short) => boolean): void { 1676 sort_subarray(arr, 0, arr.length, mustPrecede); 1677} 1678 1679export function sort_subarray(arr: short[], startIndex: int, mustPrecede: (lhs: short, rhs: short) => boolean): void { 1680 sort_subarray(arr, startIndex, arr.length, mustPrecede) 1681} 1682 1683export function sort(arr: short[], startIndex: int): void { 1684 sort(arr, startIndex, arr.length) 1685} 1686 1687// ======== tests section ======== 1688 1689/** note: used for tests, {@link test_sortAllOn} */ 1690class test_SortDatashort { 1691 name: string 1692 arr: short[] 1693 1694 constructor(name: string, arr: short[]) { 1695 this.name = name 1696 this.arr = arr 1697 } 1698} 1699 1700/** note: used for tests, {@link test_sortAllOn} */ 1701function test_sortCopy(arr: short[]): short[] { 1702 let c = new short[arr.length] 1703 for (let i = 0; i < arr.length; i++) { 1704 c[i] = arr[i] 1705 } 1706 return c 1707} 1708 1709/** note: used for tests, {@link test_sortAllOn} */ 1710function test_sortAllOnCmpFwd_short(l: short, r: short): boolean { 1711 return l < r 1712} 1713 1714/** note: used for tests, {@link test_sortAllOn} */ 1715function test_sortAllOnCmpInv_short(l: short, r: short): boolean { 1716 return r < l 1717} 1718 1719/** note: used for tests, {@link test_sortAllOn} */ 1720function test_printArr(arr: short[], startIndex: int, endIndex: int) { 1721 for (let i = startIndex; i < endIndex; i++) { 1722 console.print(arr[i] + ' ') 1723 } 1724 console.println('') 1725} 1726 1727/** 1728 * Function used to test sorting in standard library tests. 1729 * There is only one exported function: `sort`, but for testing 1730 * we need access to all sub sorts that are used. Hence this part of "tests" 1731 * is located here. All related entities are prefixed whith `test_` to stand out 1732 */ 1733export function test_sortAllOn(arr: short[]) { 1734 // block for comparator `` 1735 if (true) { 1736 const sorts = new Array<test_SortDatashort>() 1737 const bubbled = test_sortCopy(arr) 1738 bubbleSort(bubbled, 0, bubbled.length) 1739 sorts.push(new test_SortDatashort("bubble", bubbled)) 1740 1741 const insertion = test_sortCopy(arr) 1742 insertionSort(insertion, 0, insertion.length) 1743 sorts.push(new test_SortDatashort("insertion", insertion)) 1744 1745 const heaped = test_sortCopy(arr) 1746 heapSort(heaped, 0, heaped.length) 1747 sorts.push(new test_SortDatashort("heap", heaped)) 1748 1749 const quicked = test_sortCopy(arr) 1750 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 1751 sorts.push(new test_SortDatashort("quick", quicked)) 1752 1753 const quickedInsertion = test_sortCopy(arr) 1754 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 1755 sorts.push(new test_SortDatashort("quick with insertion", quickedInsertion)) 1756 1757 const just = test_sortCopy(arr) 1758 sort(just) 1759 sorts.push(new test_SortDatashort("sort()", just)) 1760 1761 let sort0 = sorts.at(0)! 1762 for (let s = 1; s < sorts.length; s++) { 1763 let sortS = sorts.at(s)! 1764 for (let i = 0; i < arr.length; i++) { 1765 if (sort0.arr[i] != sortS.arr[i]) { 1766 console.println(sort0.name + ': ') 1767 test_printArr(sort0.arr, 0, arr.length) 1768 console.println(sortS.name + ': ') 1769 test_printArr(sortS.arr, 0, arr.length) 1770 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for short') 1771 } 1772 } 1773 } 1774 } 1775 // block for comparator `est_sortAllOnCmpFwd_short` 1776 if (true) { 1777 const sorts = new Array<test_SortDatashort>() 1778 const bubbled = test_sortCopy(arr) 1779 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_short) 1780 sorts.push(new test_SortDatashort("bubble", bubbled)) 1781 1782 const insertion = test_sortCopy(arr) 1783 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_short) 1784 sorts.push(new test_SortDatashort("insertion", insertion)) 1785 1786 const heaped = test_sortCopy(arr) 1787 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_short) 1788 sorts.push(new test_SortDatashort("heap", heaped)) 1789 1790 const quicked = test_sortCopy(arr) 1791 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_short) 1792 sorts.push(new test_SortDatashort("quick", quicked)) 1793 1794 const quickedInsertion = test_sortCopy(arr) 1795 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_short) 1796 sorts.push(new test_SortDatashort("quick with insertion", quickedInsertion)) 1797 1798 const just = test_sortCopy(arr) 1799 sort_subarray(just, test_sortAllOnCmpFwd_short) 1800 sorts.push(new test_SortDatashort("sort()", just)) 1801 1802 let sort0 = sorts.at(0)! 1803 for (let s = 1; s < sorts.length; s++) { 1804 let sortS = sorts.at(s)! 1805 for (let i = 0; i < arr.length; i++) { 1806 if (sort0.arr[i] != sortS.arr[i]) { 1807 console.println(sort0.name + ': ') 1808 test_printArr(sort0.arr, 0, arr.length) 1809 console.println(sortS.name + ': ') 1810 test_printArr(sortS.arr, 0, arr.length) 1811 throw new Error("sorts est_sortAllOnCmpFwd_short are not equal: " + sort0.name + ' ' + sortS.name + ' for short') 1812 } 1813 } 1814 } 1815 } 1816 // block for comparator `est_sortAllOnCmpInv_short` 1817 if (true) { 1818 const sorts = new Array<test_SortDatashort>() 1819 const bubbled = test_sortCopy(arr) 1820 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_short) 1821 sorts.push(new test_SortDatashort("bubble", bubbled)) 1822 1823 const insertion = test_sortCopy(arr) 1824 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_short) 1825 sorts.push(new test_SortDatashort("insertion", insertion)) 1826 1827 const heaped = test_sortCopy(arr) 1828 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_short) 1829 sorts.push(new test_SortDatashort("heap", heaped)) 1830 1831 const quicked = test_sortCopy(arr) 1832 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_short) 1833 sorts.push(new test_SortDatashort("quick", quicked)) 1834 1835 const quickedInsertion = test_sortCopy(arr) 1836 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_short) 1837 sorts.push(new test_SortDatashort("quick with insertion", quickedInsertion)) 1838 1839 const just = test_sortCopy(arr) 1840 sort_subarray(just, test_sortAllOnCmpInv_short) 1841 sorts.push(new test_SortDatashort("sort()", just)) 1842 1843 let sort0 = sorts.at(0)! 1844 for (let s = 1; s < sorts.length; s++) { 1845 let sortS = sorts.at(s)! 1846 for (let i = 0; i < arr.length; i++) { 1847 if (sort0.arr[i] != sortS.arr[i]) { 1848 console.println(sort0.name + ': ') 1849 test_printArr(sort0.arr, 0, arr.length) 1850 console.println(sortS.name + ': ') 1851 test_printArr(sortS.arr, 0, arr.length) 1852 throw new Error("sorts est_sortAllOnCmpInv_short are not equal: " + sort0.name + ' ' + sortS.name + ' for short') 1853 } 1854 } 1855 } 1856 } 1857} 1858// ======== end of tests section ======== 1859 1860function bubbleSort(arr: int[], startIndex: int, endIndex: int): void { 1861 let was = true 1862 while (was) { 1863 was = false 1864 for (let i = startIndex; i < endIndex - 1; i++) { 1865 if ((arr[i + 1] < arr[i])) { 1866 const tmp = arr[i + 1] 1867 arr[i + 1] = arr[i] 1868 arr[i] = tmp 1869 was = true 1870 } 1871 } 1872 } 1873} 1874 1875function insertionSort(arr: int[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 1876 if (startIndex != initIndex) { 1877 // arr[startIndex - 1] exists and is less than or equal to all elements in range 1878 for (let i = startIndex + 1; i < endIndex; i++) { 1879 const tmp = arr[i] 1880 let pos = i 1881 while ((tmp < arr[pos - 1])) { 1882 arr[pos] = arr[pos - 1] 1883 pos-- 1884 } 1885 arr[pos] = tmp 1886 } 1887 return 1888 } 1889 for (let i = startIndex + 1; i < endIndex; i++) { 1890 const tmp = arr[i] 1891 if ((tmp < arr[startIndex])) { 1892 for (let j = i; j > startIndex; j--) { 1893 arr[j] = arr[j - 1] 1894 } 1895 arr[startIndex] = tmp 1896 } else { 1897 let pos = i 1898 while ((tmp < arr[pos - 1])) { 1899 arr[pos] = arr[pos - 1] 1900 pos-- 1901 } 1902 arr[pos] = tmp 1903 } 1904 } 1905} 1906function heapSortUp(arr: int[], idxFromStart: int, startIndex: int, heapRoot: int): void { 1907 const tmp = arr[startIndex + idxFromStart] 1908 while (startIndex + idxFromStart > heapRoot) { 1909 const p = (idxFromStart - 1) / 2 1910 if (!(arr[startIndex + p] < tmp)) { 1911 break 1912 } 1913 arr[startIndex + idxFromStart] = arr[startIndex + p] 1914 idxFromStart = p 1915 } 1916 arr[startIndex + idxFromStart] = tmp 1917} 1918 1919// Build max heap with root in startIndex given its children are roots of valid heaps 1920function heapSortDown(arr: int[], idxFromStart: int, startIndex: int, endIndex: int): void { 1921 let heapRoot = startIndex + idxFromStart 1922 let arrIndex = heapRoot 1923 let childIndex = startIndex + idxFromStart * 2 + 1 1924 const tmp = arr[arrIndex] 1925 // Walk heap to bottom and pull max child up on each level 1926 while (childIndex + 1 < endIndex) { 1927 if ((arr[childIndex] < arr[childIndex + 1])) { 1928 childIndex++ 1929 } 1930 arr[arrIndex] = arr[childIndex] 1931 arrIndex = childIndex 1932 childIndex = childIndex * 2 - startIndex + 1 1933 } 1934 if (childIndex < endIndex) { 1935 arr[arrIndex] = arr[childIndex] 1936 arrIndex = childIndex 1937 } 1938 arr[arrIndex] = tmp 1939 // Now heap is valid in all positions but arrIndex 1940 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 1941} 1942 1943export function heapSort(arr: int[], startIndex: int, endIndex: int): void { 1944 let len = endIndex - startIndex 1945 for (let i = len / 2 - 1; i >= 0; i--) { 1946 heapSortDown(arr, i, startIndex, endIndex) 1947 } 1948 1949 for (let i = endIndex - 1; i > startIndex; i--) { 1950 // move max element to the end of range 1951 const tmp = arr[i] 1952 arr[i] = arr[startIndex] 1953 arr[startIndex] = tmp 1954 heapSortDown(arr, 0, startIndex, i) 1955 } 1956} 1957 1958// Put median of three array elements to arr[index1] 1959function median3(arr: int[], index1: int, index2: int, index3: int): void { 1960 let swap_idx = index2 1961 if ((arr[index1] < arr[index2])) { 1962 if ((arr[index3] < arr[index1])) { 1963 return 1964 } 1965 if ((arr[index3] < arr[index2])) { 1966 swap_idx = index3 1967 } 1968 } else { 1969 if (!(arr[index3] < arr[index1])) { 1970 return 1971 } 1972 if ((arr[index2] < arr[index3])) { 1973 swap_idx = index3 1974 } 1975 } 1976 let tmp = arr[index1] 1977 arr[index1] = arr[swap_idx] 1978 arr[swap_idx] = tmp 1979} 1980 1981// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 1982// Elements equal to pivot go to the right 1983function quickSortSplit(arr: int[], startIndex: int, endIndex: int): int { 1984 const pivot = arr[startIndex] 1985 let i = startIndex + 1 1986 let j = endIndex - 1 1987 // No bounds check because pivot is median of three elements 1988 while ((arr[i] < pivot)) { 1989 i++ 1990 } 1991 if (i == startIndex + 1) { 1992 while (i < j && !(arr[j] < pivot)) { 1993 j-- 1994 } 1995 } else { 1996 while (!(arr[j] < pivot)) { 1997 j-- 1998 } 1999 } 2000 while (i < j) { 2001 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 2002 let tmp = arr[i] 2003 arr[i] = arr[j] 2004 arr[j] = tmp 2005 while ((arr[++i] < pivot)) {} 2006 while (!(arr[--j] < pivot)) {} 2007 } 2008 let pivotIndex = i - 1 2009 arr[startIndex] = arr[pivotIndex] 2010 arr[pivotIndex] = pivot 2011 2012 return pivotIndex 2013} 2014 2015// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 2016// Elements equal to pivot go to the left 2017function quickSortSplitLeft(arr: int[], startIndex: int, endIndex: int): int { 2018 const pivot = arr[startIndex] 2019 let i = startIndex + 1 2020 let j = endIndex - 1 2021 // No bounds check because pivot is median of three elements 2022 while ((pivot < arr[j])) { 2023 j-- 2024 } 2025 if (j + 1 == endIndex) { 2026 while (i < j && !(pivot < arr[i])) { 2027 i++ 2028 } 2029 } else { 2030 while (!(pivot < arr[i])) { 2031 i++ 2032 } 2033 } 2034 while (i < j) { 2035 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 2036 let tmp = arr[i] 2037 arr[i] = arr[j] 2038 arr[j] = tmp 2039 while (!(pivot < arr[++i])) {} 2040 while ((pivot < arr[--j])) {} 2041 } 2042 arr[startIndex] = arr[j] 2043 arr[j] = pivot 2044 2045 return j 2046} 2047 2048function quickSortImpl3(arr: int[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 2049 while (endIndex - startIndex > 3) { 2050 if (--maxDepth == 0) { 2051 heapSort(arr, startIndex, endIndex) 2052 return 2053 } 2054 // Here we assume that current interval is not the most left in the sorted range 2055 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 2056 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 2057 // after that only the right part needs to be sorted 2058 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 2059 // with many occurencies of the smallest element 2060 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 2061 continue 2062 } 2063 2064 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 2065 let p = quickSortSplit(arr, startIndex, endIndex) 2066 // make a call for the smaller part of array and continue processing the larger part in the loop 2067 if (p - startIndex < endIndex - p) { 2068 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 2069 startIndex = p + 1 2070 } else { 2071 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 2072 endIndex = p 2073 } 2074 } 2075 insertionSort(arr, startIndex, endIndex, initIndex) 2076} 2077function quickSortImpl40(arr: int[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 2078 while (endIndex - startIndex > 40) { 2079 if (--maxDepth == 0) { 2080 heapSort(arr, startIndex, endIndex) 2081 return 2082 } 2083 // Here we assume that current interval is not the most left in the sorted range 2084 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 2085 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 2086 // after that only the right part needs to be sorted 2087 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 2088 // with many occurencies of the smallest element 2089 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 2090 continue 2091 } 2092 2093 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 2094 let p = quickSortSplit(arr, startIndex, endIndex) 2095 // make a call for the smaller part of array and continue processing the larger part in the loop 2096 if (p - startIndex < endIndex - p) { 2097 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 2098 startIndex = p + 1 2099 } else { 2100 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 2101 endIndex = p 2102 } 2103 } 2104 insertionSort(arr, startIndex, endIndex, initIndex) 2105} 2106 2107function quickSort(arr: int[], startIndex: int, endIndex: int): void { 2108 let size = endIndex - startIndex 2109 if (size <= 1) { 2110 return 2111 } 2112 // find log of length to fall back into determenistic O(n logn) sort 2113 let bits = 32 2114 for (let i = 2; i < 31; i++) { 2115 if ((size >> i) == 0) { 2116 bits = i 2117 break 2118 } 2119 } 2120 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 2121} 2122 2123/** 2124 * sorts arr in-place 2125 * 2126 * @param arr an array to sort 2127 * 2128 * @param startIndex an index to start sorting with, inclusive 2129 * 2130 * @param endIndex an index to end sorting, exclusive 2131 * 2132 * @example: sort array arr 2133 * ``` 2134 * sort(arr, 0, arr.length) 2135 * ``` 2136 */ 2137export function sort(arr: int[], startIndex: int, endIndex: int): void { 2138 if (!checkRange(arr.length, startIndex, endIndex)) { 2139 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 2140 } 2141 2142 quickSort(arr, startIndex, endIndex); 2143} 2144 2145function bubbleSort(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2146 let was = true 2147 while (was) { 2148 was = false 2149 for (let i = startIndex; i < endIndex - 1; i++) { 2150 if (mustPrecede(arr[i + 1], arr[i])) { 2151 const tmp = arr[i + 1] 2152 arr[i + 1] = arr[i] 2153 arr[i] = tmp 2154 was = true 2155 } 2156 } 2157 } 2158} 2159 2160function insertionSort(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean, initIndex: int = startIndex): void { 2161 if (startIndex != initIndex) { 2162 // arr[startIndex - 1] exists and is less than or equal to all elements in range 2163 for (let i = startIndex + 1; i < endIndex; i++) { 2164 const tmp = arr[i] 2165 let pos = i 2166 while (mustPrecede(tmp, arr[pos - 1])) { 2167 arr[pos] = arr[pos - 1] 2168 pos-- 2169 } 2170 arr[pos] = tmp 2171 } 2172 return 2173 } 2174 for (let i = startIndex + 1; i < endIndex; i++) { 2175 const tmp = arr[i] 2176 if (mustPrecede(tmp, arr[startIndex])) { 2177 for (let j = i; j > startIndex; j--) { 2178 arr[j] = arr[j - 1] 2179 } 2180 arr[startIndex] = tmp 2181 } else { 2182 let pos = i 2183 while (mustPrecede(tmp, arr[pos - 1])) { 2184 arr[pos] = arr[pos - 1] 2185 pos-- 2186 } 2187 arr[pos] = tmp 2188 } 2189 } 2190} 2191function heapSortUp(arr: int[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2192 const tmp = arr[startIndex + idxFromStart] 2193 while (startIndex + idxFromStart > heapRoot) { 2194 const p = (idxFromStart - 1) / 2 2195 if (!mustPrecede(arr[startIndex + p], tmp)) { 2196 break 2197 } 2198 arr[startIndex + idxFromStart] = arr[startIndex + p] 2199 idxFromStart = p 2200 } 2201 arr[startIndex + idxFromStart] = tmp 2202} 2203 2204// Build max heap with root in startIndex given its children are roots of valid heaps 2205function heapSortDown(arr: int[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2206 let heapRoot = startIndex + idxFromStart 2207 let arrIndex = heapRoot 2208 let childIndex = startIndex + idxFromStart * 2 + 1 2209 const tmp = arr[arrIndex] 2210 // Walk heap to bottom and pull max child up on each level 2211 while (childIndex + 1 < endIndex) { 2212 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 2213 childIndex++ 2214 } 2215 arr[arrIndex] = arr[childIndex] 2216 arrIndex = childIndex 2217 childIndex = childIndex * 2 - startIndex + 1 2218 } 2219 if (childIndex < endIndex) { 2220 arr[arrIndex] = arr[childIndex] 2221 arrIndex = childIndex 2222 } 2223 arr[arrIndex] = tmp 2224 // Now heap is valid in all positions but arrIndex 2225 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 2226} 2227 2228export function heapSort(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2229 let len = endIndex - startIndex 2230 for (let i = len / 2 - 1; i >= 0; i--) { 2231 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 2232 } 2233 2234 for (let i = endIndex - 1; i > startIndex; i--) { 2235 // move max element to the end of range 2236 const tmp = arr[i] 2237 arr[i] = arr[startIndex] 2238 arr[startIndex] = tmp 2239 heapSortDown(arr, 0, startIndex, i, mustPrecede) 2240 } 2241} 2242 2243// Put median of three array elements to arr[index1] 2244function median3(arr: int[], index1: int, index2: int, index3: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2245 let swap_idx = index2 2246 if (mustPrecede(arr[index1], arr[index2])) { 2247 if (mustPrecede(arr[index3], arr[index1])) { 2248 return 2249 } 2250 if (mustPrecede(arr[index3], arr[index2])) { 2251 swap_idx = index3 2252 } 2253 } else { 2254 if (!mustPrecede(arr[index3], arr[index1])) { 2255 return 2256 } 2257 if (mustPrecede(arr[index2], arr[index3])) { 2258 swap_idx = index3 2259 } 2260 } 2261 let tmp = arr[index1] 2262 arr[index1] = arr[swap_idx] 2263 arr[swap_idx] = tmp 2264} 2265 2266// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 2267// Elements equal to pivot go to the right 2268function quickSortSplit(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): int { 2269 const pivot = arr[startIndex] 2270 let i = startIndex + 1 2271 let j = endIndex - 1 2272 // No bounds check because pivot is median of three elements 2273 while (mustPrecede(arr[i], pivot)) { 2274 i++ 2275 } 2276 if (i == startIndex + 1) { 2277 while (i < j && !mustPrecede(arr[j], pivot)) { 2278 j-- 2279 } 2280 } else { 2281 while (!mustPrecede(arr[j], pivot)) { 2282 j-- 2283 } 2284 } 2285 while (i < j) { 2286 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 2287 let tmp = arr[i] 2288 arr[i] = arr[j] 2289 arr[j] = tmp 2290 while (mustPrecede(arr[++i], pivot)) {} 2291 while (!mustPrecede(arr[--j], pivot)) {} 2292 } 2293 let pivotIndex = i - 1 2294 arr[startIndex] = arr[pivotIndex] 2295 arr[pivotIndex] = pivot 2296 2297 return pivotIndex 2298} 2299 2300// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 2301// Elements equal to pivot go to the left 2302function quickSortSplitLeft(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): int { 2303 const pivot = arr[startIndex] 2304 let i = startIndex + 1 2305 let j = endIndex - 1 2306 // No bounds check because pivot is median of three elements 2307 while (mustPrecede(pivot, arr[j])) { 2308 j-- 2309 } 2310 if (j + 1 == endIndex) { 2311 while (i < j && !mustPrecede(pivot, arr[i])) { 2312 i++ 2313 } 2314 } else { 2315 while (!mustPrecede(pivot, arr[i])) { 2316 i++ 2317 } 2318 } 2319 while (i < j) { 2320 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 2321 let tmp = arr[i] 2322 arr[i] = arr[j] 2323 arr[j] = tmp 2324 while (!mustPrecede(pivot, arr[++i])) {} 2325 while (mustPrecede(pivot, arr[--j])) {} 2326 } 2327 arr[startIndex] = arr[j] 2328 arr[j] = pivot 2329 2330 return j 2331} 2332 2333function quickSortImpl3(arr: int[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2334 while (endIndex - startIndex > 3) { 2335 if (--maxDepth == 0) { 2336 heapSort(arr, startIndex, endIndex, mustPrecede) 2337 return 2338 } 2339 2340 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 2341 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 2342 // make a call for the smaller part of array and continue processing the larger part in the loop 2343 if (p - startIndex < endIndex - p) { 2344 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 2345 startIndex = p + 1 2346 } else { 2347 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 2348 endIndex = p 2349 } 2350 } 2351 insertionSort(arr, startIndex, endIndex, mustPrecede) 2352} 2353function quickSortImpl40(arr: int[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2354 while (endIndex - startIndex > 40) { 2355 if (--maxDepth == 0) { 2356 heapSort(arr, startIndex, endIndex, mustPrecede) 2357 return 2358 } 2359 2360 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 2361 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 2362 // make a call for the smaller part of array and continue processing the larger part in the loop 2363 if (p - startIndex < endIndex - p) { 2364 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 2365 startIndex = p + 1 2366 } else { 2367 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 2368 endIndex = p 2369 } 2370 } 2371 insertionSort(arr, startIndex, endIndex, mustPrecede) 2372} 2373 2374function quickSort(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2375 let size = endIndex - startIndex 2376 if (size <= 1) { 2377 return 2378 } 2379 // find log of length to fall back into determenistic O(n logn) sort 2380 let bits = 32 2381 for (let i = 2; i < 31; i++) { 2382 if ((size >> i) == 0) { 2383 bits = i 2384 break 2385 } 2386 } 2387 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 2388} 2389 2390/** 2391 * sorts arr in-place 2392 * 2393 * @param arr an array to sort 2394 * 2395 * @param startIndex an index to start sorting with, inclusive 2396 * 2397 * @param endIndex an index to end sorting, exclusive 2398 * 2399 * @example: sort array arr 2400 * ``` 2401 * sort(arr, 0, arr.length) 2402 * ``` 2403 */ 2404export function sort_subarray(arr: int[], startIndex: int, endIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2405 if (!checkRange(arr.length, startIndex, endIndex)) { 2406 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 2407 } 2408 2409 quickSort(arr, startIndex, endIndex, mustPrecede); 2410} 2411 2412/** 2413 * sorts arr in-place 2414 * 2415 * @param arr an array to sort 2416 */ 2417export function sort_subarray(arr: int[], mustPrecede: (lhs: int, rhs: int) => boolean): void { 2418 sort_subarray(arr, 0, arr.length, mustPrecede); 2419} 2420 2421export function sort_subarray(arr: int[], startIndex: int, mustPrecede: (lhs: int, rhs: int) => boolean): void { 2422 sort_subarray(arr, startIndex, arr.length, mustPrecede) 2423} 2424 2425export function sort(arr: int[], startIndex: int): void { 2426 sort(arr, startIndex, arr.length) 2427} 2428 2429// ======== tests section ======== 2430 2431/** note: used for tests, {@link test_sortAllOn} */ 2432class test_SortDataint { 2433 name: string 2434 arr: int[] 2435 2436 constructor(name: string, arr: int[]) { 2437 this.name = name 2438 this.arr = arr 2439 } 2440} 2441 2442/** note: used for tests, {@link test_sortAllOn} */ 2443function test_sortCopy(arr: int[]): int[] { 2444 let c = new int[arr.length] 2445 for (let i = 0; i < arr.length; i++) { 2446 c[i] = arr[i] 2447 } 2448 return c 2449} 2450 2451/** note: used for tests, {@link test_sortAllOn} */ 2452function test_sortAllOnCmpFwd_int(l: int, r: int): boolean { 2453 return l < r 2454} 2455 2456/** note: used for tests, {@link test_sortAllOn} */ 2457function test_sortAllOnCmpInv_int(l: int, r: int): boolean { 2458 return r < l 2459} 2460 2461/** note: used for tests, {@link test_sortAllOn} */ 2462function test_printArr(arr: int[], startIndex: int, endIndex: int) { 2463 for (let i = startIndex; i < endIndex; i++) { 2464 console.print(arr[i] + ' ') 2465 } 2466 console.println('') 2467} 2468 2469/** 2470 * Function used to test sorting in standard library tests. 2471 * There is only one exported function: `sort`, but for testing 2472 * we need access to all sub sorts that are used. Hence this part of "tests" 2473 * is located here. All related entities are prefixed whith `test_` to stand out 2474 */ 2475export function test_sortAllOn(arr: int[]) { 2476 // block for comparator `` 2477 if (true) { 2478 const sorts = new Array<test_SortDataint>() 2479 const bubbled = test_sortCopy(arr) 2480 bubbleSort(bubbled, 0, bubbled.length) 2481 sorts.push(new test_SortDataint("bubble", bubbled)) 2482 2483 const insertion = test_sortCopy(arr) 2484 insertionSort(insertion, 0, insertion.length) 2485 sorts.push(new test_SortDataint("insertion", insertion)) 2486 2487 const heaped = test_sortCopy(arr) 2488 heapSort(heaped, 0, heaped.length) 2489 sorts.push(new test_SortDataint("heap", heaped)) 2490 2491 const quicked = test_sortCopy(arr) 2492 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 2493 sorts.push(new test_SortDataint("quick", quicked)) 2494 2495 const quickedInsertion = test_sortCopy(arr) 2496 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 2497 sorts.push(new test_SortDataint("quick with insertion", quickedInsertion)) 2498 2499 const just = test_sortCopy(arr) 2500 sort(just) 2501 sorts.push(new test_SortDataint("sort()", just)) 2502 2503 let sort0 = sorts.at(0)! 2504 for (let s = 1; s < sorts.length; s++) { 2505 let sortS = sorts.at(s)! 2506 for (let i = 0; i < arr.length; i++) { 2507 if (sort0.arr[i] != sortS.arr[i]) { 2508 console.println(sort0.name + ': ') 2509 test_printArr(sort0.arr, 0, arr.length) 2510 console.println(sortS.name + ': ') 2511 test_printArr(sortS.arr, 0, arr.length) 2512 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for int') 2513 } 2514 } 2515 } 2516 } 2517 // block for comparator `est_sortAllOnCmpFwd_int` 2518 if (true) { 2519 const sorts = new Array<test_SortDataint>() 2520 const bubbled = test_sortCopy(arr) 2521 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_int) 2522 sorts.push(new test_SortDataint("bubble", bubbled)) 2523 2524 const insertion = test_sortCopy(arr) 2525 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_int) 2526 sorts.push(new test_SortDataint("insertion", insertion)) 2527 2528 const heaped = test_sortCopy(arr) 2529 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_int) 2530 sorts.push(new test_SortDataint("heap", heaped)) 2531 2532 const quicked = test_sortCopy(arr) 2533 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_int) 2534 sorts.push(new test_SortDataint("quick", quicked)) 2535 2536 const quickedInsertion = test_sortCopy(arr) 2537 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_int) 2538 sorts.push(new test_SortDataint("quick with insertion", quickedInsertion)) 2539 2540 const just = test_sortCopy(arr) 2541 sort_subarray(just, test_sortAllOnCmpFwd_int) 2542 sorts.push(new test_SortDataint("sort()", just)) 2543 2544 let sort0 = sorts.at(0)! 2545 for (let s = 1; s < sorts.length; s++) { 2546 let sortS = sorts.at(s)! 2547 for (let i = 0; i < arr.length; i++) { 2548 if (sort0.arr[i] != sortS.arr[i]) { 2549 console.println(sort0.name + ': ') 2550 test_printArr(sort0.arr, 0, arr.length) 2551 console.println(sortS.name + ': ') 2552 test_printArr(sortS.arr, 0, arr.length) 2553 throw new Error("sorts est_sortAllOnCmpFwd_int are not equal: " + sort0.name + ' ' + sortS.name + ' for int') 2554 } 2555 } 2556 } 2557 } 2558 // block for comparator `est_sortAllOnCmpInv_int` 2559 if (true) { 2560 const sorts = new Array<test_SortDataint>() 2561 const bubbled = test_sortCopy(arr) 2562 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_int) 2563 sorts.push(new test_SortDataint("bubble", bubbled)) 2564 2565 const insertion = test_sortCopy(arr) 2566 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_int) 2567 sorts.push(new test_SortDataint("insertion", insertion)) 2568 2569 const heaped = test_sortCopy(arr) 2570 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_int) 2571 sorts.push(new test_SortDataint("heap", heaped)) 2572 2573 const quicked = test_sortCopy(arr) 2574 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_int) 2575 sorts.push(new test_SortDataint("quick", quicked)) 2576 2577 const quickedInsertion = test_sortCopy(arr) 2578 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_int) 2579 sorts.push(new test_SortDataint("quick with insertion", quickedInsertion)) 2580 2581 const just = test_sortCopy(arr) 2582 sort_subarray(just, test_sortAllOnCmpInv_int) 2583 sorts.push(new test_SortDataint("sort()", just)) 2584 2585 let sort0 = sorts.at(0)! 2586 for (let s = 1; s < sorts.length; s++) { 2587 let sortS = sorts.at(s)! 2588 for (let i = 0; i < arr.length; i++) { 2589 if (sort0.arr[i] != sortS.arr[i]) { 2590 console.println(sort0.name + ': ') 2591 test_printArr(sort0.arr, 0, arr.length) 2592 console.println(sortS.name + ': ') 2593 test_printArr(sortS.arr, 0, arr.length) 2594 throw new Error("sorts est_sortAllOnCmpInv_int are not equal: " + sort0.name + ' ' + sortS.name + ' for int') 2595 } 2596 } 2597 } 2598 } 2599} 2600// ======== end of tests section ======== 2601 2602function bubbleSort(arr: long[], startIndex: int, endIndex: int): void { 2603 let was = true 2604 while (was) { 2605 was = false 2606 for (let i = startIndex; i < endIndex - 1; i++) { 2607 if ((arr[i + 1] < arr[i])) { 2608 const tmp = arr[i + 1] 2609 arr[i + 1] = arr[i] 2610 arr[i] = tmp 2611 was = true 2612 } 2613 } 2614 } 2615} 2616 2617function insertionSort(arr: long[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 2618 if (startIndex != initIndex) { 2619 // arr[startIndex - 1] exists and is less than or equal to all elements in range 2620 for (let i = startIndex + 1; i < endIndex; i++) { 2621 const tmp = arr[i] 2622 let pos = i 2623 while ((tmp < arr[pos - 1])) { 2624 arr[pos] = arr[pos - 1] 2625 pos-- 2626 } 2627 arr[pos] = tmp 2628 } 2629 return 2630 } 2631 for (let i = startIndex + 1; i < endIndex; i++) { 2632 const tmp = arr[i] 2633 if ((tmp < arr[startIndex])) { 2634 for (let j = i; j > startIndex; j--) { 2635 arr[j] = arr[j - 1] 2636 } 2637 arr[startIndex] = tmp 2638 } else { 2639 let pos = i 2640 while ((tmp < arr[pos - 1])) { 2641 arr[pos] = arr[pos - 1] 2642 pos-- 2643 } 2644 arr[pos] = tmp 2645 } 2646 } 2647} 2648function heapSortUp(arr: long[], idxFromStart: int, startIndex: int, heapRoot: int): void { 2649 const tmp = arr[startIndex + idxFromStart] 2650 while (startIndex + idxFromStart > heapRoot) { 2651 const p = (idxFromStart - 1) / 2 2652 if (!(arr[startIndex + p] < tmp)) { 2653 break 2654 } 2655 arr[startIndex + idxFromStart] = arr[startIndex + p] 2656 idxFromStart = p 2657 } 2658 arr[startIndex + idxFromStart] = tmp 2659} 2660 2661// Build max heap with root in startIndex given its children are roots of valid heaps 2662function heapSortDown(arr: long[], idxFromStart: int, startIndex: int, endIndex: int): void { 2663 let heapRoot = startIndex + idxFromStart 2664 let arrIndex = heapRoot 2665 let childIndex = startIndex + idxFromStart * 2 + 1 2666 const tmp = arr[arrIndex] 2667 // Walk heap to bottom and pull max child up on each level 2668 while (childIndex + 1 < endIndex) { 2669 if ((arr[childIndex] < arr[childIndex + 1])) { 2670 childIndex++ 2671 } 2672 arr[arrIndex] = arr[childIndex] 2673 arrIndex = childIndex 2674 childIndex = childIndex * 2 - startIndex + 1 2675 } 2676 if (childIndex < endIndex) { 2677 arr[arrIndex] = arr[childIndex] 2678 arrIndex = childIndex 2679 } 2680 arr[arrIndex] = tmp 2681 // Now heap is valid in all positions but arrIndex 2682 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 2683} 2684 2685export function heapSort(arr: long[], startIndex: int, endIndex: int): void { 2686 let len = endIndex - startIndex 2687 for (let i = len / 2 - 1; i >= 0; i--) { 2688 heapSortDown(arr, i, startIndex, endIndex) 2689 } 2690 2691 for (let i = endIndex - 1; i > startIndex; i--) { 2692 // move max element to the end of range 2693 const tmp = arr[i] 2694 arr[i] = arr[startIndex] 2695 arr[startIndex] = tmp 2696 heapSortDown(arr, 0, startIndex, i) 2697 } 2698} 2699 2700// Put median of three array elements to arr[index1] 2701function median3(arr: long[], index1: int, index2: int, index3: int): void { 2702 let swap_idx = index2 2703 if ((arr[index1] < arr[index2])) { 2704 if ((arr[index3] < arr[index1])) { 2705 return 2706 } 2707 if ((arr[index3] < arr[index2])) { 2708 swap_idx = index3 2709 } 2710 } else { 2711 if (!(arr[index3] < arr[index1])) { 2712 return 2713 } 2714 if ((arr[index2] < arr[index3])) { 2715 swap_idx = index3 2716 } 2717 } 2718 let tmp = arr[index1] 2719 arr[index1] = arr[swap_idx] 2720 arr[swap_idx] = tmp 2721} 2722 2723// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 2724// Elements equal to pivot go to the right 2725function quickSortSplit(arr: long[], startIndex: int, endIndex: int): int { 2726 const pivot = arr[startIndex] 2727 let i = startIndex + 1 2728 let j = endIndex - 1 2729 // No bounds check because pivot is median of three elements 2730 while ((arr[i] < pivot)) { 2731 i++ 2732 } 2733 if (i == startIndex + 1) { 2734 while (i < j && !(arr[j] < pivot)) { 2735 j-- 2736 } 2737 } else { 2738 while (!(arr[j] < pivot)) { 2739 j-- 2740 } 2741 } 2742 while (i < j) { 2743 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 2744 let tmp = arr[i] 2745 arr[i] = arr[j] 2746 arr[j] = tmp 2747 while ((arr[++i] < pivot)) {} 2748 while (!(arr[--j] < pivot)) {} 2749 } 2750 let pivotIndex = i - 1 2751 arr[startIndex] = arr[pivotIndex] 2752 arr[pivotIndex] = pivot 2753 2754 return pivotIndex 2755} 2756 2757// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 2758// Elements equal to pivot go to the left 2759function quickSortSplitLeft(arr: long[], startIndex: int, endIndex: int): int { 2760 const pivot = arr[startIndex] 2761 let i = startIndex + 1 2762 let j = endIndex - 1 2763 // No bounds check because pivot is median of three elements 2764 while ((pivot < arr[j])) { 2765 j-- 2766 } 2767 if (j + 1 == endIndex) { 2768 while (i < j && !(pivot < arr[i])) { 2769 i++ 2770 } 2771 } else { 2772 while (!(pivot < arr[i])) { 2773 i++ 2774 } 2775 } 2776 while (i < j) { 2777 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 2778 let tmp = arr[i] 2779 arr[i] = arr[j] 2780 arr[j] = tmp 2781 while (!(pivot < arr[++i])) {} 2782 while ((pivot < arr[--j])) {} 2783 } 2784 arr[startIndex] = arr[j] 2785 arr[j] = pivot 2786 2787 return j 2788} 2789 2790function quickSortImpl3(arr: long[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 2791 while (endIndex - startIndex > 3) { 2792 if (--maxDepth == 0) { 2793 heapSort(arr, startIndex, endIndex) 2794 return 2795 } 2796 // Here we assume that current interval is not the most left in the sorted range 2797 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 2798 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 2799 // after that only the right part needs to be sorted 2800 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 2801 // with many occurencies of the smallest element 2802 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 2803 continue 2804 } 2805 2806 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 2807 let p = quickSortSplit(arr, startIndex, endIndex) 2808 // make a call for the smaller part of array and continue processing the larger part in the loop 2809 if (p - startIndex < endIndex - p) { 2810 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 2811 startIndex = p + 1 2812 } else { 2813 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 2814 endIndex = p 2815 } 2816 } 2817 insertionSort(arr, startIndex, endIndex, initIndex) 2818} 2819function quickSortImpl40(arr: long[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 2820 while (endIndex - startIndex > 40) { 2821 if (--maxDepth == 0) { 2822 heapSort(arr, startIndex, endIndex) 2823 return 2824 } 2825 // Here we assume that current interval is not the most left in the sorted range 2826 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 2827 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 2828 // after that only the right part needs to be sorted 2829 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 2830 // with many occurencies of the smallest element 2831 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 2832 continue 2833 } 2834 2835 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 2836 let p = quickSortSplit(arr, startIndex, endIndex) 2837 // make a call for the smaller part of array and continue processing the larger part in the loop 2838 if (p - startIndex < endIndex - p) { 2839 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 2840 startIndex = p + 1 2841 } else { 2842 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 2843 endIndex = p 2844 } 2845 } 2846 insertionSort(arr, startIndex, endIndex, initIndex) 2847} 2848 2849function quickSort(arr: long[], startIndex: int, endIndex: int): void { 2850 let size = endIndex - startIndex 2851 if (size <= 1) { 2852 return 2853 } 2854 // find log of length to fall back into determenistic O(n logn) sort 2855 let bits = 32 2856 for (let i = 2; i < 31; i++) { 2857 if ((size >> i) == 0) { 2858 bits = i 2859 break 2860 } 2861 } 2862 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 2863} 2864 2865/** 2866 * sorts arr in-place 2867 * 2868 * @param arr an array to sort 2869 * 2870 * @param startIndex an index to start sorting with, inclusive 2871 * 2872 * @param endIndex an index to end sorting, exclusive 2873 * 2874 * @example: sort array arr 2875 * ``` 2876 * sort(arr, 0, arr.length) 2877 * ``` 2878 */ 2879export function sort(arr: long[], startIndex: int, endIndex: int): void { 2880 if (!checkRange(arr.length, startIndex, endIndex)) { 2881 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 2882 } 2883 2884 quickSort(arr, startIndex, endIndex); 2885} 2886 2887function bubbleSort(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 2888 let was = true 2889 while (was) { 2890 was = false 2891 for (let i = startIndex; i < endIndex - 1; i++) { 2892 if (mustPrecede(arr[i + 1], arr[i])) { 2893 const tmp = arr[i + 1] 2894 arr[i + 1] = arr[i] 2895 arr[i] = tmp 2896 was = true 2897 } 2898 } 2899 } 2900} 2901 2902function insertionSort(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean, initIndex: int = startIndex): void { 2903 if (startIndex != initIndex) { 2904 // arr[startIndex - 1] exists and is less than or equal to all elements in range 2905 for (let i = startIndex + 1; i < endIndex; i++) { 2906 const tmp = arr[i] 2907 let pos = i 2908 while (mustPrecede(tmp, arr[pos - 1])) { 2909 arr[pos] = arr[pos - 1] 2910 pos-- 2911 } 2912 arr[pos] = tmp 2913 } 2914 return 2915 } 2916 for (let i = startIndex + 1; i < endIndex; i++) { 2917 const tmp = arr[i] 2918 if (mustPrecede(tmp, arr[startIndex])) { 2919 for (let j = i; j > startIndex; j--) { 2920 arr[j] = arr[j - 1] 2921 } 2922 arr[startIndex] = tmp 2923 } else { 2924 let pos = i 2925 while (mustPrecede(tmp, arr[pos - 1])) { 2926 arr[pos] = arr[pos - 1] 2927 pos-- 2928 } 2929 arr[pos] = tmp 2930 } 2931 } 2932} 2933function heapSortUp(arr: long[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 2934 const tmp = arr[startIndex + idxFromStart] 2935 while (startIndex + idxFromStart > heapRoot) { 2936 const p = (idxFromStart - 1) / 2 2937 if (!mustPrecede(arr[startIndex + p], tmp)) { 2938 break 2939 } 2940 arr[startIndex + idxFromStart] = arr[startIndex + p] 2941 idxFromStart = p 2942 } 2943 arr[startIndex + idxFromStart] = tmp 2944} 2945 2946// Build max heap with root in startIndex given its children are roots of valid heaps 2947function heapSortDown(arr: long[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 2948 let heapRoot = startIndex + idxFromStart 2949 let arrIndex = heapRoot 2950 let childIndex = startIndex + idxFromStart * 2 + 1 2951 const tmp = arr[arrIndex] 2952 // Walk heap to bottom and pull max child up on each level 2953 while (childIndex + 1 < endIndex) { 2954 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 2955 childIndex++ 2956 } 2957 arr[arrIndex] = arr[childIndex] 2958 arrIndex = childIndex 2959 childIndex = childIndex * 2 - startIndex + 1 2960 } 2961 if (childIndex < endIndex) { 2962 arr[arrIndex] = arr[childIndex] 2963 arrIndex = childIndex 2964 } 2965 arr[arrIndex] = tmp 2966 // Now heap is valid in all positions but arrIndex 2967 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 2968} 2969 2970export function heapSort(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 2971 let len = endIndex - startIndex 2972 for (let i = len / 2 - 1; i >= 0; i--) { 2973 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 2974 } 2975 2976 for (let i = endIndex - 1; i > startIndex; i--) { 2977 // move max element to the end of range 2978 const tmp = arr[i] 2979 arr[i] = arr[startIndex] 2980 arr[startIndex] = tmp 2981 heapSortDown(arr, 0, startIndex, i, mustPrecede) 2982 } 2983} 2984 2985// Put median of three array elements to arr[index1] 2986function median3(arr: long[], index1: int, index2: int, index3: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 2987 let swap_idx = index2 2988 if (mustPrecede(arr[index1], arr[index2])) { 2989 if (mustPrecede(arr[index3], arr[index1])) { 2990 return 2991 } 2992 if (mustPrecede(arr[index3], arr[index2])) { 2993 swap_idx = index3 2994 } 2995 } else { 2996 if (!mustPrecede(arr[index3], arr[index1])) { 2997 return 2998 } 2999 if (mustPrecede(arr[index2], arr[index3])) { 3000 swap_idx = index3 3001 } 3002 } 3003 let tmp = arr[index1] 3004 arr[index1] = arr[swap_idx] 3005 arr[swap_idx] = tmp 3006} 3007 3008// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3009// Elements equal to pivot go to the right 3010function quickSortSplit(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): int { 3011 const pivot = arr[startIndex] 3012 let i = startIndex + 1 3013 let j = endIndex - 1 3014 // No bounds check because pivot is median of three elements 3015 while (mustPrecede(arr[i], pivot)) { 3016 i++ 3017 } 3018 if (i == startIndex + 1) { 3019 while (i < j && !mustPrecede(arr[j], pivot)) { 3020 j-- 3021 } 3022 } else { 3023 while (!mustPrecede(arr[j], pivot)) { 3024 j-- 3025 } 3026 } 3027 while (i < j) { 3028 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 3029 let tmp = arr[i] 3030 arr[i] = arr[j] 3031 arr[j] = tmp 3032 while (mustPrecede(arr[++i], pivot)) {} 3033 while (!mustPrecede(arr[--j], pivot)) {} 3034 } 3035 let pivotIndex = i - 1 3036 arr[startIndex] = arr[pivotIndex] 3037 arr[pivotIndex] = pivot 3038 3039 return pivotIndex 3040} 3041 3042// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3043// Elements equal to pivot go to the left 3044function quickSortSplitLeft(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): int { 3045 const pivot = arr[startIndex] 3046 let i = startIndex + 1 3047 let j = endIndex - 1 3048 // No bounds check because pivot is median of three elements 3049 while (mustPrecede(pivot, arr[j])) { 3050 j-- 3051 } 3052 if (j + 1 == endIndex) { 3053 while (i < j && !mustPrecede(pivot, arr[i])) { 3054 i++ 3055 } 3056 } else { 3057 while (!mustPrecede(pivot, arr[i])) { 3058 i++ 3059 } 3060 } 3061 while (i < j) { 3062 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 3063 let tmp = arr[i] 3064 arr[i] = arr[j] 3065 arr[j] = tmp 3066 while (!mustPrecede(pivot, arr[++i])) {} 3067 while (mustPrecede(pivot, arr[--j])) {} 3068 } 3069 arr[startIndex] = arr[j] 3070 arr[j] = pivot 3071 3072 return j 3073} 3074 3075function quickSortImpl3(arr: long[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 3076 while (endIndex - startIndex > 3) { 3077 if (--maxDepth == 0) { 3078 heapSort(arr, startIndex, endIndex, mustPrecede) 3079 return 3080 } 3081 3082 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 3083 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 3084 // make a call for the smaller part of array and continue processing the larger part in the loop 3085 if (p - startIndex < endIndex - p) { 3086 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 3087 startIndex = p + 1 3088 } else { 3089 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 3090 endIndex = p 3091 } 3092 } 3093 insertionSort(arr, startIndex, endIndex, mustPrecede) 3094} 3095function quickSortImpl40(arr: long[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 3096 while (endIndex - startIndex > 40) { 3097 if (--maxDepth == 0) { 3098 heapSort(arr, startIndex, endIndex, mustPrecede) 3099 return 3100 } 3101 3102 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 3103 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 3104 // make a call for the smaller part of array and continue processing the larger part in the loop 3105 if (p - startIndex < endIndex - p) { 3106 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 3107 startIndex = p + 1 3108 } else { 3109 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 3110 endIndex = p 3111 } 3112 } 3113 insertionSort(arr, startIndex, endIndex, mustPrecede) 3114} 3115 3116function quickSort(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 3117 let size = endIndex - startIndex 3118 if (size <= 1) { 3119 return 3120 } 3121 // find log of length to fall back into determenistic O(n logn) sort 3122 let bits = 32 3123 for (let i = 2; i < 31; i++) { 3124 if ((size >> i) == 0) { 3125 bits = i 3126 break 3127 } 3128 } 3129 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 3130} 3131 3132/** 3133 * sorts arr in-place 3134 * 3135 * @param arr an array to sort 3136 * 3137 * @param startIndex an index to start sorting with, inclusive 3138 * 3139 * @param endIndex an index to end sorting, exclusive 3140 * 3141 * @example: sort array arr 3142 * ``` 3143 * sort(arr, 0, arr.length) 3144 * ``` 3145 */ 3146export function sort_subarray(arr: long[], startIndex: int, endIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 3147 if (!checkRange(arr.length, startIndex, endIndex)) { 3148 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 3149 } 3150 3151 quickSort(arr, startIndex, endIndex, mustPrecede); 3152} 3153 3154/** 3155 * sorts arr in-place 3156 * 3157 * @param arr an array to sort 3158 */ 3159export function sort_subarray(arr: long[], mustPrecede: (lhs: long, rhs: long) => boolean): void { 3160 sort_subarray(arr, 0, arr.length, mustPrecede); 3161} 3162 3163export function sort_subarray(arr: long[], startIndex: int, mustPrecede: (lhs: long, rhs: long) => boolean): void { 3164 sort_subarray(arr, startIndex, arr.length, mustPrecede) 3165} 3166 3167export function sort(arr: long[], startIndex: int): void { 3168 sort(arr, startIndex, arr.length) 3169} 3170 3171// ======== tests section ======== 3172 3173/** note: used for tests, {@link test_sortAllOn} */ 3174class test_SortDatalong { 3175 name: string 3176 arr: long[] 3177 3178 constructor(name: string, arr: long[]) { 3179 this.name = name 3180 this.arr = arr 3181 } 3182} 3183 3184/** note: used for tests, {@link test_sortAllOn} */ 3185function test_sortCopy(arr: long[]): long[] { 3186 let c = new long[arr.length] 3187 for (let i = 0; i < arr.length; i++) { 3188 c[i] = arr[i] 3189 } 3190 return c 3191} 3192 3193/** note: used for tests, {@link test_sortAllOn} */ 3194function test_sortAllOnCmpFwd_long(l: long, r: long): boolean { 3195 return l < r 3196} 3197 3198/** note: used for tests, {@link test_sortAllOn} */ 3199function test_sortAllOnCmpInv_long(l: long, r: long): boolean { 3200 return r < l 3201} 3202 3203/** note: used for tests, {@link test_sortAllOn} */ 3204function test_printArr(arr: long[], startIndex: int, endIndex: int) { 3205 for (let i = startIndex; i < endIndex; i++) { 3206 console.print(arr[i] + ' ') 3207 } 3208 console.println('') 3209} 3210 3211/** 3212 * Function used to test sorting in standard library tests. 3213 * There is only one exported function: `sort`, but for testing 3214 * we need access to all sub sorts that are used. Hence this part of "tests" 3215 * is located here. All related entities are prefixed whith `test_` to stand out 3216 */ 3217export function test_sortAllOn(arr: long[]) { 3218 // block for comparator `` 3219 if (true) { 3220 const sorts = new Array<test_SortDatalong>() 3221 const bubbled = test_sortCopy(arr) 3222 bubbleSort(bubbled, 0, bubbled.length) 3223 sorts.push(new test_SortDatalong("bubble", bubbled)) 3224 3225 const insertion = test_sortCopy(arr) 3226 insertionSort(insertion, 0, insertion.length) 3227 sorts.push(new test_SortDatalong("insertion", insertion)) 3228 3229 const heaped = test_sortCopy(arr) 3230 heapSort(heaped, 0, heaped.length) 3231 sorts.push(new test_SortDatalong("heap", heaped)) 3232 3233 const quicked = test_sortCopy(arr) 3234 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 3235 sorts.push(new test_SortDatalong("quick", quicked)) 3236 3237 const quickedInsertion = test_sortCopy(arr) 3238 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 3239 sorts.push(new test_SortDatalong("quick with insertion", quickedInsertion)) 3240 3241 const just = test_sortCopy(arr) 3242 sort(just) 3243 sorts.push(new test_SortDatalong("sort()", just)) 3244 3245 let sort0 = sorts.at(0)! 3246 for (let s = 1; s < sorts.length; s++) { 3247 let sortS = sorts.at(s)! 3248 for (let i = 0; i < arr.length; i++) { 3249 if (sort0.arr[i] != sortS.arr[i]) { 3250 console.println(sort0.name + ': ') 3251 test_printArr(sort0.arr, 0, arr.length) 3252 console.println(sortS.name + ': ') 3253 test_printArr(sortS.arr, 0, arr.length) 3254 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for long') 3255 } 3256 } 3257 } 3258 } 3259 // block for comparator `est_sortAllOnCmpFwd_long` 3260 if (true) { 3261 const sorts = new Array<test_SortDatalong>() 3262 const bubbled = test_sortCopy(arr) 3263 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_long) 3264 sorts.push(new test_SortDatalong("bubble", bubbled)) 3265 3266 const insertion = test_sortCopy(arr) 3267 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_long) 3268 sorts.push(new test_SortDatalong("insertion", insertion)) 3269 3270 const heaped = test_sortCopy(arr) 3271 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_long) 3272 sorts.push(new test_SortDatalong("heap", heaped)) 3273 3274 const quicked = test_sortCopy(arr) 3275 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_long) 3276 sorts.push(new test_SortDatalong("quick", quicked)) 3277 3278 const quickedInsertion = test_sortCopy(arr) 3279 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_long) 3280 sorts.push(new test_SortDatalong("quick with insertion", quickedInsertion)) 3281 3282 const just = test_sortCopy(arr) 3283 sort_subarray(just, test_sortAllOnCmpFwd_long) 3284 sorts.push(new test_SortDatalong("sort()", just)) 3285 3286 let sort0 = sorts.at(0)! 3287 for (let s = 1; s < sorts.length; s++) { 3288 let sortS = sorts.at(s)! 3289 for (let i = 0; i < arr.length; i++) { 3290 if (sort0.arr[i] != sortS.arr[i]) { 3291 console.println(sort0.name + ': ') 3292 test_printArr(sort0.arr, 0, arr.length) 3293 console.println(sortS.name + ': ') 3294 test_printArr(sortS.arr, 0, arr.length) 3295 throw new Error("sorts est_sortAllOnCmpFwd_long are not equal: " + sort0.name + ' ' + sortS.name + ' for long') 3296 } 3297 } 3298 } 3299 } 3300 // block for comparator `est_sortAllOnCmpInv_long` 3301 if (true) { 3302 const sorts = new Array<test_SortDatalong>() 3303 const bubbled = test_sortCopy(arr) 3304 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_long) 3305 sorts.push(new test_SortDatalong("bubble", bubbled)) 3306 3307 const insertion = test_sortCopy(arr) 3308 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_long) 3309 sorts.push(new test_SortDatalong("insertion", insertion)) 3310 3311 const heaped = test_sortCopy(arr) 3312 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_long) 3313 sorts.push(new test_SortDatalong("heap", heaped)) 3314 3315 const quicked = test_sortCopy(arr) 3316 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_long) 3317 sorts.push(new test_SortDatalong("quick", quicked)) 3318 3319 const quickedInsertion = test_sortCopy(arr) 3320 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_long) 3321 sorts.push(new test_SortDatalong("quick with insertion", quickedInsertion)) 3322 3323 const just = test_sortCopy(arr) 3324 sort_subarray(just, test_sortAllOnCmpInv_long) 3325 sorts.push(new test_SortDatalong("sort()", just)) 3326 3327 let sort0 = sorts.at(0)! 3328 for (let s = 1; s < sorts.length; s++) { 3329 let sortS = sorts.at(s)! 3330 for (let i = 0; i < arr.length; i++) { 3331 if (sort0.arr[i] != sortS.arr[i]) { 3332 console.println(sort0.name + ': ') 3333 test_printArr(sort0.arr, 0, arr.length) 3334 console.println(sortS.name + ': ') 3335 test_printArr(sortS.arr, 0, arr.length) 3336 throw new Error("sorts est_sortAllOnCmpInv_long are not equal: " + sort0.name + ' ' + sortS.name + ' for long') 3337 } 3338 } 3339 } 3340 } 3341} 3342// ======== end of tests section ======== 3343 3344function bubbleSort(arr: float[], startIndex: int, endIndex: int): void { 3345 let was = true 3346 while (was) { 3347 was = false 3348 for (let i = startIndex; i < endIndex - 1; i++) { 3349 if ((arr[i + 1] < arr[i])) { 3350 const tmp = arr[i + 1] 3351 arr[i + 1] = arr[i] 3352 arr[i] = tmp 3353 was = true 3354 } 3355 } 3356 } 3357} 3358 3359function insertionSort(arr: float[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 3360 if (startIndex != initIndex) { 3361 // arr[startIndex - 1] exists and is less than or equal to all elements in range 3362 for (let i = startIndex + 1; i < endIndex; i++) { 3363 const tmp = arr[i] 3364 let pos = i 3365 while ((tmp < arr[pos - 1])) { 3366 arr[pos] = arr[pos - 1] 3367 pos-- 3368 } 3369 arr[pos] = tmp 3370 } 3371 return 3372 } 3373 for (let i = startIndex + 1; i < endIndex; i++) { 3374 const tmp = arr[i] 3375 if ((tmp < arr[startIndex])) { 3376 for (let j = i; j > startIndex; j--) { 3377 arr[j] = arr[j - 1] 3378 } 3379 arr[startIndex] = tmp 3380 } else { 3381 let pos = i 3382 while ((tmp < arr[pos - 1])) { 3383 arr[pos] = arr[pos - 1] 3384 pos-- 3385 } 3386 arr[pos] = tmp 3387 } 3388 } 3389} 3390function heapSortUp(arr: float[], idxFromStart: int, startIndex: int, heapRoot: int): void { 3391 const tmp = arr[startIndex + idxFromStart] 3392 while (startIndex + idxFromStart > heapRoot) { 3393 const p = (idxFromStart - 1) / 2 3394 if (!(arr[startIndex + p] < tmp)) { 3395 break 3396 } 3397 arr[startIndex + idxFromStart] = arr[startIndex + p] 3398 idxFromStart = p 3399 } 3400 arr[startIndex + idxFromStart] = tmp 3401} 3402 3403// Build max heap with root in startIndex given its children are roots of valid heaps 3404function heapSortDown(arr: float[], idxFromStart: int, startIndex: int, endIndex: int): void { 3405 let heapRoot = startIndex + idxFromStart 3406 let arrIndex = heapRoot 3407 let childIndex = startIndex + idxFromStart * 2 + 1 3408 const tmp = arr[arrIndex] 3409 // Walk heap to bottom and pull max child up on each level 3410 while (childIndex + 1 < endIndex) { 3411 if ((arr[childIndex] < arr[childIndex + 1])) { 3412 childIndex++ 3413 } 3414 arr[arrIndex] = arr[childIndex] 3415 arrIndex = childIndex 3416 childIndex = childIndex * 2 - startIndex + 1 3417 } 3418 if (childIndex < endIndex) { 3419 arr[arrIndex] = arr[childIndex] 3420 arrIndex = childIndex 3421 } 3422 arr[arrIndex] = tmp 3423 // Now heap is valid in all positions but arrIndex 3424 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 3425} 3426 3427export function heapSort(arr: float[], startIndex: int, endIndex: int): void { 3428 let len = endIndex - startIndex 3429 for (let i = len / 2 - 1; i >= 0; i--) { 3430 heapSortDown(arr, i, startIndex, endIndex) 3431 } 3432 3433 for (let i = endIndex - 1; i > startIndex; i--) { 3434 // move max element to the end of range 3435 const tmp = arr[i] 3436 arr[i] = arr[startIndex] 3437 arr[startIndex] = tmp 3438 heapSortDown(arr, 0, startIndex, i) 3439 } 3440} 3441 3442// Put median of three array elements to arr[index1] 3443function median3(arr: float[], index1: int, index2: int, index3: int): void { 3444 let swap_idx = index2 3445 if ((arr[index1] < arr[index2])) { 3446 if ((arr[index3] < arr[index1])) { 3447 return 3448 } 3449 if ((arr[index3] < arr[index2])) { 3450 swap_idx = index3 3451 } 3452 } else { 3453 if (!(arr[index3] < arr[index1])) { 3454 return 3455 } 3456 if ((arr[index2] < arr[index3])) { 3457 swap_idx = index3 3458 } 3459 } 3460 let tmp = arr[index1] 3461 arr[index1] = arr[swap_idx] 3462 arr[swap_idx] = tmp 3463} 3464 3465// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3466// Elements equal to pivot go to the right 3467function quickSortSplit(arr: float[], startIndex: int, endIndex: int): int { 3468 const pivot = arr[startIndex] 3469 let i = startIndex + 1 3470 let j = endIndex - 1 3471 // No bounds check because pivot is median of three elements 3472 while ((arr[i] < pivot)) { 3473 i++ 3474 } 3475 if (i == startIndex + 1) { 3476 while (i < j && !(arr[j] < pivot)) { 3477 j-- 3478 } 3479 } else { 3480 while (!(arr[j] < pivot)) { 3481 j-- 3482 } 3483 } 3484 while (i < j) { 3485 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 3486 let tmp = arr[i] 3487 arr[i] = arr[j] 3488 arr[j] = tmp 3489 while ((arr[++i] < pivot)) {} 3490 while (!(arr[--j] < pivot)) {} 3491 } 3492 let pivotIndex = i - 1 3493 arr[startIndex] = arr[pivotIndex] 3494 arr[pivotIndex] = pivot 3495 3496 return pivotIndex 3497} 3498 3499// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3500// Elements equal to pivot go to the left 3501function quickSortSplitLeft(arr: float[], startIndex: int, endIndex: int): int { 3502 const pivot = arr[startIndex] 3503 let i = startIndex + 1 3504 let j = endIndex - 1 3505 // No bounds check because pivot is median of three elements 3506 while ((pivot < arr[j])) { 3507 j-- 3508 } 3509 if (j + 1 == endIndex) { 3510 while (i < j && !(pivot < arr[i])) { 3511 i++ 3512 } 3513 } else { 3514 while (!(pivot < arr[i])) { 3515 i++ 3516 } 3517 } 3518 while (i < j) { 3519 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 3520 let tmp = arr[i] 3521 arr[i] = arr[j] 3522 arr[j] = tmp 3523 while (!(pivot < arr[++i])) {} 3524 while ((pivot < arr[--j])) {} 3525 } 3526 arr[startIndex] = arr[j] 3527 arr[j] = pivot 3528 3529 return j 3530} 3531 3532function quickSortImpl3(arr: float[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 3533 while (endIndex - startIndex > 3) { 3534 if (--maxDepth == 0) { 3535 heapSort(arr, startIndex, endIndex) 3536 return 3537 } 3538 // Here we assume that current interval is not the most left in the sorted range 3539 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 3540 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 3541 // after that only the right part needs to be sorted 3542 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 3543 // with many occurencies of the smallest element 3544 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 3545 continue 3546 } 3547 3548 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 3549 let p = quickSortSplit(arr, startIndex, endIndex) 3550 // make a call for the smaller part of array and continue processing the larger part in the loop 3551 if (p - startIndex < endIndex - p) { 3552 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 3553 startIndex = p + 1 3554 } else { 3555 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 3556 endIndex = p 3557 } 3558 } 3559 insertionSort(arr, startIndex, endIndex, initIndex) 3560} 3561function quickSortImpl40(arr: float[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 3562 while (endIndex - startIndex > 40) { 3563 if (--maxDepth == 0) { 3564 heapSort(arr, startIndex, endIndex) 3565 return 3566 } 3567 // Here we assume that current interval is not the most left in the sorted range 3568 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 3569 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 3570 // after that only the right part needs to be sorted 3571 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 3572 // with many occurencies of the smallest element 3573 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 3574 continue 3575 } 3576 3577 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 3578 let p = quickSortSplit(arr, startIndex, endIndex) 3579 // make a call for the smaller part of array and continue processing the larger part in the loop 3580 if (p - startIndex < endIndex - p) { 3581 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 3582 startIndex = p + 1 3583 } else { 3584 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 3585 endIndex = p 3586 } 3587 } 3588 insertionSort(arr, startIndex, endIndex, initIndex) 3589} 3590 3591function quickSort(arr: float[], startIndex: int, endIndex: int): void { 3592 let size = endIndex - startIndex 3593 if (size <= 1) { 3594 return 3595 } 3596 // find log of length to fall back into determenistic O(n logn) sort 3597 let bits = 32 3598 for (let i = 2; i < 31; i++) { 3599 if ((size >> i) == 0) { 3600 bits = i 3601 break 3602 } 3603 } 3604 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 3605} 3606 3607/** 3608 * sorts arr in-place 3609 * 3610 * @param arr an array to sort 3611 * 3612 * @param startIndex an index to start sorting with, inclusive 3613 * 3614 * @param endIndex an index to end sorting, exclusive 3615 * 3616 * @example: sort array arr 3617 * ``` 3618 * sort(arr, 0, arr.length) 3619 * ``` 3620 */ 3621export function sort(arr: float[], startIndex: int, endIndex: int): void { 3622 if (!checkRange(arr.length, startIndex, endIndex)) { 3623 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 3624 } 3625 3626 quickSort(arr, startIndex, endIndex); 3627} 3628 3629function bubbleSort(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3630 let was = true 3631 while (was) { 3632 was = false 3633 for (let i = startIndex; i < endIndex - 1; i++) { 3634 if (mustPrecede(arr[i + 1], arr[i])) { 3635 const tmp = arr[i + 1] 3636 arr[i + 1] = arr[i] 3637 arr[i] = tmp 3638 was = true 3639 } 3640 } 3641 } 3642} 3643 3644function insertionSort(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean, initIndex: int = startIndex): void { 3645 if (startIndex != initIndex) { 3646 // arr[startIndex - 1] exists and is less than or equal to all elements in range 3647 for (let i = startIndex + 1; i < endIndex; i++) { 3648 const tmp = arr[i] 3649 let pos = i 3650 while (mustPrecede(tmp, arr[pos - 1])) { 3651 arr[pos] = arr[pos - 1] 3652 pos-- 3653 } 3654 arr[pos] = tmp 3655 } 3656 return 3657 } 3658 for (let i = startIndex + 1; i < endIndex; i++) { 3659 const tmp = arr[i] 3660 if (mustPrecede(tmp, arr[startIndex])) { 3661 for (let j = i; j > startIndex; j--) { 3662 arr[j] = arr[j - 1] 3663 } 3664 arr[startIndex] = tmp 3665 } else { 3666 let pos = i 3667 while (mustPrecede(tmp, arr[pos - 1])) { 3668 arr[pos] = arr[pos - 1] 3669 pos-- 3670 } 3671 arr[pos] = tmp 3672 } 3673 } 3674} 3675function heapSortUp(arr: float[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3676 const tmp = arr[startIndex + idxFromStart] 3677 while (startIndex + idxFromStart > heapRoot) { 3678 const p = (idxFromStart - 1) / 2 3679 if (!mustPrecede(arr[startIndex + p], tmp)) { 3680 break 3681 } 3682 arr[startIndex + idxFromStart] = arr[startIndex + p] 3683 idxFromStart = p 3684 } 3685 arr[startIndex + idxFromStart] = tmp 3686} 3687 3688// Build max heap with root in startIndex given its children are roots of valid heaps 3689function heapSortDown(arr: float[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3690 let heapRoot = startIndex + idxFromStart 3691 let arrIndex = heapRoot 3692 let childIndex = startIndex + idxFromStart * 2 + 1 3693 const tmp = arr[arrIndex] 3694 // Walk heap to bottom and pull max child up on each level 3695 while (childIndex + 1 < endIndex) { 3696 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 3697 childIndex++ 3698 } 3699 arr[arrIndex] = arr[childIndex] 3700 arrIndex = childIndex 3701 childIndex = childIndex * 2 - startIndex + 1 3702 } 3703 if (childIndex < endIndex) { 3704 arr[arrIndex] = arr[childIndex] 3705 arrIndex = childIndex 3706 } 3707 arr[arrIndex] = tmp 3708 // Now heap is valid in all positions but arrIndex 3709 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 3710} 3711 3712export function heapSort(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3713 let len = endIndex - startIndex 3714 for (let i = len / 2 - 1; i >= 0; i--) { 3715 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 3716 } 3717 3718 for (let i = endIndex - 1; i > startIndex; i--) { 3719 // move max element to the end of range 3720 const tmp = arr[i] 3721 arr[i] = arr[startIndex] 3722 arr[startIndex] = tmp 3723 heapSortDown(arr, 0, startIndex, i, mustPrecede) 3724 } 3725} 3726 3727// Put median of three array elements to arr[index1] 3728function median3(arr: float[], index1: int, index2: int, index3: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3729 let swap_idx = index2 3730 if (mustPrecede(arr[index1], arr[index2])) { 3731 if (mustPrecede(arr[index3], arr[index1])) { 3732 return 3733 } 3734 if (mustPrecede(arr[index3], arr[index2])) { 3735 swap_idx = index3 3736 } 3737 } else { 3738 if (!mustPrecede(arr[index3], arr[index1])) { 3739 return 3740 } 3741 if (mustPrecede(arr[index2], arr[index3])) { 3742 swap_idx = index3 3743 } 3744 } 3745 let tmp = arr[index1] 3746 arr[index1] = arr[swap_idx] 3747 arr[swap_idx] = tmp 3748} 3749 3750// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3751// Elements equal to pivot go to the right 3752function quickSortSplit(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): int { 3753 const pivot = arr[startIndex] 3754 let i = startIndex + 1 3755 let j = endIndex - 1 3756 // No bounds check because pivot is median of three elements 3757 while (mustPrecede(arr[i], pivot)) { 3758 i++ 3759 } 3760 if (i == startIndex + 1) { 3761 while (i < j && !mustPrecede(arr[j], pivot)) { 3762 j-- 3763 } 3764 } else { 3765 while (!mustPrecede(arr[j], pivot)) { 3766 j-- 3767 } 3768 } 3769 while (i < j) { 3770 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 3771 let tmp = arr[i] 3772 arr[i] = arr[j] 3773 arr[j] = tmp 3774 while (mustPrecede(arr[++i], pivot)) {} 3775 while (!mustPrecede(arr[--j], pivot)) {} 3776 } 3777 let pivotIndex = i - 1 3778 arr[startIndex] = arr[pivotIndex] 3779 arr[pivotIndex] = pivot 3780 3781 return pivotIndex 3782} 3783 3784// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 3785// Elements equal to pivot go to the left 3786function quickSortSplitLeft(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): int { 3787 const pivot = arr[startIndex] 3788 let i = startIndex + 1 3789 let j = endIndex - 1 3790 // No bounds check because pivot is median of three elements 3791 while (mustPrecede(pivot, arr[j])) { 3792 j-- 3793 } 3794 if (j + 1 == endIndex) { 3795 while (i < j && !mustPrecede(pivot, arr[i])) { 3796 i++ 3797 } 3798 } else { 3799 while (!mustPrecede(pivot, arr[i])) { 3800 i++ 3801 } 3802 } 3803 while (i < j) { 3804 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 3805 let tmp = arr[i] 3806 arr[i] = arr[j] 3807 arr[j] = tmp 3808 while (!mustPrecede(pivot, arr[++i])) {} 3809 while (mustPrecede(pivot, arr[--j])) {} 3810 } 3811 arr[startIndex] = arr[j] 3812 arr[j] = pivot 3813 3814 return j 3815} 3816 3817function quickSortImpl3(arr: float[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3818 while (endIndex - startIndex > 3) { 3819 if (--maxDepth == 0) { 3820 heapSort(arr, startIndex, endIndex, mustPrecede) 3821 return 3822 } 3823 3824 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 3825 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 3826 // make a call for the smaller part of array and continue processing the larger part in the loop 3827 if (p - startIndex < endIndex - p) { 3828 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 3829 startIndex = p + 1 3830 } else { 3831 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 3832 endIndex = p 3833 } 3834 } 3835 insertionSort(arr, startIndex, endIndex, mustPrecede) 3836} 3837function quickSortImpl40(arr: float[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3838 while (endIndex - startIndex > 40) { 3839 if (--maxDepth == 0) { 3840 heapSort(arr, startIndex, endIndex, mustPrecede) 3841 return 3842 } 3843 3844 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 3845 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 3846 // make a call for the smaller part of array and continue processing the larger part in the loop 3847 if (p - startIndex < endIndex - p) { 3848 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 3849 startIndex = p + 1 3850 } else { 3851 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 3852 endIndex = p 3853 } 3854 } 3855 insertionSort(arr, startIndex, endIndex, mustPrecede) 3856} 3857 3858function quickSort(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3859 let size = endIndex - startIndex 3860 if (size <= 1) { 3861 return 3862 } 3863 // find log of length to fall back into determenistic O(n logn) sort 3864 let bits = 32 3865 for (let i = 2; i < 31; i++) { 3866 if ((size >> i) == 0) { 3867 bits = i 3868 break 3869 } 3870 } 3871 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 3872} 3873 3874/** 3875 * sorts arr in-place 3876 * 3877 * @param arr an array to sort 3878 * 3879 * @param startIndex an index to start sorting with, inclusive 3880 * 3881 * @param endIndex an index to end sorting, exclusive 3882 * 3883 * @example: sort array arr 3884 * ``` 3885 * sort(arr, 0, arr.length) 3886 * ``` 3887 */ 3888export function sort_subarray(arr: float[], startIndex: int, endIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3889 if (!checkRange(arr.length, startIndex, endIndex)) { 3890 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 3891 } 3892 3893 quickSort(arr, startIndex, endIndex, mustPrecede); 3894} 3895 3896/** 3897 * sorts arr in-place 3898 * 3899 * @param arr an array to sort 3900 */ 3901export function sort_subarray(arr: float[], mustPrecede: (lhs: float, rhs: float) => boolean): void { 3902 sort_subarray(arr, 0, arr.length, mustPrecede); 3903} 3904 3905export function sort_subarray(arr: float[], startIndex: int, mustPrecede: (lhs: float, rhs: float) => boolean): void { 3906 sort_subarray(arr, startIndex, arr.length, mustPrecede) 3907} 3908 3909export function sort(arr: float[], startIndex: int): void { 3910 sort(arr, startIndex, arr.length) 3911} 3912 3913// ======== tests section ======== 3914 3915/** note: used for tests, {@link test_sortAllOn} */ 3916class test_SortDatafloat { 3917 name: string 3918 arr: float[] 3919 3920 constructor(name: string, arr: float[]) { 3921 this.name = name 3922 this.arr = arr 3923 } 3924} 3925 3926/** note: used for tests, {@link test_sortAllOn} */ 3927function test_sortCopy(arr: float[]): float[] { 3928 let c = new float[arr.length] 3929 for (let i = 0; i < arr.length; i++) { 3930 c[i] = arr[i] 3931 } 3932 return c 3933} 3934 3935/** note: used for tests, {@link test_sortAllOn} */ 3936function test_sortAllOnCmpFwd_float(l: float, r: float): boolean { 3937 return l < r 3938} 3939 3940/** note: used for tests, {@link test_sortAllOn} */ 3941function test_sortAllOnCmpInv_float(l: float, r: float): boolean { 3942 return r < l 3943} 3944 3945/** note: used for tests, {@link test_sortAllOn} */ 3946function test_printArr(arr: float[], startIndex: int, endIndex: int) { 3947 for (let i = startIndex; i < endIndex; i++) { 3948 console.print(arr[i] + ' ') 3949 } 3950 console.println('') 3951} 3952 3953/** 3954 * Function used to test sorting in standard library tests. 3955 * There is only one exported function: `sort`, but for testing 3956 * we need access to all sub sorts that are used. Hence this part of "tests" 3957 * is located here. All related entities are prefixed whith `test_` to stand out 3958 */ 3959export function test_sortAllOn(arr: float[]) { 3960 // block for comparator `` 3961 if (true) { 3962 const sorts = new Array<test_SortDatafloat>() 3963 const bubbled = test_sortCopy(arr) 3964 bubbleSort(bubbled, 0, bubbled.length) 3965 sorts.push(new test_SortDatafloat("bubble", bubbled)) 3966 3967 const insertion = test_sortCopy(arr) 3968 insertionSort(insertion, 0, insertion.length) 3969 sorts.push(new test_SortDatafloat("insertion", insertion)) 3970 3971 const heaped = test_sortCopy(arr) 3972 heapSort(heaped, 0, heaped.length) 3973 sorts.push(new test_SortDatafloat("heap", heaped)) 3974 3975 const quicked = test_sortCopy(arr) 3976 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 3977 sorts.push(new test_SortDatafloat("quick", quicked)) 3978 3979 const quickedInsertion = test_sortCopy(arr) 3980 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 3981 sorts.push(new test_SortDatafloat("quick with insertion", quickedInsertion)) 3982 3983 const just = test_sortCopy(arr) 3984 sort(just) 3985 sorts.push(new test_SortDatafloat("sort()", just)) 3986 3987 let sort0 = sorts.at(0)! 3988 for (let s = 1; s < sorts.length; s++) { 3989 let sortS = sorts.at(s)! 3990 for (let i = 0; i < arr.length; i++) { 3991 if (sort0.arr[i] != sortS.arr[i]) { 3992 console.println(sort0.name + ': ') 3993 test_printArr(sort0.arr, 0, arr.length) 3994 console.println(sortS.name + ': ') 3995 test_printArr(sortS.arr, 0, arr.length) 3996 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for float') 3997 } 3998 } 3999 } 4000 } 4001 // block for comparator `est_sortAllOnCmpFwd_float` 4002 if (true) { 4003 const sorts = new Array<test_SortDatafloat>() 4004 const bubbled = test_sortCopy(arr) 4005 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_float) 4006 sorts.push(new test_SortDatafloat("bubble", bubbled)) 4007 4008 const insertion = test_sortCopy(arr) 4009 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_float) 4010 sorts.push(new test_SortDatafloat("insertion", insertion)) 4011 4012 const heaped = test_sortCopy(arr) 4013 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_float) 4014 sorts.push(new test_SortDatafloat("heap", heaped)) 4015 4016 const quicked = test_sortCopy(arr) 4017 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_float) 4018 sorts.push(new test_SortDatafloat("quick", quicked)) 4019 4020 const quickedInsertion = test_sortCopy(arr) 4021 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_float) 4022 sorts.push(new test_SortDatafloat("quick with insertion", quickedInsertion)) 4023 4024 const just = test_sortCopy(arr) 4025 sort_subarray(just, test_sortAllOnCmpFwd_float) 4026 sorts.push(new test_SortDatafloat("sort()", just)) 4027 4028 let sort0 = sorts.at(0)! 4029 for (let s = 1; s < sorts.length; s++) { 4030 let sortS = sorts.at(s)! 4031 for (let i = 0; i < arr.length; i++) { 4032 if (sort0.arr[i] != sortS.arr[i]) { 4033 console.println(sort0.name + ': ') 4034 test_printArr(sort0.arr, 0, arr.length) 4035 console.println(sortS.name + ': ') 4036 test_printArr(sortS.arr, 0, arr.length) 4037 throw new Error("sorts est_sortAllOnCmpFwd_float are not equal: " + sort0.name + ' ' + sortS.name + ' for float') 4038 } 4039 } 4040 } 4041 } 4042 // block for comparator `est_sortAllOnCmpInv_float` 4043 if (true) { 4044 const sorts = new Array<test_SortDatafloat>() 4045 const bubbled = test_sortCopy(arr) 4046 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_float) 4047 sorts.push(new test_SortDatafloat("bubble", bubbled)) 4048 4049 const insertion = test_sortCopy(arr) 4050 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_float) 4051 sorts.push(new test_SortDatafloat("insertion", insertion)) 4052 4053 const heaped = test_sortCopy(arr) 4054 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_float) 4055 sorts.push(new test_SortDatafloat("heap", heaped)) 4056 4057 const quicked = test_sortCopy(arr) 4058 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_float) 4059 sorts.push(new test_SortDatafloat("quick", quicked)) 4060 4061 const quickedInsertion = test_sortCopy(arr) 4062 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_float) 4063 sorts.push(new test_SortDatafloat("quick with insertion", quickedInsertion)) 4064 4065 const just = test_sortCopy(arr) 4066 sort_subarray(just, test_sortAllOnCmpInv_float) 4067 sorts.push(new test_SortDatafloat("sort()", just)) 4068 4069 let sort0 = sorts.at(0)! 4070 for (let s = 1; s < sorts.length; s++) { 4071 let sortS = sorts.at(s)! 4072 for (let i = 0; i < arr.length; i++) { 4073 if (sort0.arr[i] != sortS.arr[i]) { 4074 console.println(sort0.name + ': ') 4075 test_printArr(sort0.arr, 0, arr.length) 4076 console.println(sortS.name + ': ') 4077 test_printArr(sortS.arr, 0, arr.length) 4078 throw new Error("sorts est_sortAllOnCmpInv_float are not equal: " + sort0.name + ' ' + sortS.name + ' for float') 4079 } 4080 } 4081 } 4082 } 4083} 4084// ======== end of tests section ======== 4085 4086function bubbleSort(arr: double[], startIndex: int, endIndex: int): void { 4087 let was = true 4088 while (was) { 4089 was = false 4090 for (let i = startIndex; i < endIndex - 1; i++) { 4091 if ((arr[i + 1] < arr[i])) { 4092 const tmp = arr[i + 1] 4093 arr[i + 1] = arr[i] 4094 arr[i] = tmp 4095 was = true 4096 } 4097 } 4098 } 4099} 4100 4101function insertionSort(arr: double[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 4102 if (startIndex != initIndex) { 4103 // arr[startIndex - 1] exists and is less than or equal to all elements in range 4104 for (let i = startIndex + 1; i < endIndex; i++) { 4105 const tmp = arr[i] 4106 let pos = i 4107 while ((tmp < arr[pos - 1])) { 4108 arr[pos] = arr[pos - 1] 4109 pos-- 4110 } 4111 arr[pos] = tmp 4112 } 4113 return 4114 } 4115 for (let i = startIndex + 1; i < endIndex; i++) { 4116 const tmp = arr[i] 4117 if ((tmp < arr[startIndex])) { 4118 for (let j = i; j > startIndex; j--) { 4119 arr[j] = arr[j - 1] 4120 } 4121 arr[startIndex] = tmp 4122 } else { 4123 let pos = i 4124 while ((tmp < arr[pos - 1])) { 4125 arr[pos] = arr[pos - 1] 4126 pos-- 4127 } 4128 arr[pos] = tmp 4129 } 4130 } 4131} 4132function heapSortUp(arr: double[], idxFromStart: int, startIndex: int, heapRoot: int): void { 4133 const tmp = arr[startIndex + idxFromStart] 4134 while (startIndex + idxFromStart > heapRoot) { 4135 const p = (idxFromStart - 1) / 2 4136 if (!(arr[startIndex + p] < tmp)) { 4137 break 4138 } 4139 arr[startIndex + idxFromStart] = arr[startIndex + p] 4140 idxFromStart = p 4141 } 4142 arr[startIndex + idxFromStart] = tmp 4143} 4144 4145// Build max heap with root in startIndex given its children are roots of valid heaps 4146function heapSortDown(arr: double[], idxFromStart: int, startIndex: int, endIndex: int): void { 4147 let heapRoot = startIndex + idxFromStart 4148 let arrIndex = heapRoot 4149 let childIndex = startIndex + idxFromStart * 2 + 1 4150 const tmp = arr[arrIndex] 4151 // Walk heap to bottom and pull max child up on each level 4152 while (childIndex + 1 < endIndex) { 4153 if ((arr[childIndex] < arr[childIndex + 1])) { 4154 childIndex++ 4155 } 4156 arr[arrIndex] = arr[childIndex] 4157 arrIndex = childIndex 4158 childIndex = childIndex * 2 - startIndex + 1 4159 } 4160 if (childIndex < endIndex) { 4161 arr[arrIndex] = arr[childIndex] 4162 arrIndex = childIndex 4163 } 4164 arr[arrIndex] = tmp 4165 // Now heap is valid in all positions but arrIndex 4166 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 4167} 4168 4169export function heapSort(arr: double[], startIndex: int, endIndex: int): void { 4170 let len = endIndex - startIndex 4171 for (let i = len / 2 - 1; i >= 0; i--) { 4172 heapSortDown(arr, i, startIndex, endIndex) 4173 } 4174 4175 for (let i = endIndex - 1; i > startIndex; i--) { 4176 // move max element to the end of range 4177 const tmp = arr[i] 4178 arr[i] = arr[startIndex] 4179 arr[startIndex] = tmp 4180 heapSortDown(arr, 0, startIndex, i) 4181 } 4182} 4183 4184// Put median of three array elements to arr[index1] 4185function median3(arr: double[], index1: int, index2: int, index3: int): void { 4186 let swap_idx = index2 4187 if ((arr[index1] < arr[index2])) { 4188 if ((arr[index3] < arr[index1])) { 4189 return 4190 } 4191 if ((arr[index3] < arr[index2])) { 4192 swap_idx = index3 4193 } 4194 } else { 4195 if (!(arr[index3] < arr[index1])) { 4196 return 4197 } 4198 if ((arr[index2] < arr[index3])) { 4199 swap_idx = index3 4200 } 4201 } 4202 let tmp = arr[index1] 4203 arr[index1] = arr[swap_idx] 4204 arr[swap_idx] = tmp 4205} 4206 4207// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4208// Elements equal to pivot go to the right 4209function quickSortSplit(arr: double[], startIndex: int, endIndex: int): int { 4210 const pivot = arr[startIndex] 4211 let i = startIndex + 1 4212 let j = endIndex - 1 4213 // No bounds check because pivot is median of three elements 4214 while ((arr[i] < pivot)) { 4215 i++ 4216 } 4217 if (i == startIndex + 1) { 4218 while (i < j && !(arr[j] < pivot)) { 4219 j-- 4220 } 4221 } else { 4222 while (!(arr[j] < pivot)) { 4223 j-- 4224 } 4225 } 4226 while (i < j) { 4227 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 4228 let tmp = arr[i] 4229 arr[i] = arr[j] 4230 arr[j] = tmp 4231 while ((arr[++i] < pivot)) {} 4232 while (!(arr[--j] < pivot)) {} 4233 } 4234 let pivotIndex = i - 1 4235 arr[startIndex] = arr[pivotIndex] 4236 arr[pivotIndex] = pivot 4237 4238 return pivotIndex 4239} 4240 4241// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4242// Elements equal to pivot go to the left 4243function quickSortSplitLeft(arr: double[], startIndex: int, endIndex: int): int { 4244 const pivot = arr[startIndex] 4245 let i = startIndex + 1 4246 let j = endIndex - 1 4247 // No bounds check because pivot is median of three elements 4248 while ((pivot < arr[j])) { 4249 j-- 4250 } 4251 if (j + 1 == endIndex) { 4252 while (i < j && !(pivot < arr[i])) { 4253 i++ 4254 } 4255 } else { 4256 while (!(pivot < arr[i])) { 4257 i++ 4258 } 4259 } 4260 while (i < j) { 4261 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 4262 let tmp = arr[i] 4263 arr[i] = arr[j] 4264 arr[j] = tmp 4265 while (!(pivot < arr[++i])) {} 4266 while ((pivot < arr[--j])) {} 4267 } 4268 arr[startIndex] = arr[j] 4269 arr[j] = pivot 4270 4271 return j 4272} 4273 4274function quickSortImpl3(arr: double[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 4275 while (endIndex - startIndex > 3) { 4276 if (--maxDepth == 0) { 4277 heapSort(arr, startIndex, endIndex) 4278 return 4279 } 4280 // Here we assume that current interval is not the most left in the sorted range 4281 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 4282 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 4283 // after that only the right part needs to be sorted 4284 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 4285 // with many occurencies of the smallest element 4286 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 4287 continue 4288 } 4289 4290 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 4291 let p = quickSortSplit(arr, startIndex, endIndex) 4292 // make a call for the smaller part of array and continue processing the larger part in the loop 4293 if (p - startIndex < endIndex - p) { 4294 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 4295 startIndex = p + 1 4296 } else { 4297 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 4298 endIndex = p 4299 } 4300 } 4301 insertionSort(arr, startIndex, endIndex, initIndex) 4302} 4303function quickSortImpl40(arr: double[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 4304 while (endIndex - startIndex > 40) { 4305 if (--maxDepth == 0) { 4306 heapSort(arr, startIndex, endIndex) 4307 return 4308 } 4309 // Here we assume that current interval is not the most left in the sorted range 4310 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 4311 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 4312 // after that only the right part needs to be sorted 4313 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 4314 // with many occurencies of the smallest element 4315 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 4316 continue 4317 } 4318 4319 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 4320 let p = quickSortSplit(arr, startIndex, endIndex) 4321 // make a call for the smaller part of array and continue processing the larger part in the loop 4322 if (p - startIndex < endIndex - p) { 4323 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 4324 startIndex = p + 1 4325 } else { 4326 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 4327 endIndex = p 4328 } 4329 } 4330 insertionSort(arr, startIndex, endIndex, initIndex) 4331} 4332 4333function quickSort(arr: double[], startIndex: int, endIndex: int): void { 4334 let size = endIndex - startIndex 4335 if (size <= 1) { 4336 return 4337 } 4338 // find log of length to fall back into determenistic O(n logn) sort 4339 let bits = 32 4340 for (let i = 2; i < 31; i++) { 4341 if ((size >> i) == 0) { 4342 bits = i 4343 break 4344 } 4345 } 4346 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 4347} 4348 4349/** 4350 * sorts arr in-place 4351 * 4352 * @param arr an array to sort 4353 * 4354 * @param startIndex an index to start sorting with, inclusive 4355 * 4356 * @param endIndex an index to end sorting, exclusive 4357 * 4358 * @example: sort array arr 4359 * ``` 4360 * sort(arr, 0, arr.length) 4361 * ``` 4362 */ 4363export function sort(arr: double[], startIndex: int, endIndex: int): void { 4364 if (!checkRange(arr.length, startIndex, endIndex)) { 4365 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 4366 } 4367 4368 quickSort(arr, startIndex, endIndex); 4369} 4370 4371function bubbleSort(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4372 let was = true 4373 while (was) { 4374 was = false 4375 for (let i = startIndex; i < endIndex - 1; i++) { 4376 if (mustPrecede(arr[i + 1], arr[i])) { 4377 const tmp = arr[i + 1] 4378 arr[i + 1] = arr[i] 4379 arr[i] = tmp 4380 was = true 4381 } 4382 } 4383 } 4384} 4385 4386function insertionSort(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean, initIndex: int = startIndex): void { 4387 if (startIndex != initIndex) { 4388 // arr[startIndex - 1] exists and is less than or equal to all elements in range 4389 for (let i = startIndex + 1; i < endIndex; i++) { 4390 const tmp = arr[i] 4391 let pos = i 4392 while (mustPrecede(tmp, arr[pos - 1])) { 4393 arr[pos] = arr[pos - 1] 4394 pos-- 4395 } 4396 arr[pos] = tmp 4397 } 4398 return 4399 } 4400 for (let i = startIndex + 1; i < endIndex; i++) { 4401 const tmp = arr[i] 4402 if (mustPrecede(tmp, arr[startIndex])) { 4403 for (let j = i; j > startIndex; j--) { 4404 arr[j] = arr[j - 1] 4405 } 4406 arr[startIndex] = tmp 4407 } else { 4408 let pos = i 4409 while (mustPrecede(tmp, arr[pos - 1])) { 4410 arr[pos] = arr[pos - 1] 4411 pos-- 4412 } 4413 arr[pos] = tmp 4414 } 4415 } 4416} 4417function heapSortUp(arr: double[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4418 const tmp = arr[startIndex + idxFromStart] 4419 while (startIndex + idxFromStart > heapRoot) { 4420 const p = (idxFromStart - 1) / 2 4421 if (!mustPrecede(arr[startIndex + p], tmp)) { 4422 break 4423 } 4424 arr[startIndex + idxFromStart] = arr[startIndex + p] 4425 idxFromStart = p 4426 } 4427 arr[startIndex + idxFromStart] = tmp 4428} 4429 4430// Build max heap with root in startIndex given its children are roots of valid heaps 4431function heapSortDown(arr: double[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4432 let heapRoot = startIndex + idxFromStart 4433 let arrIndex = heapRoot 4434 let childIndex = startIndex + idxFromStart * 2 + 1 4435 const tmp = arr[arrIndex] 4436 // Walk heap to bottom and pull max child up on each level 4437 while (childIndex + 1 < endIndex) { 4438 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 4439 childIndex++ 4440 } 4441 arr[arrIndex] = arr[childIndex] 4442 arrIndex = childIndex 4443 childIndex = childIndex * 2 - startIndex + 1 4444 } 4445 if (childIndex < endIndex) { 4446 arr[arrIndex] = arr[childIndex] 4447 arrIndex = childIndex 4448 } 4449 arr[arrIndex] = tmp 4450 // Now heap is valid in all positions but arrIndex 4451 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 4452} 4453 4454export function heapSort(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4455 let len = endIndex - startIndex 4456 for (let i = len / 2 - 1; i >= 0; i--) { 4457 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 4458 } 4459 4460 for (let i = endIndex - 1; i > startIndex; i--) { 4461 // move max element to the end of range 4462 const tmp = arr[i] 4463 arr[i] = arr[startIndex] 4464 arr[startIndex] = tmp 4465 heapSortDown(arr, 0, startIndex, i, mustPrecede) 4466 } 4467} 4468 4469// Put median of three array elements to arr[index1] 4470function median3(arr: double[], index1: int, index2: int, index3: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4471 let swap_idx = index2 4472 if (mustPrecede(arr[index1], arr[index2])) { 4473 if (mustPrecede(arr[index3], arr[index1])) { 4474 return 4475 } 4476 if (mustPrecede(arr[index3], arr[index2])) { 4477 swap_idx = index3 4478 } 4479 } else { 4480 if (!mustPrecede(arr[index3], arr[index1])) { 4481 return 4482 } 4483 if (mustPrecede(arr[index2], arr[index3])) { 4484 swap_idx = index3 4485 } 4486 } 4487 let tmp = arr[index1] 4488 arr[index1] = arr[swap_idx] 4489 arr[swap_idx] = tmp 4490} 4491 4492// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4493// Elements equal to pivot go to the right 4494function quickSortSplit(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): int { 4495 const pivot = arr[startIndex] 4496 let i = startIndex + 1 4497 let j = endIndex - 1 4498 // No bounds check because pivot is median of three elements 4499 while (mustPrecede(arr[i], pivot)) { 4500 i++ 4501 } 4502 if (i == startIndex + 1) { 4503 while (i < j && !mustPrecede(arr[j], pivot)) { 4504 j-- 4505 } 4506 } else { 4507 while (!mustPrecede(arr[j], pivot)) { 4508 j-- 4509 } 4510 } 4511 while (i < j) { 4512 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 4513 let tmp = arr[i] 4514 arr[i] = arr[j] 4515 arr[j] = tmp 4516 while (mustPrecede(arr[++i], pivot)) {} 4517 while (!mustPrecede(arr[--j], pivot)) {} 4518 } 4519 let pivotIndex = i - 1 4520 arr[startIndex] = arr[pivotIndex] 4521 arr[pivotIndex] = pivot 4522 4523 return pivotIndex 4524} 4525 4526// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4527// Elements equal to pivot go to the left 4528function quickSortSplitLeft(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): int { 4529 const pivot = arr[startIndex] 4530 let i = startIndex + 1 4531 let j = endIndex - 1 4532 // No bounds check because pivot is median of three elements 4533 while (mustPrecede(pivot, arr[j])) { 4534 j-- 4535 } 4536 if (j + 1 == endIndex) { 4537 while (i < j && !mustPrecede(pivot, arr[i])) { 4538 i++ 4539 } 4540 } else { 4541 while (!mustPrecede(pivot, arr[i])) { 4542 i++ 4543 } 4544 } 4545 while (i < j) { 4546 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 4547 let tmp = arr[i] 4548 arr[i] = arr[j] 4549 arr[j] = tmp 4550 while (!mustPrecede(pivot, arr[++i])) {} 4551 while (mustPrecede(pivot, arr[--j])) {} 4552 } 4553 arr[startIndex] = arr[j] 4554 arr[j] = pivot 4555 4556 return j 4557} 4558 4559function quickSortImpl3(arr: double[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4560 while (endIndex - startIndex > 3) { 4561 if (--maxDepth == 0) { 4562 heapSort(arr, startIndex, endIndex, mustPrecede) 4563 return 4564 } 4565 4566 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 4567 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 4568 // make a call for the smaller part of array and continue processing the larger part in the loop 4569 if (p - startIndex < endIndex - p) { 4570 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 4571 startIndex = p + 1 4572 } else { 4573 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 4574 endIndex = p 4575 } 4576 } 4577 insertionSort(arr, startIndex, endIndex, mustPrecede) 4578} 4579function quickSortImpl40(arr: double[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4580 while (endIndex - startIndex > 40) { 4581 if (--maxDepth == 0) { 4582 heapSort(arr, startIndex, endIndex, mustPrecede) 4583 return 4584 } 4585 4586 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 4587 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 4588 // make a call for the smaller part of array and continue processing the larger part in the loop 4589 if (p - startIndex < endIndex - p) { 4590 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 4591 startIndex = p + 1 4592 } else { 4593 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 4594 endIndex = p 4595 } 4596 } 4597 insertionSort(arr, startIndex, endIndex, mustPrecede) 4598} 4599 4600function quickSort(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4601 let size = endIndex - startIndex 4602 if (size <= 1) { 4603 return 4604 } 4605 // find log of length to fall back into determenistic O(n logn) sort 4606 let bits = 32 4607 for (let i = 2; i < 31; i++) { 4608 if ((size >> i) == 0) { 4609 bits = i 4610 break 4611 } 4612 } 4613 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 4614} 4615 4616/** 4617 * sorts arr in-place 4618 * 4619 * @param arr an array to sort 4620 * 4621 * @param startIndex an index to start sorting with, inclusive 4622 * 4623 * @param endIndex an index to end sorting, exclusive 4624 * 4625 * @example: sort array arr 4626 * ``` 4627 * sort(arr, 0, arr.length) 4628 * ``` 4629 */ 4630export function sort_subarray(arr: double[], startIndex: int, endIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4631 if (!checkRange(arr.length, startIndex, endIndex)) { 4632 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 4633 } 4634 4635 quickSort(arr, startIndex, endIndex, mustPrecede); 4636} 4637 4638/** 4639 * sorts arr in-place 4640 * 4641 * @param arr an array to sort 4642 */ 4643export function sort_subarray(arr: double[], mustPrecede: (lhs: double, rhs: double) => boolean): void { 4644 sort_subarray(arr, 0, arr.length, mustPrecede); 4645} 4646 4647export function sort_subarray(arr: double[], startIndex: int, mustPrecede: (lhs: double, rhs: double) => boolean): void { 4648 sort_subarray(arr, startIndex, arr.length, mustPrecede) 4649} 4650 4651export function sort(arr: double[], startIndex: int): void { 4652 sort(arr, startIndex, arr.length) 4653} 4654 4655// ======== tests section ======== 4656 4657/** note: used for tests, {@link test_sortAllOn} */ 4658class test_SortDatadouble { 4659 name: string 4660 arr: double[] 4661 4662 constructor(name: string, arr: double[]) { 4663 this.name = name 4664 this.arr = arr 4665 } 4666} 4667 4668/** note: used for tests, {@link test_sortAllOn} */ 4669function test_sortCopy(arr: double[]): double[] { 4670 let c = new double[arr.length] 4671 for (let i = 0; i < arr.length; i++) { 4672 c[i] = arr[i] 4673 } 4674 return c 4675} 4676 4677/** note: used for tests, {@link test_sortAllOn} */ 4678function test_sortAllOnCmpFwd_double(l: double, r: double): boolean { 4679 return l < r 4680} 4681 4682/** note: used for tests, {@link test_sortAllOn} */ 4683function test_sortAllOnCmpInv_double(l: double, r: double): boolean { 4684 return r < l 4685} 4686 4687/** note: used for tests, {@link test_sortAllOn} */ 4688function test_printArr(arr: double[], startIndex: int, endIndex: int) { 4689 for (let i = startIndex; i < endIndex; i++) { 4690 console.print(arr[i] + ' ') 4691 } 4692 console.println('') 4693} 4694 4695/** 4696 * Function used to test sorting in standard library tests. 4697 * There is only one exported function: `sort`, but for testing 4698 * we need access to all sub sorts that are used. Hence this part of "tests" 4699 * is located here. All related entities are prefixed whith `test_` to stand out 4700 */ 4701export function test_sortAllOn(arr: double[]) { 4702 // block for comparator `` 4703 if (true) { 4704 const sorts = new Array<test_SortDatadouble>() 4705 const bubbled = test_sortCopy(arr) 4706 bubbleSort(bubbled, 0, bubbled.length) 4707 sorts.push(new test_SortDatadouble("bubble", bubbled)) 4708 4709 const insertion = test_sortCopy(arr) 4710 insertionSort(insertion, 0, insertion.length) 4711 sorts.push(new test_SortDatadouble("insertion", insertion)) 4712 4713 const heaped = test_sortCopy(arr) 4714 heapSort(heaped, 0, heaped.length) 4715 sorts.push(new test_SortDatadouble("heap", heaped)) 4716 4717 const quicked = test_sortCopy(arr) 4718 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 4719 sorts.push(new test_SortDatadouble("quick", quicked)) 4720 4721 const quickedInsertion = test_sortCopy(arr) 4722 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 4723 sorts.push(new test_SortDatadouble("quick with insertion", quickedInsertion)) 4724 4725 const just = test_sortCopy(arr) 4726 sort(just) 4727 sorts.push(new test_SortDatadouble("sort()", just)) 4728 4729 let sort0 = sorts.at(0)! 4730 for (let s = 1; s < sorts.length; s++) { 4731 let sortS = sorts.at(s)! 4732 for (let i = 0; i < arr.length; i++) { 4733 if (sort0.arr[i] != sortS.arr[i]) { 4734 console.println(sort0.name + ': ') 4735 test_printArr(sort0.arr, 0, arr.length) 4736 console.println(sortS.name + ': ') 4737 test_printArr(sortS.arr, 0, arr.length) 4738 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for double') 4739 } 4740 } 4741 } 4742 } 4743 // block for comparator `est_sortAllOnCmpFwd_double` 4744 if (true) { 4745 const sorts = new Array<test_SortDatadouble>() 4746 const bubbled = test_sortCopy(arr) 4747 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_double) 4748 sorts.push(new test_SortDatadouble("bubble", bubbled)) 4749 4750 const insertion = test_sortCopy(arr) 4751 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_double) 4752 sorts.push(new test_SortDatadouble("insertion", insertion)) 4753 4754 const heaped = test_sortCopy(arr) 4755 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_double) 4756 sorts.push(new test_SortDatadouble("heap", heaped)) 4757 4758 const quicked = test_sortCopy(arr) 4759 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_double) 4760 sorts.push(new test_SortDatadouble("quick", quicked)) 4761 4762 const quickedInsertion = test_sortCopy(arr) 4763 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_double) 4764 sorts.push(new test_SortDatadouble("quick with insertion", quickedInsertion)) 4765 4766 const just = test_sortCopy(arr) 4767 sort_subarray(just, test_sortAllOnCmpFwd_double) 4768 sorts.push(new test_SortDatadouble("sort()", just)) 4769 4770 let sort0 = sorts.at(0)! 4771 for (let s = 1; s < sorts.length; s++) { 4772 let sortS = sorts.at(s)! 4773 for (let i = 0; i < arr.length; i++) { 4774 if (sort0.arr[i] != sortS.arr[i]) { 4775 console.println(sort0.name + ': ') 4776 test_printArr(sort0.arr, 0, arr.length) 4777 console.println(sortS.name + ': ') 4778 test_printArr(sortS.arr, 0, arr.length) 4779 throw new Error("sorts est_sortAllOnCmpFwd_double are not equal: " + sort0.name + ' ' + sortS.name + ' for double') 4780 } 4781 } 4782 } 4783 } 4784 // block for comparator `est_sortAllOnCmpInv_double` 4785 if (true) { 4786 const sorts = new Array<test_SortDatadouble>() 4787 const bubbled = test_sortCopy(arr) 4788 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_double) 4789 sorts.push(new test_SortDatadouble("bubble", bubbled)) 4790 4791 const insertion = test_sortCopy(arr) 4792 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_double) 4793 sorts.push(new test_SortDatadouble("insertion", insertion)) 4794 4795 const heaped = test_sortCopy(arr) 4796 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_double) 4797 sorts.push(new test_SortDatadouble("heap", heaped)) 4798 4799 const quicked = test_sortCopy(arr) 4800 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_double) 4801 sorts.push(new test_SortDatadouble("quick", quicked)) 4802 4803 const quickedInsertion = test_sortCopy(arr) 4804 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_double) 4805 sorts.push(new test_SortDatadouble("quick with insertion", quickedInsertion)) 4806 4807 const just = test_sortCopy(arr) 4808 sort_subarray(just, test_sortAllOnCmpInv_double) 4809 sorts.push(new test_SortDatadouble("sort()", just)) 4810 4811 let sort0 = sorts.at(0)! 4812 for (let s = 1; s < sorts.length; s++) { 4813 let sortS = sorts.at(s)! 4814 for (let i = 0; i < arr.length; i++) { 4815 if (sort0.arr[i] != sortS.arr[i]) { 4816 console.println(sort0.name + ': ') 4817 test_printArr(sort0.arr, 0, arr.length) 4818 console.println(sortS.name + ': ') 4819 test_printArr(sortS.arr, 0, arr.length) 4820 throw new Error("sorts est_sortAllOnCmpInv_double are not equal: " + sort0.name + ' ' + sortS.name + ' for double') 4821 } 4822 } 4823 } 4824 } 4825} 4826// ======== end of tests section ======== 4827 4828function bubbleSort(arr: char[], startIndex: int, endIndex: int): void { 4829 let was = true 4830 while (was) { 4831 was = false 4832 for (let i = startIndex; i < endIndex - 1; i++) { 4833 if ((arr[i + 1] < arr[i])) { 4834 const tmp = arr[i + 1] 4835 arr[i + 1] = arr[i] 4836 arr[i] = tmp 4837 was = true 4838 } 4839 } 4840 } 4841} 4842 4843function insertionSort(arr: char[], startIndex: int, endIndex: int, initIndex: int = startIndex): void { 4844 if (startIndex != initIndex) { 4845 // arr[startIndex - 1] exists and is less than or equal to all elements in range 4846 for (let i = startIndex + 1; i < endIndex; i++) { 4847 const tmp = arr[i] 4848 let pos = i 4849 while ((tmp < arr[pos - 1])) { 4850 arr[pos] = arr[pos - 1] 4851 pos-- 4852 } 4853 arr[pos] = tmp 4854 } 4855 return 4856 } 4857 for (let i = startIndex + 1; i < endIndex; i++) { 4858 const tmp = arr[i] 4859 if ((tmp < arr[startIndex])) { 4860 for (let j = i; j > startIndex; j--) { 4861 arr[j] = arr[j - 1] 4862 } 4863 arr[startIndex] = tmp 4864 } else { 4865 let pos = i 4866 while ((tmp < arr[pos - 1])) { 4867 arr[pos] = arr[pos - 1] 4868 pos-- 4869 } 4870 arr[pos] = tmp 4871 } 4872 } 4873} 4874function heapSortUp(arr: char[], idxFromStart: int, startIndex: int, heapRoot: int): void { 4875 const tmp = arr[startIndex + idxFromStart] 4876 while (startIndex + idxFromStart > heapRoot) { 4877 const p = (idxFromStart - 1) / 2 4878 if (!(arr[startIndex + p] < tmp)) { 4879 break 4880 } 4881 arr[startIndex + idxFromStart] = arr[startIndex + p] 4882 idxFromStart = p 4883 } 4884 arr[startIndex + idxFromStart] = tmp 4885} 4886 4887// Build max heap with root in startIndex given its children are roots of valid heaps 4888function heapSortDown(arr: char[], idxFromStart: int, startIndex: int, endIndex: int): void { 4889 let heapRoot = startIndex + idxFromStart 4890 let arrIndex = heapRoot 4891 let childIndex = startIndex + idxFromStart * 2 + 1 4892 const tmp = arr[arrIndex] 4893 // Walk heap to bottom and pull max child up on each level 4894 while (childIndex + 1 < endIndex) { 4895 if ((arr[childIndex] < arr[childIndex + 1])) { 4896 childIndex++ 4897 } 4898 arr[arrIndex] = arr[childIndex] 4899 arrIndex = childIndex 4900 childIndex = childIndex * 2 - startIndex + 1 4901 } 4902 if (childIndex < endIndex) { 4903 arr[arrIndex] = arr[childIndex] 4904 arrIndex = childIndex 4905 } 4906 arr[arrIndex] = tmp 4907 // Now heap is valid in all positions but arrIndex 4908 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot) 4909} 4910 4911export function heapSort(arr: char[], startIndex: int, endIndex: int): void { 4912 let len = endIndex - startIndex 4913 for (let i = len / 2 - 1; i >= 0; i--) { 4914 heapSortDown(arr, i, startIndex, endIndex) 4915 } 4916 4917 for (let i = endIndex - 1; i > startIndex; i--) { 4918 // move max element to the end of range 4919 const tmp = arr[i] 4920 arr[i] = arr[startIndex] 4921 arr[startIndex] = tmp 4922 heapSortDown(arr, 0, startIndex, i) 4923 } 4924} 4925 4926// Put median of three array elements to arr[index1] 4927function median3(arr: char[], index1: int, index2: int, index3: int): void { 4928 let swap_idx = index2 4929 if ((arr[index1] < arr[index2])) { 4930 if ((arr[index3] < arr[index1])) { 4931 return 4932 } 4933 if ((arr[index3] < arr[index2])) { 4934 swap_idx = index3 4935 } 4936 } else { 4937 if (!(arr[index3] < arr[index1])) { 4938 return 4939 } 4940 if ((arr[index2] < arr[index3])) { 4941 swap_idx = index3 4942 } 4943 } 4944 let tmp = arr[index1] 4945 arr[index1] = arr[swap_idx] 4946 arr[swap_idx] = tmp 4947} 4948 4949// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4950// Elements equal to pivot go to the right 4951function quickSortSplit(arr: char[], startIndex: int, endIndex: int): int { 4952 const pivot = arr[startIndex] 4953 let i = startIndex + 1 4954 let j = endIndex - 1 4955 // No bounds check because pivot is median of three elements 4956 while ((arr[i] < pivot)) { 4957 i++ 4958 } 4959 if (i == startIndex + 1) { 4960 while (i < j && !(arr[j] < pivot)) { 4961 j-- 4962 } 4963 } else { 4964 while (!(arr[j] < pivot)) { 4965 j-- 4966 } 4967 } 4968 while (i < j) { 4969 // Here !(arr[i] < pivot) and (arr[j] < pivot) holds 4970 let tmp = arr[i] 4971 arr[i] = arr[j] 4972 arr[j] = tmp 4973 while ((arr[++i] < pivot)) {} 4974 while (!(arr[--j] < pivot)) {} 4975 } 4976 let pivotIndex = i - 1 4977 arr[startIndex] = arr[pivotIndex] 4978 arr[pivotIndex] = pivot 4979 4980 return pivotIndex 4981} 4982 4983// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 4984// Elements equal to pivot go to the left 4985function quickSortSplitLeft(arr: char[], startIndex: int, endIndex: int): int { 4986 const pivot = arr[startIndex] 4987 let i = startIndex + 1 4988 let j = endIndex - 1 4989 // No bounds check because pivot is median of three elements 4990 while ((pivot < arr[j])) { 4991 j-- 4992 } 4993 if (j + 1 == endIndex) { 4994 while (i < j && !(pivot < arr[i])) { 4995 i++ 4996 } 4997 } else { 4998 while (!(pivot < arr[i])) { 4999 i++ 5000 } 5001 } 5002 while (i < j) { 5003 // Here (pivot < arr[i]) and !(pivot < arr[j]) holds 5004 let tmp = arr[i] 5005 arr[i] = arr[j] 5006 arr[j] = tmp 5007 while (!(pivot < arr[++i])) {} 5008 while ((pivot < arr[--j])) {} 5009 } 5010 arr[startIndex] = arr[j] 5011 arr[j] = pivot 5012 5013 return j 5014} 5015 5016function quickSortImpl3(arr: char[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 5017 while (endIndex - startIndex > 3) { 5018 if (--maxDepth == 0) { 5019 heapSort(arr, startIndex, endIndex) 5020 return 5021 } 5022 // Here we assume that current interval is not the most left in the sorted range 5023 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 5024 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 5025 // after that only the right part needs to be sorted 5026 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 5027 // with many occurencies of the smallest element 5028 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 5029 continue 5030 } 5031 5032 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 5033 let p = quickSortSplit(arr, startIndex, endIndex) 5034 // make a call for the smaller part of array and continue processing the larger part in the loop 5035 if (p - startIndex < endIndex - p) { 5036 quickSortImpl3(arr, startIndex, p, maxDepth, initIndex) 5037 startIndex = p + 1 5038 } else { 5039 quickSortImpl3(arr, p + 1, endIndex, maxDepth, initIndex) 5040 endIndex = p 5041 } 5042 } 5043 insertionSort(arr, startIndex, endIndex, initIndex) 5044} 5045function quickSortImpl40(arr: char[], startIndex: int, endIndex: int, maxDepth: int, initIndex: int = startIndex): void { 5046 while (endIndex - startIndex > 40) { 5047 if (--maxDepth == 0) { 5048 heapSort(arr, startIndex, endIndex) 5049 return 5050 } 5051 // Here we assume that current interval is not the most left in the sorted range 5052 if (startIndex != initIndex && arr[startIndex - 1] >= arr[startIndex]) { 5053 // We call quickSortSplitLeft here to move all elements equal to pivot (and arr[startIndex - 1]) to the left part; 5054 // after that only the right part needs to be sorted 5055 // If we always used quickSortSplitLeft instead of quickSortSplit, this would not work well for array 5056 // with many occurencies of the smallest element 5057 startIndex = quickSortSplitLeft(arr, startIndex, endIndex) + 1 5058 continue 5059 } 5060 5061 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2) 5062 let p = quickSortSplit(arr, startIndex, endIndex) 5063 // make a call for the smaller part of array and continue processing the larger part in the loop 5064 if (p - startIndex < endIndex - p) { 5065 quickSortImpl40(arr, startIndex, p, maxDepth, initIndex) 5066 startIndex = p + 1 5067 } else { 5068 quickSortImpl40(arr, p + 1, endIndex, maxDepth, initIndex) 5069 endIndex = p 5070 } 5071 } 5072 insertionSort(arr, startIndex, endIndex, initIndex) 5073} 5074 5075function quickSort(arr: char[], startIndex: int, endIndex: int): void { 5076 let size = endIndex - startIndex 5077 if (size <= 1) { 5078 return 5079 } 5080 // find log of length to fall back into determenistic O(n logn) sort 5081 let bits = 32 5082 for (let i = 2; i < 31; i++) { 5083 if ((size >> i) == 0) { 5084 bits = i 5085 break 5086 } 5087 } 5088 quickSortImpl40(arr, startIndex, endIndex, bits * 3) 5089} 5090 5091/** 5092 * sorts arr in-place 5093 * 5094 * @param arr an array to sort 5095 * 5096 * @param startIndex an index to start sorting with, inclusive 5097 * 5098 * @param endIndex an index to end sorting, exclusive 5099 * 5100 * @example: sort array arr 5101 * ``` 5102 * sort(arr, 0, arr.length) 5103 * ``` 5104 */ 5105export function sort(arr: char[], startIndex: int, endIndex: int): void { 5106 if (!checkRange(arr.length, startIndex, endIndex)) { 5107 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 5108 } 5109 5110 quickSort(arr, startIndex, endIndex); 5111} 5112 5113function bubbleSort(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5114 let was = true 5115 while (was) { 5116 was = false 5117 for (let i = startIndex; i < endIndex - 1; i++) { 5118 if (mustPrecede(arr[i + 1], arr[i])) { 5119 const tmp = arr[i + 1] 5120 arr[i + 1] = arr[i] 5121 arr[i] = tmp 5122 was = true 5123 } 5124 } 5125 } 5126} 5127 5128function insertionSort(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean, initIndex: int = startIndex): void { 5129 if (startIndex != initIndex) { 5130 // arr[startIndex - 1] exists and is less than or equal to all elements in range 5131 for (let i = startIndex + 1; i < endIndex; i++) { 5132 const tmp = arr[i] 5133 let pos = i 5134 while (mustPrecede(tmp, arr[pos - 1])) { 5135 arr[pos] = arr[pos - 1] 5136 pos-- 5137 } 5138 arr[pos] = tmp 5139 } 5140 return 5141 } 5142 for (let i = startIndex + 1; i < endIndex; i++) { 5143 const tmp = arr[i] 5144 if (mustPrecede(tmp, arr[startIndex])) { 5145 for (let j = i; j > startIndex; j--) { 5146 arr[j] = arr[j - 1] 5147 } 5148 arr[startIndex] = tmp 5149 } else { 5150 let pos = i 5151 while (mustPrecede(tmp, arr[pos - 1])) { 5152 arr[pos] = arr[pos - 1] 5153 pos-- 5154 } 5155 arr[pos] = tmp 5156 } 5157 } 5158} 5159function heapSortUp(arr: char[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5160 const tmp = arr[startIndex + idxFromStart] 5161 while (startIndex + idxFromStart > heapRoot) { 5162 const p = (idxFromStart - 1) / 2 5163 if (!mustPrecede(arr[startIndex + p], tmp)) { 5164 break 5165 } 5166 arr[startIndex + idxFromStart] = arr[startIndex + p] 5167 idxFromStart = p 5168 } 5169 arr[startIndex + idxFromStart] = tmp 5170} 5171 5172// Build max heap with root in startIndex given its children are roots of valid heaps 5173function heapSortDown(arr: char[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5174 let heapRoot = startIndex + idxFromStart 5175 let arrIndex = heapRoot 5176 let childIndex = startIndex + idxFromStart * 2 + 1 5177 const tmp = arr[arrIndex] 5178 // Walk heap to bottom and pull max child up on each level 5179 while (childIndex + 1 < endIndex) { 5180 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 5181 childIndex++ 5182 } 5183 arr[arrIndex] = arr[childIndex] 5184 arrIndex = childIndex 5185 childIndex = childIndex * 2 - startIndex + 1 5186 } 5187 if (childIndex < endIndex) { 5188 arr[arrIndex] = arr[childIndex] 5189 arrIndex = childIndex 5190 } 5191 arr[arrIndex] = tmp 5192 // Now heap is valid in all positions but arrIndex 5193 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 5194} 5195 5196export function heapSort(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5197 let len = endIndex - startIndex 5198 for (let i = len / 2 - 1; i >= 0; i--) { 5199 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 5200 } 5201 5202 for (let i = endIndex - 1; i > startIndex; i--) { 5203 // move max element to the end of range 5204 const tmp = arr[i] 5205 arr[i] = arr[startIndex] 5206 arr[startIndex] = tmp 5207 heapSortDown(arr, 0, startIndex, i, mustPrecede) 5208 } 5209} 5210 5211// Put median of three array elements to arr[index1] 5212function median3(arr: char[], index1: int, index2: int, index3: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5213 let swap_idx = index2 5214 if (mustPrecede(arr[index1], arr[index2])) { 5215 if (mustPrecede(arr[index3], arr[index1])) { 5216 return 5217 } 5218 if (mustPrecede(arr[index3], arr[index2])) { 5219 swap_idx = index3 5220 } 5221 } else { 5222 if (!mustPrecede(arr[index3], arr[index1])) { 5223 return 5224 } 5225 if (mustPrecede(arr[index2], arr[index3])) { 5226 swap_idx = index3 5227 } 5228 } 5229 let tmp = arr[index1] 5230 arr[index1] = arr[swap_idx] 5231 arr[swap_idx] = tmp 5232} 5233 5234// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 5235// Elements equal to pivot go to the right 5236function quickSortSplit(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): int { 5237 const pivot = arr[startIndex] 5238 let i = startIndex + 1 5239 let j = endIndex - 1 5240 // No bounds check because pivot is median of three elements 5241 while (mustPrecede(arr[i], pivot)) { 5242 i++ 5243 } 5244 if (i == startIndex + 1) { 5245 while (i < j && !mustPrecede(arr[j], pivot)) { 5246 j-- 5247 } 5248 } else { 5249 while (!mustPrecede(arr[j], pivot)) { 5250 j-- 5251 } 5252 } 5253 while (i < j) { 5254 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 5255 let tmp = arr[i] 5256 arr[i] = arr[j] 5257 arr[j] = tmp 5258 while (mustPrecede(arr[++i], pivot)) {} 5259 while (!mustPrecede(arr[--j], pivot)) {} 5260 } 5261 let pivotIndex = i - 1 5262 arr[startIndex] = arr[pivotIndex] 5263 arr[pivotIndex] = pivot 5264 5265 return pivotIndex 5266} 5267 5268// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 5269// Elements equal to pivot go to the left 5270function quickSortSplitLeft(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): int { 5271 const pivot = arr[startIndex] 5272 let i = startIndex + 1 5273 let j = endIndex - 1 5274 // No bounds check because pivot is median of three elements 5275 while (mustPrecede(pivot, arr[j])) { 5276 j-- 5277 } 5278 if (j + 1 == endIndex) { 5279 while (i < j && !mustPrecede(pivot, arr[i])) { 5280 i++ 5281 } 5282 } else { 5283 while (!mustPrecede(pivot, arr[i])) { 5284 i++ 5285 } 5286 } 5287 while (i < j) { 5288 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 5289 let tmp = arr[i] 5290 arr[i] = arr[j] 5291 arr[j] = tmp 5292 while (!mustPrecede(pivot, arr[++i])) {} 5293 while (mustPrecede(pivot, arr[--j])) {} 5294 } 5295 arr[startIndex] = arr[j] 5296 arr[j] = pivot 5297 5298 return j 5299} 5300 5301function quickSortImpl3(arr: char[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5302 while (endIndex - startIndex > 3) { 5303 if (--maxDepth == 0) { 5304 heapSort(arr, startIndex, endIndex, mustPrecede) 5305 return 5306 } 5307 5308 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 5309 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 5310 // make a call for the smaller part of array and continue processing the larger part in the loop 5311 if (p - startIndex < endIndex - p) { 5312 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 5313 startIndex = p + 1 5314 } else { 5315 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 5316 endIndex = p 5317 } 5318 } 5319 insertionSort(arr, startIndex, endIndex, mustPrecede) 5320} 5321function quickSortImpl40(arr: char[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5322 while (endIndex - startIndex > 40) { 5323 if (--maxDepth == 0) { 5324 heapSort(arr, startIndex, endIndex, mustPrecede) 5325 return 5326 } 5327 5328 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 5329 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 5330 // make a call for the smaller part of array and continue processing the larger part in the loop 5331 if (p - startIndex < endIndex - p) { 5332 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 5333 startIndex = p + 1 5334 } else { 5335 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 5336 endIndex = p 5337 } 5338 } 5339 insertionSort(arr, startIndex, endIndex, mustPrecede) 5340} 5341 5342function quickSort(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5343 let size = endIndex - startIndex 5344 if (size <= 1) { 5345 return 5346 } 5347 // find log of length to fall back into determenistic O(n logn) sort 5348 let bits = 32 5349 for (let i = 2; i < 31; i++) { 5350 if ((size >> i) == 0) { 5351 bits = i 5352 break 5353 } 5354 } 5355 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 5356} 5357 5358/** 5359 * sorts arr in-place 5360 * 5361 * @param arr an array to sort 5362 * 5363 * @param startIndex an index to start sorting with, inclusive 5364 * 5365 * @param endIndex an index to end sorting, exclusive 5366 * 5367 * @example: sort array arr 5368 * ``` 5369 * sort(arr, 0, arr.length) 5370 * ``` 5371 */ 5372export function sort_subarray(arr: char[], startIndex: int, endIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5373 if (!checkRange(arr.length, startIndex, endIndex)) { 5374 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 5375 } 5376 5377 quickSort(arr, startIndex, endIndex, mustPrecede); 5378} 5379 5380/** 5381 * sorts arr in-place 5382 * 5383 * @param arr an array to sort 5384 */ 5385export function sort_subarray(arr: char[], mustPrecede: (lhs: char, rhs: char) => boolean): void { 5386 sort_subarray(arr, 0, arr.length, mustPrecede); 5387} 5388 5389export function sort_subarray(arr: char[], startIndex: int, mustPrecede: (lhs: char, rhs: char) => boolean): void { 5390 sort_subarray(arr, startIndex, arr.length, mustPrecede) 5391} 5392 5393export function sort(arr: char[], startIndex: int): void { 5394 sort(arr, startIndex, arr.length) 5395} 5396 5397// ======== tests section ======== 5398 5399/** note: used for tests, {@link test_sortAllOn} */ 5400class test_SortDatachar { 5401 name: string 5402 arr: char[] 5403 5404 constructor(name: string, arr: char[]) { 5405 this.name = name 5406 this.arr = arr 5407 } 5408} 5409 5410/** note: used for tests, {@link test_sortAllOn} */ 5411function test_sortCopy(arr: char[]): char[] { 5412 let c = new char[arr.length] 5413 for (let i = 0; i < arr.length; i++) { 5414 c[i] = arr[i] 5415 } 5416 return c 5417} 5418 5419/** note: used for tests, {@link test_sortAllOn} */ 5420function test_sortAllOnCmpFwd_char(l: char, r: char): boolean { 5421 return l < r 5422} 5423 5424/** note: used for tests, {@link test_sortAllOn} */ 5425function test_sortAllOnCmpInv_char(l: char, r: char): boolean { 5426 return r < l 5427} 5428 5429/** note: used for tests, {@link test_sortAllOn} */ 5430function test_printArr(arr: char[], startIndex: int, endIndex: int) { 5431 for (let i = startIndex; i < endIndex; i++) { 5432 console.print(arr[i] + ' ') 5433 } 5434 console.println('') 5435} 5436 5437/** 5438 * Function used to test sorting in standard library tests. 5439 * There is only one exported function: `sort`, but for testing 5440 * we need access to all sub sorts that are used. Hence this part of "tests" 5441 * is located here. All related entities are prefixed whith `test_` to stand out 5442 */ 5443export function test_sortAllOn(arr: char[]) { 5444 // block for comparator `` 5445 if (true) { 5446 const sorts = new Array<test_SortDatachar>() 5447 const bubbled = test_sortCopy(arr) 5448 bubbleSort(bubbled, 0, bubbled.length) 5449 sorts.push(new test_SortDatachar("bubble", bubbled)) 5450 5451 const insertion = test_sortCopy(arr) 5452 insertionSort(insertion, 0, insertion.length) 5453 sorts.push(new test_SortDatachar("insertion", insertion)) 5454 5455 const heaped = test_sortCopy(arr) 5456 heapSort(heaped, 0, heaped.length) 5457 sorts.push(new test_SortDatachar("heap", heaped)) 5458 5459 const quicked = test_sortCopy(arr) 5460 quickSortImpl3(quicked, 0, quicked.length, quicked.length) 5461 sorts.push(new test_SortDatachar("quick", quicked)) 5462 5463 const quickedInsertion = test_sortCopy(arr) 5464 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length) 5465 sorts.push(new test_SortDatachar("quick with insertion", quickedInsertion)) 5466 5467 const just = test_sortCopy(arr) 5468 sort(just) 5469 sorts.push(new test_SortDatachar("sort()", just)) 5470 5471 let sort0 = sorts.at(0)! 5472 for (let s = 1; s < sorts.length; s++) { 5473 let sortS = sorts.at(s)! 5474 for (let i = 0; i < arr.length; i++) { 5475 if (sort0.arr[i] != sortS.arr[i]) { 5476 console.println(sort0.name + ': ') 5477 test_printArr(sort0.arr, 0, arr.length) 5478 console.println(sortS.name + ': ') 5479 test_printArr(sortS.arr, 0, arr.length) 5480 throw new Error("sorts are not equal: " + sort0.name + ' ' + sortS.name + ' for char') 5481 } 5482 } 5483 } 5484 } 5485 // block for comparator `est_sortAllOnCmpFwd_char` 5486 if (true) { 5487 const sorts = new Array<test_SortDatachar>() 5488 const bubbled = test_sortCopy(arr) 5489 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpFwd_char) 5490 sorts.push(new test_SortDatachar("bubble", bubbled)) 5491 5492 const insertion = test_sortCopy(arr) 5493 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpFwd_char) 5494 sorts.push(new test_SortDatachar("insertion", insertion)) 5495 5496 const heaped = test_sortCopy(arr) 5497 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpFwd_char) 5498 sorts.push(new test_SortDatachar("heap", heaped)) 5499 5500 const quicked = test_sortCopy(arr) 5501 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpFwd_char) 5502 sorts.push(new test_SortDatachar("quick", quicked)) 5503 5504 const quickedInsertion = test_sortCopy(arr) 5505 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpFwd_char) 5506 sorts.push(new test_SortDatachar("quick with insertion", quickedInsertion)) 5507 5508 const just = test_sortCopy(arr) 5509 sort_subarray(just, test_sortAllOnCmpFwd_char) 5510 sorts.push(new test_SortDatachar("sort()", just)) 5511 5512 let sort0 = sorts.at(0)! 5513 for (let s = 1; s < sorts.length; s++) { 5514 let sortS = sorts.at(s)! 5515 for (let i = 0; i < arr.length; i++) { 5516 if (sort0.arr[i] != sortS.arr[i]) { 5517 console.println(sort0.name + ': ') 5518 test_printArr(sort0.arr, 0, arr.length) 5519 console.println(sortS.name + ': ') 5520 test_printArr(sortS.arr, 0, arr.length) 5521 throw new Error("sorts est_sortAllOnCmpFwd_char are not equal: " + sort0.name + ' ' + sortS.name + ' for char') 5522 } 5523 } 5524 } 5525 } 5526 // block for comparator `est_sortAllOnCmpInv_char` 5527 if (true) { 5528 const sorts = new Array<test_SortDatachar>() 5529 const bubbled = test_sortCopy(arr) 5530 bubbleSort(bubbled, 0, bubbled.length, test_sortAllOnCmpInv_char) 5531 sorts.push(new test_SortDatachar("bubble", bubbled)) 5532 5533 const insertion = test_sortCopy(arr) 5534 insertionSort(insertion, 0, insertion.length, test_sortAllOnCmpInv_char) 5535 sorts.push(new test_SortDatachar("insertion", insertion)) 5536 5537 const heaped = test_sortCopy(arr) 5538 heapSort(heaped, 0, heaped.length, test_sortAllOnCmpInv_char) 5539 sorts.push(new test_SortDatachar("heap", heaped)) 5540 5541 const quicked = test_sortCopy(arr) 5542 quickSortImpl3(quicked, 0, quicked.length, quicked.length, test_sortAllOnCmpInv_char) 5543 sorts.push(new test_SortDatachar("quick", quicked)) 5544 5545 const quickedInsertion = test_sortCopy(arr) 5546 quickSortImpl40(quickedInsertion, 0, quickedInsertion.length, quickedInsertion.length, test_sortAllOnCmpInv_char) 5547 sorts.push(new test_SortDatachar("quick with insertion", quickedInsertion)) 5548 5549 const just = test_sortCopy(arr) 5550 sort_subarray(just, test_sortAllOnCmpInv_char) 5551 sorts.push(new test_SortDatachar("sort()", just)) 5552 5553 let sort0 = sorts.at(0)! 5554 for (let s = 1; s < sorts.length; s++) { 5555 let sortS = sorts.at(s)! 5556 for (let i = 0; i < arr.length; i++) { 5557 if (sort0.arr[i] != sortS.arr[i]) { 5558 console.println(sort0.name + ': ') 5559 test_printArr(sort0.arr, 0, arr.length) 5560 console.println(sortS.name + ': ') 5561 test_printArr(sortS.arr, 0, arr.length) 5562 throw new Error("sorts est_sortAllOnCmpInv_char are not equal: " + sort0.name + ' ' + sortS.name + ' for char') 5563 } 5564 } 5565 } 5566 } 5567} 5568// ======== end of tests section ======== 5569 5570function bubbleSort(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5571 let was = true 5572 while (was) { 5573 was = false 5574 for (let i = startIndex; i < endIndex - 1; i++) { 5575 if (mustPrecede(arr[i + 1], arr[i])) { 5576 const tmp = arr[i + 1] 5577 arr[i + 1] = arr[i] 5578 arr[i] = tmp 5579 was = true 5580 } 5581 } 5582 } 5583} 5584 5585function insertionSort(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean, initIndex: int = startIndex): void { 5586 if (startIndex != initIndex) { 5587 // arr[startIndex - 1] exists and is less than or equal to all elements in range 5588 for (let i = startIndex + 1; i < endIndex; i++) { 5589 const tmp = arr[i] 5590 let pos = i 5591 while (mustPrecede(tmp, arr[pos - 1])) { 5592 arr[pos] = arr[pos - 1] 5593 pos-- 5594 } 5595 arr[pos] = tmp 5596 } 5597 return 5598 } 5599 for (let i = startIndex + 1; i < endIndex; i++) { 5600 const tmp = arr[i] 5601 if (mustPrecede(tmp, arr[startIndex])) { 5602 for (let j = i; j > startIndex; j--) { 5603 arr[j] = arr[j - 1] 5604 } 5605 arr[startIndex] = tmp 5606 } else { 5607 let pos = i 5608 while (mustPrecede(tmp, arr[pos - 1])) { 5609 arr[pos] = arr[pos - 1] 5610 pos-- 5611 } 5612 arr[pos] = tmp 5613 } 5614 } 5615} 5616function heapSortUp(arr: NullishType[], idxFromStart: int, startIndex: int, heapRoot: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5617 const tmp = arr[startIndex + idxFromStart] 5618 while (startIndex + idxFromStart > heapRoot) { 5619 const p = (idxFromStart - 1) / 2 5620 if (!mustPrecede(arr[startIndex + p], tmp)) { 5621 break 5622 } 5623 arr[startIndex + idxFromStart] = arr[startIndex + p] 5624 idxFromStart = p 5625 } 5626 arr[startIndex + idxFromStart] = tmp 5627} 5628 5629// Build max heap with root in startIndex given its children are roots of valid heaps 5630function heapSortDown(arr: NullishType[], idxFromStart: int, startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5631 let heapRoot = startIndex + idxFromStart 5632 let arrIndex = heapRoot 5633 let childIndex = startIndex + idxFromStart * 2 + 1 5634 const tmp = arr[arrIndex] 5635 // Walk heap to bottom and pull max child up on each level 5636 while (childIndex + 1 < endIndex) { 5637 if (mustPrecede(arr[childIndex], arr[childIndex + 1])) { 5638 childIndex++ 5639 } 5640 arr[arrIndex] = arr[childIndex] 5641 arrIndex = childIndex 5642 childIndex = childIndex * 2 - startIndex + 1 5643 } 5644 if (childIndex < endIndex) { 5645 arr[arrIndex] = arr[childIndex] 5646 arrIndex = childIndex 5647 } 5648 arr[arrIndex] = tmp 5649 // Now heap is valid in all positions but arrIndex 5650 heapSortUp(arr, arrIndex - startIndex, startIndex, heapRoot, mustPrecede) 5651} 5652 5653export function heapSort(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5654 let len = endIndex - startIndex 5655 for (let i = len / 2 - 1; i >= 0; i--) { 5656 heapSortDown(arr, i, startIndex, endIndex, mustPrecede) 5657 } 5658 5659 for (let i = endIndex - 1; i > startIndex; i--) { 5660 // move max element to the end of range 5661 const tmp = arr[i] 5662 arr[i] = arr[startIndex] 5663 arr[startIndex] = tmp 5664 heapSortDown(arr, 0, startIndex, i, mustPrecede) 5665 } 5666} 5667 5668// Put median of three array elements to arr[index1] 5669function median3(arr: NullishType[], index1: int, index2: int, index3: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5670 let swap_idx = index2 5671 if (mustPrecede(arr[index1], arr[index2])) { 5672 if (mustPrecede(arr[index3], arr[index1])) { 5673 return 5674 } 5675 if (mustPrecede(arr[index3], arr[index2])) { 5676 swap_idx = index3 5677 } 5678 } else { 5679 if (!mustPrecede(arr[index3], arr[index1])) { 5680 return 5681 } 5682 if (mustPrecede(arr[index2], arr[index3])) { 5683 swap_idx = index3 5684 } 5685 } 5686 let tmp = arr[index1] 5687 arr[index1] = arr[swap_idx] 5688 arr[swap_idx] = tmp 5689} 5690 5691// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 5692// Elements equal to pivot go to the right 5693function quickSortSplit(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): int { 5694 const pivot = arr[startIndex] 5695 let i = startIndex + 1 5696 let j = endIndex - 1 5697 // No bounds check because pivot is median of three elements 5698 while (mustPrecede(arr[i], pivot)) { 5699 i++ 5700 } 5701 if (i == startIndex + 1) { 5702 while (i < j && !mustPrecede(arr[j], pivot)) { 5703 j-- 5704 } 5705 } else { 5706 while (!mustPrecede(arr[j], pivot)) { 5707 j-- 5708 } 5709 } 5710 while (i < j) { 5711 // Here !mustPrecede(arr[i], pivot) and mustPrecede(arr[j], pivot) holds 5712 let tmp = arr[i] 5713 arr[i] = arr[j] 5714 arr[j] = tmp 5715 while (mustPrecede(arr[++i], pivot)) {} 5716 while (!mustPrecede(arr[--j], pivot)) {} 5717 } 5718 let pivotIndex = i - 1 5719 arr[startIndex] = arr[pivotIndex] 5720 arr[pivotIndex] = pivot 5721 5722 return pivotIndex 5723} 5724 5725// Split range [startIndex, endIndex) by pivot arr[startIndex] and return pivot position 5726// Elements equal to pivot go to the left 5727function quickSortSplitLeft(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): int { 5728 const pivot = arr[startIndex] 5729 let i = startIndex + 1 5730 let j = endIndex - 1 5731 // No bounds check because pivot is median of three elements 5732 while (mustPrecede(pivot, arr[j])) { 5733 j-- 5734 } 5735 if (j + 1 == endIndex) { 5736 while (i < j && !mustPrecede(pivot, arr[i])) { 5737 i++ 5738 } 5739 } else { 5740 while (!mustPrecede(pivot, arr[i])) { 5741 i++ 5742 } 5743 } 5744 while (i < j) { 5745 // Here mustPrecede(pivot, arr[i]) and !mustPrecede(pivot, arr[j]) holds 5746 let tmp = arr[i] 5747 arr[i] = arr[j] 5748 arr[j] = tmp 5749 while (!mustPrecede(pivot, arr[++i])) {} 5750 while (mustPrecede(pivot, arr[--j])) {} 5751 } 5752 arr[startIndex] = arr[j] 5753 arr[j] = pivot 5754 5755 return j 5756} 5757 5758function quickSortImpl3(arr: NullishType[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5759 while (endIndex - startIndex > 3) { 5760 if (--maxDepth == 0) { 5761 heapSort(arr, startIndex, endIndex, mustPrecede) 5762 return 5763 } 5764 5765 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 5766 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 5767 // make a call for the smaller part of array and continue processing the larger part in the loop 5768 if (p - startIndex < endIndex - p) { 5769 quickSortImpl3(arr, startIndex, p, maxDepth, mustPrecede) 5770 startIndex = p + 1 5771 } else { 5772 quickSortImpl3(arr, p + 1, endIndex, maxDepth, mustPrecede) 5773 endIndex = p 5774 } 5775 } 5776 insertionSort(arr, startIndex, endIndex, mustPrecede) 5777} 5778function quickSortImpl40(arr: NullishType[], startIndex: int, endIndex: int, maxDepth: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5779 while (endIndex - startIndex > 40) { 5780 if (--maxDepth == 0) { 5781 heapSort(arr, startIndex, endIndex, mustPrecede) 5782 return 5783 } 5784 5785 median3(arr, startIndex, endIndex - 1, (startIndex + endIndex) / 2, mustPrecede) 5786 let p = quickSortSplit(arr, startIndex, endIndex, mustPrecede) 5787 // make a call for the smaller part of array and continue processing the larger part in the loop 5788 if (p - startIndex < endIndex - p) { 5789 quickSortImpl40(arr, startIndex, p, maxDepth, mustPrecede) 5790 startIndex = p + 1 5791 } else { 5792 quickSortImpl40(arr, p + 1, endIndex, maxDepth, mustPrecede) 5793 endIndex = p 5794 } 5795 } 5796 insertionSort(arr, startIndex, endIndex, mustPrecede) 5797} 5798 5799function quickSort(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5800 let size = endIndex - startIndex 5801 if (size <= 1) { 5802 return 5803 } 5804 // find log of length to fall back into determenistic O(n logn) sort 5805 let bits = 32 5806 for (let i = 2; i < 31; i++) { 5807 if ((size >> i) == 0) { 5808 bits = i 5809 break 5810 } 5811 } 5812 quickSortImpl40(arr, startIndex, endIndex, bits * 3, mustPrecede) 5813} 5814 5815/** 5816 * sorts arr in-place 5817 * 5818 * @param arr an array to sort 5819 * 5820 * @param startIndex an index to start sorting with, inclusive 5821 * 5822 * @param endIndex an index to end sorting, exclusive 5823 * 5824 * @example: sort array arr 5825 * ``` 5826 * sort(arr, 0, arr.length) 5827 * ``` 5828 */ 5829export function sort_subarray(arr: NullishType[], startIndex: int, endIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5830 if (!checkRange(arr.length, startIndex, endIndex)) { 5831 throw new ArrayIndexOutOfBoundsError("sort: bounds verification failed") 5832 } 5833 5834 quickSort(arr, startIndex, endIndex, mustPrecede); 5835} 5836 5837/** 5838 * sorts arr in-place 5839 * 5840 * @param arr an array to sort 5841 */ 5842export function sort_subarray(arr: NullishType[], mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5843 sort_subarray(arr, 0, arr.length, mustPrecede); 5844} 5845 5846export function sort_subarray(arr: NullishType[], startIndex: int, mustPrecede: (lhs: NullishType, rhs: NullishType) => boolean): void { 5847 sort_subarray(arr, startIndex, arr.length, mustPrecede) 5848} 5849 5850