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