Author Topic: CodeGuy Standard Sorting Library (Because QB64 dot net happens)  (Read 8089 times)

0 Members and 1 Guest are viewing this topic.

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« on: September 04, 2018, 09:45:56 pm »
This is the code to my library, which I've coded, tested, refactored, benchmarked and retested until ABSOLUTELY certain of its correctness. This includes my venerated HashListSort(), numeric sorting algorithm which SMOKES FlashSort by considerable (15-20)% or more margins. There is also a couple of selection algorithms (QuickSelect, FlashSelect) and some basic statistical stuff too. All the standards like Heap, Merge and Quick (recursive, iterative, Median of 3, Dual-pivot) and MANY others just too numerous to list without the aid of some text processing. All have been commented to aid in understanding and I have written a test harness that tests for varying N, ascending, descending and sequence checking for verification. Some algorithms like 1-0 sort, CountingSort and such are VERY limited in their range and mostly for demo purposes. Most can be adapted for things other than numbers. In the demo, I included their complexity class, so you can see how good sorts perform versus the really not-so-good and how the not-so-good can be EXTREMELY useful in SOME special cases. When appending to my library, only the QuickSort SUB is necessary. This code is self-contained and can be used as is with modifications for data type if necessary. TESTED, VERIFIED, WORKS.

Code: QB64: [Select]
  1. _TITLE "CGSortAlgorithmsLibraryAndTest12i"
  2. '* Quora: Is there an algorithm to fimd duplicate elements in an array in O(N) time? Of COURSE there is. There are actually MANY that are
  3. '* not only efficient, but relatively simple. The simplest is a HashTable. However, this requires knowledge beforehand of the number of
  4. '* elements constant reconstruction of the HashTable on exceeding the efficient Load Factor, aka "emptiness," which is roughly 20%. Past
  5. '* this, searching and probing become worse than the typical O(1.25) cited in many research papers and backed by personal experience.
  6. '* The second involves application of a VERY fast sorting method and either traversal or partial sort and binary search of the remainder.
  7. '* There is no algorithm that does this in less than O(N) time, although there are asymptotically close heuristics.
  8. '* my answer assumes no prior knowledge of data type beyond numeric.
  9. '* first, we will start with the typical element by element search. VERY slow and complexity is O(N^2).
  10. '* yes, this is abysmally slow, so testing only with a small number of elements.
  11. '* this example includes my ENTIRE library of sorting algorithms, some slightly modified from standard.
  12. '* all tested and verified are indicated by a date stamp
  13. '* tested/verified
  14. '*******************************************************************************************************************************************************************
  15. 'Date tested O()     Algorithm name                                                                 time(ms)  in-place  deployment special notes
  16. '2018-Mar-13 N       FlashSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)         117  N         requires inversion for descending order
  17.  
  18. '2018 Mar 13 NLogN   QuickSortRecursive (CGSortLibArr() AS DOUBLE, start&, finish&, order&)              148  Y         Requires CPU stack, uses middle array element for
  19. '                                                                                                                       partitioning step.
  20.  
  21. '2018 Mar 13 NLogN   QSortRecursiveSimplified (CGSortLibArr() AS DOUBLE, start&, finish&)                160  Y         Requires CPU stack
  22.  
  23. '2018nMar 13 NLogN   QuickSortIterativeMedianOf3 (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)     161  Y         Uses software-based stack, Median of Three
  24. '                                                                                                                       Partitioning strategy used to defeat "QuickSort
  25. '                                                                                                                       Killer" array arrangements for QuickSort algorithms
  26. '                                                                                                                       using the middle element as the sole pivot chice.
  27. '                                                                                                                       Remember, DDoS attacks using this flaw in Java?
  28.  
  29. '2018 Mar 13 NLogN   QuickSortIterative (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)              164  Y         Uses software-based stack, handy for old CPUs that do
  30. '                                                                                                                       not support recursive stacks in hardware. Or just to
  31. '                                                                                                                       implement for certainty where hardware or virtualization
  32. '                                                                                                                       is not guaranteed to support hardware stacks.
  33. '
  34.  
  35. '2018 Mar 13 N       HashListSort (CGSortLibArr() AS DOUBLE, start AS LONG, Finish AS LONG, order&)      171  N         Can be implemented without CGSortLibArr() with mods
  36. '                                                                                                                       With the data type and range in this original demo
  37. '                                                                                                                       HashListSort actually BEATS FlashSort by at least
  38. '                                                                                                                       an 11% margin. Don't let this fool you. This is the
  39. '                                                                                                                       result of a SINGLE run, and generalizing on a single
  40. '                                                                                                                       run is not a good idea, which is why I assembled a
  41. '                                                                                                                       test harness using multiple passes and ascending,
  42. '                                                                                                                       descending order.
  43.  
  44. '2018 Mar 13 NLogN   IntroSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                       226  N         uses MergeSort, HeapSort, InsertionSort and performs
  45. '                                                                                                                       comparably and favorably to non-hybrid QuickSort,
  46. '                                                                                                                       usually within a few percent or less.
  47.  
  48. '2018 Mar 13 NLogN   QuickSortDualPivot (CGSortLibArr() AS DOUBLE, start&, finish&, order&)              244  Y         Not bulletproof but works for most cases of highly
  49. '                                                                                                                       repetitive data fails for low-repetition data.
  50.  
  51. '2018 Mar 13 NLongN  SnakeSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                       250  N         Auxiliary memory is O(N). Also a very nice-performing
  52. '                                                                                                                       algorithm. Not the fastest (yes, compared to HashListSort
  53. '                                                                                                                       with 70ms @ (0, 131071) elements, not even FlashSort can
  54. '                                                                                                                       keep up.
  55.  
  56. '2018 Mar 13 NLogN   MergeSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                       257  N         Auxiliary memory is O(N/2) when used with
  57. '                                                                                                                       EfficientMerge
  58.  
  59. '2018 Mar 13 NLogN   MergeSortTwoWay (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                 287  N         Good for memory-constrained systems
  60.  
  61. '2018 Mar 13 N       RadixSort (a() AS DOUBLE, start&, finish&, order&)                                  296  N         Only for integers, otherwise it will use MergeSort
  62. '                                                                                                                       to maintain RadixSort's stability
  63.  
  64. '2018 Mar 14         BucketSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&, recurse%)            280  N         Without recursion, 100 times slower 20812ns
  65. '                                                                                                                       Final subarray sort done with MergeSort keeps this
  66. '                                                                                                                       algorithm competitive.
  67.  
  68. '2018 Mar 13         SinglePassShellSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)             335  Y         Got this idea from reading LOTS of articles. Performs
  69. '                                                                                                                       respectably.
  70.  
  71. '2018 Mar 13         PrimeGapSort2 (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                   343  Y         Invented by CodeGuy/Zom-B, uses wheel factorization
  72. '                                                                                                                       to generate primes.
  73.  
  74. '2018 Mar 13         PostSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                        351  N         Large auxiliary overhead. Final sort of subarrays
  75. '                                                                                                                       done with MergeSort also keeps this algorithm competitive
  76. '                                                                                                                       Like BucketSort, except that it uses a fixed number of
  77. '                                                                                                                       buckets. Using fewwer actually increases speed, at 1
  78. '                                                                                                                       Bucket, it's essentially a MergeSort.
  79.  
  80. '2018 Mar 13         PrimeGapSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                    382  Y         Invented by CodeGuy. Proud to declare PrimeGapSort
  81. '                                                                                                                       is competitive and performs on par with ShellSort or
  82. '                                                                                                                       better. Uses gaps that are prime.
  83.  
  84. '2018 Mar 13         JoinSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                        484  N         A respectably quick algorithm. Also, not the fastest
  85. '                                                                                                                       but for a comparison sort, good enough.
  86.  
  87. '2018 Mar 13 NLogN   HeapSort (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)                        492  Y
  88.  
  89. '2018 Mar 13         ShellSortMetzner (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                500  Y         With this variant, it is appreciably faster than ShellSort.
  90.  
  91. '2018-Mar-13         ShellSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                       546  Y
  92.  
  93. '2018 Mar 13         CombSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                        898  Y
  94.  
  95. '2018 Mar 13 Nlog^2N BatcherOddEvenMergeSort (CGSortLibArr() AS DOUBLE, Start&, Finish&)                1093  Y         Only works for power-of-2 sized arrays
  96.  
  97. '2018 Mar 13         SmoothSort (TypedCGSortLibArr() AS DataElement, order&)                            1292  Y         requires use of TYPE array) and only 0 to ubound.
  98. '                                                                                                                       no ranges
  99. '2018-Mar 13         ShellSortBidirectional (CGSortLibArr() AS DOUBLE, start&, finish&, order&)         2421  Y
  100.  
  101. '2018 Mar 13         BitonicSort (CGSortLibArr() AS DOUBLE, lo&, n&, dir&)                              2609  Y
  102.  
  103. '2018-Mar-13 N^2     InsertionSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                229133  Y         Very fast for nearly-sorted arrays. Used as finishing
  104. '                                                                                                                       run for many ShellSort variations.
  105.  
  106. '2018 Mar 13 N^2     InsertionSortBinary (CGSortLibArr() AS DOUBLE, start&, finish&, order&)          330328  Y         Supposedly faster than InsertionSort. Using randomized
  107. '                                                                                                                       Double-precision, generally non-repeating, not proven
  108. '                                                                                                                       in practice.
  109.  
  110. '2018 Mar 13 N^2     CycleSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                    784852  Y
  111.  
  112. '            N^2     bubblesort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                      ------
  113. '            N^2     CocktailSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                    ------
  114. '            N^2     SelectionSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                   ------
  115. '            N^2     InsertionSortx (CGSortLibArr() AS DOUBLE, start&, finish&, order&)                  ------
  116. '                    FlashSORTType (CGSortLibArr() AS FlashRec, start AS LONG, finish AS LONG, order&)   ------ as yet untested. An experimental algorithm for use with
  117. '                                                                                                       string-type variables
  118.  
  119. '* InsertionSort(), BubbleSort(), MergeSort() are considered stable. The remainder either are not or as yet unconfirmed
  120. '* I ran these individually and corrected flaws. Sorts that have times have been tested in ascending/descending order
  121. '* this is a work in progress. The algorithms marked by ------ are too slow to be practical for this demo code
  122. '* Tested on double-precision data.
  123. '*******************************************************************************************************************************************************************
  124. '[code=qb64]
  125.     DIM crlf AS STRING
  126.     crlf = CHR$(13) + CHR$(10)
  127.     io& = FREEFILE
  128.     OPEN "CGSortAlgorithmsLibraryAndTest12i.bas" FOR INPUT AS io&
  129.     casecount& = 0
  130.     copy$ = "select case SortChoose%" + crlf
  131.     WHILE NOT EOF(io&)
  132.         LINE INPUT #io&, t$
  133.         t$ = LTRIM$(t$)
  134.         IF LCASE$(LEFT$(t$, 4)) = "sub " THEN
  135.             t$ = LCASE$(MID$(t$, 5))
  136.             IF INSTR(1, LCASE$(t$), "sort") THEN
  137.                 p& = INSTR(t$, "(")
  138.                 IF INSTR(t$, "sort") < p& THEN
  139.                     IF INSTR(t$, "sort") > 0 THEN
  140.                         copy$ = copy$ + "case" + STR$(casecount&) + crlf
  141.                         ASC(t$, p&) = 32
  142.                         ASC(t$, LEN(t$)) = 32
  143.                         copy$ = copy$ + t$ + crlf
  144.                         casecount& = casecount& + 1
  145.                     END IF
  146.                 END IF
  147.             END IF
  148.         END IF
  149.     WEND
  150.     copy$ = copy$ + "end select" + crlf
  151.     CLOSE io&
  152.     _CLIPBOARD$ = copy$
  153. '* this TYPE declaration MUST appear in your code to use my library
  154. TYPE MinMaxRec
  155.     min AS LONG
  156.     max AS LONG
  157.  
  158. '* for Stabilized smoothsort
  159. TYPE DataElement
  160.     thekey AS DOUBLE
  161.     originalorder AS LONG
  162.     'Name AS STRING * 32
  163. '* to here
  164.  
  165. TYPE SortPerfRec
  166.     Name AS STRING * 40
  167.     AccumulatedTime AS DOUBLE
  168.     runs AS LONG
  169.     Index AS INTEGER
  170.     accnum AS DOUBLE
  171. REDIM SortResults(0 TO 255) AS SortPerfRec
  172. REDIM PerformThis%(0 TO 255)
  173. FOR s& = LBOUND(performthis%) TO UBOUND(performthis%)
  174.     PerformThis%(s&) = -1
  175. SortTestN& = 63
  176. SortThreshhold& = 16
  177. pgmh& = _NEWIMAGE(1366, 768, 32)
  178. SCREEN pgmh&
  179. outsf& = _LOADFONT("c:\windows\fonts\cour.ttf", 14, "monospace")
  180. _FONT outsf&
  181.  
  182.     REDIM _PRESERVE TestArrayType(0 TO 0) AS DataElement
  183.     REDIM _PRESERVE TestCGSortLibArr(0 TO SortTestN&) AS DOUBLE
  184.     REDIM _PRESERVE ElementCounts(0 TO SortTestN&) AS LONG '* only used for demo code
  185.  
  186.     Main_Sorted_From_N& = LBOUND(TestCGSortLibArr)
  187.     Main_Sorted_To_N& = UBOUND(TestCGSortLibArr)
  188.  
  189.     IF 0 THEN '* 104s is JUST too long for 65536 elements
  190.         t! = TIMER(.001)
  191.         FOR SearchTestArrayIndex& = LBOUND(TestArray) TO UBOUND(testarray) - 1
  192.             '* skip this element if it has already been found
  193.             IF ElementCounts(SearcTestArrayIndex&) <> -1 THEN
  194.                 FOR SearchNextOccurrence& = SearchTestOccurrence& + 1 TO UBOUND(testarray)
  195.                     IF ElementCounts(SearchNextOccurrence&) <> -1 THEN
  196.                         IF TestCGSortLibArr(SearchTestArrayIndex&) = TestCGSortLibArr(SearchNextOccurrence&) THEN
  197.                             ElementCounts(SearchTestArrayIndex&) = ElementCounts(SearchTestArrayIndex&) + 1
  198.                             ElementCounts(SearchNextOccurrence&) = -1
  199.                         END IF
  200.                     END IF
  201.                 NEXT
  202.             END IF
  203.             IF ElementCounts(SearchTestArrayIndex&) > 1 THEN
  204.                 PRINT "("; TestCGSortLibArr(SearchTestArrayIndex&); ElementCounts(SearchTestArrayIndex&); ")";
  205.             END IF
  206.         NEXT
  207.         u! = TIMER(.001)
  208.         PRINT DeltaTime(t!, u!)
  209.     END IF
  210.  
  211.     '_FULLSCREEN
  212.     '* now we get a bit more clever and use a sorting method that isn't QuickSort, to bypass its potentially O(N^2) performance
  213.     '* One very fast algorithm posted in prior posts, KD Neubert FlashSort() comes to mind. Fast? Damn near O(N).
  214.     '* the new fastest sort I know of, invented by CodeGuy, beats FlashSort often enough to call a winner, especially for very
  215.     '* large N on repetitive sets.
  216.  
  217.     NTrials& = 0 '* 1 less than you really want
  218.     FOR SortChoose% = 0 TO UBOUND(sortresults)
  219.         IF PerformThis%(SortChoose%) THEN
  220.             FOR SetTestArray& = Main_Sorted_From_N& TO Main_Sorted_To_N&
  221.                 TestCGSortLibArr(SetTestArray&) = RND '*Main_Sorted_To_N& - SetTestArray& 'INT(RND * 1048576) AND 1
  222.                 'TestArrayType(SetTestArray&).thekey = INT(256 * RND) '(RND * 1023) AND 1023
  223.                 'TestArrayType(SetTestArray&).originalorder = SetTestArray&
  224.             NEXT
  225.             LOCATE 3, 1: PRINT SortResults(SortChoose%).Name;
  226.             FOR sortdir& = 1 TO 1 STEP 2
  227.                 FOR passes& = 0 TO NTrials&
  228.  
  229.                     $CHECKING:OFF
  230.                     'KnuthShuffle TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&
  231.                     '* TestCGSortLibArr
  232.                     u! = TIMER(.001)
  233.                     WHILE TIMER(.001) = u!
  234.                     WEND
  235.                     u! = TIMER(.001)
  236.                     SELECT CASE SortChoose%
  237.                         CASE 0
  238.                             SortResults(SortChoose%).Name = "[s+][i-][n    ]Post"
  239.                             PostSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  240.                         CASE 1
  241.                             SortResults(SortChoose%).Name = "[s-][i-][n    ]Flash"
  242.                             FlashSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  243.                         CASE 2
  244.                             SortResults(SortChoose%).Name = "[s-][i+][*****]Shell"
  245.                             ShellSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  246.                         CASE 3
  247.                             SortResults(SortChoose%).Name = "[s-][i+][*****]ShellBidirectional"
  248.                             ShellSortBidirectional TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  249.                         CASE 4
  250.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]QuickRecursive"
  251.                             QuickSortRecursive TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  252.                         CASE 5
  253.                             SortResults(SortChoose%).Name = "[s-][i+][NlogN]QuickIterative"
  254.                             QuickSortIterative TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  255.                         CASE 6
  256.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]QuickDualPivot"
  257.                             QuickSortDualPivot TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  258.                         CASE 7
  259.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]MergeRoutine"
  260.                             MergeSortRoutine TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  261.                         CASE 8
  262.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]Bubble"
  263.                             BubbleSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  264.                         CASE 9
  265.                             SortResults(SortChoose%).Name = "[s-][i+][n^2  ]Cocktail"
  266.                             CocktailSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  267.                         CASE 10
  268.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]InsertionBinary"
  269.                             InsertionSortBinary TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  270.                         CASE 11
  271.                             SortResults(SortChoose%).Name = "[s+][i-][n^1  ]Bucket"
  272.                             r% = 1
  273.                             BucketSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&, r%
  274.                         CASE 12
  275.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]Heap"
  276.                             HeapSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  277.                         CASE 13
  278.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]QuickIntrospective"
  279.                             QuickSortIntrospective TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  280.                         CASE 14
  281.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]BubbleModified"
  282.                             BubbleSortModified TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  283.                         CASE 15
  284.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]MergeTwoWay"
  285.                             MergeSortTwoWay TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  286.                         CASE 16
  287.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]TreeUsingBST"
  288.                             TreeSortUsingBST TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  289.                             'SortResults(SortChoose%).Name = "DistCountingSort"
  290.                             'CountingSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  291.                             'flashstring TestArrayType() ,Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  292.                             '_CONTINUE
  293.                         CASE 17
  294.                             SortResults(SortChoose%).Name = "[s-][i+][*****]PrimeGap2(codeGuy/Zom-B)"
  295.                             primeGapSort2 TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  296.                         CASE 18
  297.                             SortResults(SortChoose%).Name = "[s-][i+][*****]Comb"
  298.                             CombSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  299.                         CASE 19
  300.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]Selection"
  301.                             SelectionSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  302.                         CASE 20
  303.                             SortResults(SortChoose%).Name = "[s-][i+][n^2  ]Cycle"
  304.                             cycleSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  305.                         CASE 21
  306.                             SortResults(SortChoose%).Name = "[s-][i+][*****]ShellMetzner"
  307.                             shellSortMetzner TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  308.                         CASE -22
  309.                             SortResults(SortChoose%).Name = "[s-][i+][*****]PrimeGap"
  310.                             PrimeGapSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  311.                         CASE 23
  312.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]Insertion"
  313.                             InsertionSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  314.                         CASE 24
  315.                             SortResults(SortChoose%).Name = "[s-][i-][n    ]HashList(CodeGuy)"
  316.                             HashListSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  317.                         CASE 25
  318.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]Radix"
  319.                             RadixSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  320.                         CASE -26
  321.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]BatcherOddEvenMerge"
  322.                             BatcherOddEvenMergeSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  323.                         CASE 27
  324.                             SortResults(SortChoose%).Name = "[s-][i+][*****]ShellSinglePass"
  325.                             SinglePassShellSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  326.                         CASE 28
  327.                             SortResults(SortChoose%).Name = "[s-][i+][*****]Bitonic"
  328.                             BitonicSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  329.                         CASE 29
  330.                             SortResults(SortChoose%).Name = "[s-][i-][NLogN]Snake"
  331.                             SnakeSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  332.                         CASE 30
  333.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]Tim=========>"
  334.                             TimSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  335.                             'SortResults(SortChoose%).Name = "DistCountingSort"
  336.                             'DIM T_minmax AS MinMaxRec: Tscale# = 1
  337.                             'CGScaleArrayToInteger TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&, T_minmax, Tscale#
  338.                             'CGFrequencyCounts TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&, T_minmax, Tscale#
  339.  
  340.                             '_CONTINUE
  341.                         CASE 31
  342.                             SortResults(SortChoose%).Name = "[s-][i+][*****]Join"
  343.                             JoinSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  344.                         CASE 32
  345.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]QuickSimplifiedRecursive"
  346.                             QSortRecursiveSimplified TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  347.                         CASE 33
  348.                             '* Edsgar Djikstra's SmoothSort
  349.                             SortResults(SortChoose%).Name = "[s+][i+][*****]Smooth_TypedArray"
  350.                             s! = TIMER(.001)
  351.                             REDIM TestArrayType(0 TO Main_Sorted_To_N&)
  352.                             FOR s& = LBOUND(TestArrayType) TO UBOUND(TestArrayType)
  353.                                 TestArrayType(s&).originalorder = s&
  354.                                 TestArrayType(s&).thekey = TestCGSortLibArr(s&)
  355.                             NEXT
  356.                             t! = TIMER(.001)
  357.                             SmoothSort_TypedArray TestArrayType(), sortdir&
  358.                             x! = TIMER(.001)
  359.                             FOR s& = LBOUND(TestArrayType) TO UBOUND(TestArrayType)
  360.                                 TestCGSortLibArr(s&) = TestArrayType(s&).thekey
  361.                             NEXT
  362.                             REDIM TestArrayType(0 TO 0)
  363.                             y! = TIMER(.001)
  364.                             subtracttime! = DeltaTime(x!, y!) + DeltaTime!(s!, t!)
  365.                             SortResults(SortChoose%).AccumulatedTime = SortResults(SortChoose%).AccumulatedTime - subtracttime!
  366.                         CASE 34
  367.                             SortResults(SortChoose%).Name = "[s-][i+][n^2  ]Gnome"
  368.                             GnomeSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  369.                         CASE 35
  370.                             SortResults(SortChoose%).Name = "[s-][i+][NLogN]QuickMedianOf3It"
  371.                             QuickSortIterativeMedianOf3 TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  372.                         CASE 36
  373.                             SortResults(SortChoose%).Name = "[s-][i+][n^2  ]SelectionUnstable"
  374.                             SelectionSortUnstable TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  375.                         CASE 37
  376.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]InsertionRecursive"
  377.                             InsertionSortRecursive TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  378.                         CASE 38
  379.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]MergeEmerge"
  380.                             MergeSortEmerge TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  381.                         CASE 39
  382.                             '* necessary because this routine eats a LOT of stack
  383.                             IF Main_Sorted_To_N& - Main_Sorted_From_N& > 8191 THEN
  384.                                 PerformThis%(SortChoose%) = 0
  385.                             ELSE
  386.                                 SortResults(SortChoose%).Name = "[s+][i+][n^2  ]BubbleRecursive"
  387.                                 BubbleSortRecursive TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir
  388.                             END IF
  389.                         CASE 40
  390.                             SortResults(SortChoose%).Name = "[s+][i+][n^2  ]BubbleRecursiveEmerge<-------"
  391.                             BubbleSortRecursiveEmerge TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  392.                         CASE 41
  393.                             SortResults(SortChoose%).Name = "[s+][i-][NLogN]MergeSortEfficient->"
  394.                             MergeSortEfficient TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  395.                         CASE -42
  396.                             SortResults(SortChoose%).Name = "[s+][i-][N    ]CountingInteger"
  397.                             CountingSortInteger TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  398.                         CASE -43
  399.                             SortResults(SortChoose%).Name = "[s+][i-][N    ]CountingNonInteger"
  400.                             CountingSortNonInteger TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  401.                         CASE -44
  402.                             SortResults(SortChoose%).Name = "[s+][i-][N    ]BeadInteger"
  403.                             BeadSortInteger TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  404.                         CASE -45
  405.                             SortResults(SortChoose%).Name = "[s+][i-][N    ]BeadNonInteger"
  406.                             BeadSortNonInteger TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  407.                         CASE 46
  408.                             SortResults(SortChoose%).Name = "[s-][i+][N^2  ]Pancake"
  409.                             PancakeSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  410.                         CASE 47
  411.                             SortResults(SortChoose%).Name = "[s-][i+][N^2  ]PrimeGap2(Split)"
  412.                             PrimeGapSort2Split TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  413.                         CASE -48
  414.                             SortResults(SortChoose%).Name = "[s-][i+][N^2  ]OneZero"
  415.                             OneZeroSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  416.                         CASE -49
  417.                             SortResults(SortChoose%).Name = "[s+][i+][N    ]UniqueInteger"
  418.                             UniqueIntegerSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  419.                         CASE 50
  420.                             SortResults(SortChoose%).Name = "[s-][i-][N    ]FlashSortGMMA"
  421.                             FlashSortGMMA TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  422.                         CASE 51
  423.                             SortResults(SortChoose%).Name = "[s+][i+][NLogN]MergeInsert"
  424.                             MergeInsert TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  425.                         CASE -52
  426.                             SortResults(SortChoose%).Name = "[s+][i+][N^2  ]ExchangeSort"
  427.                             ExchangeSort TestCGSortLibArr(), Main_Sorted_From_N&, Main_Sorted_To_N&, sortdir&
  428.                         CASE ELSE
  429.                             _CONTINUE
  430.                     END SELECT
  431.                     v! = TIMER(.001)
  432.                     IF ArraySequenceCheck&(TestCGSortLibArr(), LBOUND(Testarray), UBOUND(Testarray), sortdir&) THEN
  433.                         w! = TIMER(.001)
  434.                         CountArrayRepetitions TestCGSortLibArr(), LBOUND(Testarray), UBOUND(Testarray)
  435.                         x! = TIMER(.001)
  436.                         LOCATE 1, 1
  437.                         PRINT USING "## "; SortChoose%;
  438.                         PRINT SortResults(SortChoose%).Name;
  439.                         IF sortdir& = 1 THEN
  440.                             PRINT "- ascending";
  441.                         ELSE
  442.                             PRINT "-descending";
  443.                         END IF
  444.                         PRINT USING "####.##########  "; DeltaTime!(u!, v!)
  445.                         SortResults(SortChoose%).AccumulatedTime = SortResults(SortChoose%).AccumulatedTime + DeltaTime!(u!, v!)
  446.                         SortResults(SortChoose%).runs = SortResults(SortChoose%).runs + 1
  447.                         SortResults(SortChoose%).Index = SortChoose%
  448.                         SortResults(SortChoose%).accnum = SortResults(SortChoose%).accnum + SortTestN& + 1
  449.                     END IF
  450.                     $CHECKING:ON
  451.                 NEXT
  452.             NEXT
  453.         END IF
  454.     NEXT
  455.     '* an example of using the DataElement sorts
  456.     REDIM Results(0 TO UBOUND(sortresults)) AS DataElement
  457.     Rcount& = LBOUND(results)
  458.     FOR s& = 0 TO UBOUND(results)
  459.         IF PerformThis%(s&) THEN
  460.             IF SortResults(s&).AccumulatedTime > 0 THEN
  461.                 IF SortResults(s&).runs > 0 THEN
  462.                     Results(Rcount&).originalorder = s&
  463.                     Results(Rcount&).thekey = CDBL(SortResults(s&).AccumulatedTime / SortResults(s&).accnum)
  464.                     Rcount& = Rcount& + 1
  465.                 END IF
  466.             END IF
  467.         END IF
  468.     NEXT
  469.  
  470.     IF Rcount& > 0 THEN
  471.         CLS
  472.         Rcount& = Rcount& - 1
  473.         REDIM _PRESERVE Results(LBOUND(results) TO Rcount&) AS DataElement
  474.         '* give Djikstra some props and use his sort to do an ascending order sort on Results()
  475.         SmoothSort_TypedArray Results(), 1
  476.         '* index to the fastest performing sort
  477.         LOCATE 2, 1: PRINT "legend: [s]table, [i]n-place [complexity class]";
  478.         LOCATE 4, 1
  479.         PRINT USING "n=###,###,###,###"; (Main_Sorted_To_N& - Main_Sorted_From_N& + 1);
  480.         LOCATE 5, 1
  481.         FirstOrder& = Results(LBOUND(Results)).originalorder
  482.         halforder& = Results(LBOUND(results) + (UBOUND(results) - LBOUND(results) + 1) \ 2).originalorder
  483.         FOR s& = LBOUND(Results) TO UBOUND(results)
  484.             'COLOR (s& MOD 8) + 1, 1, 1
  485.             p& = Results(s&).originalorder
  486.             PRINT SortResults(p&).Name;
  487.             PRINT USING "####.######## "; SortResults(p&).AccumulatedTime;
  488.             PRINT USING "####.######## "; SortResults(p&).AccumulatedTime / SortResults(p&).accnum;
  489.             PRINT USING "####.############# "; SortResults(p&).AccumulatedTime / SortResults(FirstOrder&).AccumulatedTime
  490.             IF SortResults(p&).AccumulatedTime / SortResults(halforder&).AccumulatedTime > SortThreshhold& THEN
  491.                 PerformThis%(p&) = 0
  492.             END IF
  493.         NEXT
  494.         'COLOR 2, 1, 1
  495.     END IF
  496.  
  497.     '**********************
  498.     'CLS
  499.     'PRINT "N="; LTRIM$(STR$(Main_Sorted_To_N& - Main_Sorted_From_N& + 1))
  500.     'FOR h& = 0 TO 255
  501.     '    IF SortResults(h&).runs > 0 THEN
  502.     '        PRINT SortResults(h&).Name;
  503.     '        PRINT USING "avg ###.###########"; SortResults(h&).AccumulatedTime / SortResults(h&).runs;
  504.     '        'PRINT SortResults(h&).runs;
  505.     '        'PRINT SortResults(h&).Index;
  506.     '        PRINT USING "####.############## Index"; SortResults(h&).AccumulatedTime / SortResults(0).AccumulatedTime
  507.     '    END IF
  508.     'NEXT
  509.     '* does what it says
  510.     '**********************
  511.     SortTestN& = SortTestN& * 2 + 1
  512. LOOP 'UNTIL SortTestN& > 16777215
  513.  
  514. SUB CountArrayRepetitions (CGSortLibArr() AS DOUBLE, start&, finish&)
  515.     REDIM ElementCounts(0 TO 0) AS LONG
  516.     REDIM ElementPointers(0 TO 0) AS LONG
  517.     ProbeCount& = LBOUND(CGSortLibArr)
  518.     ElementCountIndex& = LBOUND(ElementCounts)
  519.     s& = start&
  520.     DO
  521.         IF s& > finish& THEN
  522.             EXIT DO
  523.         ELSE
  524.             ElementPointers(ElementCountIndex&) = s&
  525.             r& = s&
  526.             DO
  527.                 IF r& > finish& THEN
  528.                     EXIT DO
  529.                 ELSE
  530.                     IF CGSortLibArr(r&) = CGSortLibArr(s&) THEN
  531.                         ElementCounts(ElementCountIndex&) = ElementCounts(ElementCountIndex&) + 1
  532.                         r& = r& + 1
  533.                     ELSE
  534.                         EXIT DO
  535.                     END IF
  536.                 END IF
  537.             LOOP
  538.             s& = r&
  539.             ElementCountIndex& = ElementCountIndex& + 1
  540.             IF ElementCountIndex& > UBOUND(ElementCounts) THEN
  541.                 REDIM _PRESERVE ElementCounts(LBOUND(elementcounts) TO ElementCountIndex&)
  542.                 REDIM _PRESERVE ElementPointers(LBOUND(ElementPointers) TO ElementCountIndex&)
  543.             END IF
  544.         END IF
  545.     LOOP
  546.     IF 0 THEN
  547.         FOR s& = LBOUND(elementcounts) TO UBOUND(elementcounts)
  548.             PRINT "("; CGSortLibArr(ElementPointers(s&)); ElementCounts(s&); ")";
  549.         NEXT
  550.     END IF
  551.  
  552. '*****************************************************************************************************************
  553.  
  554. '*******************************
  555. '* The Tiny Library Starts Here:
  556. '*******************************
  557. '* answers the question, what's the Kth smallest element of an array of numbers. Generally
  558. '* regarded as an O(n) algorithm, provided the array is not already in order, otherwise it
  559. '* COULD become O(n^2) (think bubble, cycle or selection sorts, all of which are SLOW on
  560. '* unordered datasets.
  561.  
  562. SUB OrderStatisticK (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, OSK_k)
  563.     DIM OSK_PivotX AS DOUBLE '* MUST be same type as element of CGSortLibArr()
  564.     '* These MUST be same type as start and finish
  565.     DIM OSK_i AS LONG
  566.     DIM OSK_j AS LONG
  567.     DIM OSK_k AS LONG
  568.     DIM OSK_lower AS LONG
  569.     DIM OSK_upper AS LONG
  570.     '*********************************************
  571.     OSK_lower = start
  572.     OSK_upper = finish - 1
  573.     WHILE OSK_lower < OSK_upper
  574.         OSK_i = OSK_lower
  575.         OSK_j = OSK_upper
  576.         OSK_PivotX = CGSortLibArr(OSK_k)
  577.         WHILE OSK_i <= OSK_k AND OSK_j >= OSK_k
  578.             WHILE CGSortLibArr(OSK_i) < OSK_PivotX
  579.                 OSK_i = OSK_i + 1
  580.             WEND
  581.             WHILE CGSortLibArr(OSK_j) > OSK_PivotX
  582.                 OSK_j = OSK_j - 1
  583.             WEND
  584.             SWAP CGSortLibArr(OSK_i), CGSortLibArr(OSK_j)
  585.             OSK_i = OSK_i + 1
  586.             OSK_j = OSK_j - 1
  587.         WEND
  588.         IF OSK_j < OSK_k THEN
  589.             OSK_lower = OSK_i
  590.         END IF
  591.         IF OSK_i > OSK_k THEN
  592.             OSK_upper = OSK_j
  593.         END IF
  594.     WEND
  595.  
  596. '******************************************
  597. '* still VERY competitive until N>16,777,216
  598. '* however, now dethroned by HashListSort
  599. ' sorts CGSortLibArr() with Start& to Finish& elements by use of
  600. ' index vector L with M elements, with M ca. 0.1 Finish&.
  601. ' Translation of Karl-Dietrich Neubert's FlashSort
  602. ' algorithm into BASIC by Erdmann Hess.
  603. ' Arbitrary numeric type version.
  604.  
  605. ' This WAS the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  606. ' strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  607. ' namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  608. ' kept popping up. Traced it to L() and added a minor (about 2.4%) increase in the upper bound of L(). I suppose this
  609. ' could also be used for non-integer and non-string types as well.
  610.  
  611. '* KD Neubert FlashSort. Incredibly FAST numeric sort. This is a distribution sort, like BucketSort or PostSort, except far less overhead
  612. '* in memory. Refactored By CodeGuy for the best clarity I can possibly provide. The original version has a .125(upperbound-lowerbound) array,
  613. '* but was changed to .128(upperbound-lowerbound) avoid array bound errors. Tested. Fast. Works.
  614. '*********************************************
  615. SUB FlashSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  616.     '* change these:
  617.     DIM hold AS DOUBLE
  618.     DIM flash AS DOUBLE
  619.     DIM ANMiN AS DOUBLE
  620.     '* to the same type as the array being sorted
  621.  
  622.     '* change these:
  623.     DIM KIndex AS LONG
  624.     DIM MIndex AS LONG
  625.     DIM SIndex AS LONG
  626.     '* to long for qbxx as qbxx has no _unsigned types
  627.  
  628.     '* the original ratio was .125 but i kept getting array bounds errors
  629.     MIndex = (INT(.128 * (finish - start + 1)) + 1) OR 2
  630.  
  631.     '* change these:
  632.     DIM FlashTrackL(0 TO MIndex) AS LONG
  633.     DIM FlashI AS LONG
  634.     DIM FlashJ AS LONG
  635.     DIM NextFlashJ AS LONG
  636.     DIM FlashNMove AS LONG
  637.     DIM MaxValueIndex AS LONG
  638.     DIM MinValueIndex AS LONG
  639.     DIM FinishMinusOne AS LONG
  640.     '* to the appropriate type for the range being sorted (must match start, finish variables)
  641.  
  642.     '* don't mess:
  643.     DIM FlashC1 AS DOUBLE '* for some reason does not work with _float
  644.     '* with this. it needs to be a double at the very least but float gives this a far greater range
  645.     '* more than likely more range than is practical. but ya never know (change this to double for qbxx)
  646.  
  647.     ' sorts array A with finish elements by use of
  648.     ' index vector L with M elements, with M ca. 0.128(finish-start).
  649.     ' Translation of Karl-Dietrich Neubert's FlashSort
  650.     ' algorithm into BASIC by Erdmann Hess.
  651.     ' Generalized Numeric Version -- recoded by codeguy
  652.  
  653.     '* This is the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  654.     '* strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  655.     '* namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  656.     '* kept popping up. Traced it to FlashTrackL() and added a minor (about 2.4&) increase in the upper bound of FlashTrackL().
  657.     '* I suppose this could also be used for non-integer and non-string types as well. Note: For very large N, HashListSort()
  658.     '* works even faster and has a similar memory footprint. But yes, this is still faster than QuickSort for N>10000 and like
  659.     '* HashListSort, operates in asymptotically close to O(N) time.
  660.  
  661.     REM =============== CLASS FORMATION =================
  662.  
  663.     ANMiN = CGSortLibArr(start)
  664.     MaxValueIndex = finish
  665.     MinValueIndex = start
  666.     FOR FlashI = start TO finish
  667.         IF (CGSortLibArr(FlashI) > CGSortLibArr(MaxValueIndex)) THEN MaxValueIndex = FlashI
  668.         IF (CGSortLibArr(FlashI) < CGSortLibArr(MinValueIndex)) THEN MinValueIndex = FlashI
  669.     NEXT FlashI
  670.     SWAP CGSortLibArr(MinValueIndex), CGSortLibArr(start): MinValueIndex = start: ANMiN = CGSortLibArr(MinValueIndex)
  671.     SWAP CGSortLibArr(MaxValueIndex), CGSortLibArr(finish): MaxValueIndex = finish
  672.  
  673.     IF ANMiN = CGSortLibArr(MaxValueIndex) THEN
  674.         '* this is a monotonic sequence array and by definition is already sorted
  675.         EXIT SUB
  676.     END IF
  677.  
  678.     DIM FlashTrackL(MIndex)
  679.     FlashC1 = (MIndex - 1) / (CGSortLibArr(MaxValueIndex) - ANMiN)
  680.  
  681.     FOR FlashI = start + 1 TO finish - 1
  682.         KIndex = INT(FlashC1 * (CGSortLibArr(FlashI) - ANMiN)) + 1
  683.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + 1
  684.     NEXT
  685.  
  686.     FOR KIndex = LBOUND(FlashTrackL) + 1 TO MIndex
  687.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + FlashTrackL(KIndex - 1)
  688.     NEXT KIndex
  689.  
  690.     REM ==================== PERMUTATION ================
  691.     FlashNMove = 0
  692.     FlashJ = start + 1
  693.     KIndex = MIndex
  694.     FinishMinusOne = finish - 1
  695.     WHILE (FlashNMove < FinishMinusOne)
  696.         WHILE (FlashJ > FlashTrackL(KIndex))
  697.             FlashJ = FlashJ + 1
  698.             KIndex = INT(FlashC1 * (CGSortLibArr(FlashJ) - ANMiN)) + 1
  699.         WEND
  700.         flash = CGSortLibArr(FlashJ)
  701.         DO
  702.             IF (FlashJ = (FlashTrackL(KIndex) + 1)) THEN
  703.                 EXIT DO
  704.             ELSE
  705.                 IF FlashNMove < (FinishMinusOne) THEN
  706.                     KIndex = INT(FlashC1 * (flash - ANMiN)) + 1
  707.                     hold = CGSortLibArr(FlashTrackL(KIndex))
  708.                     CGSortLibArr(FlashTrackL(KIndex)) = flash
  709.                     flash = hold
  710.                     FlashTrackL(KIndex) = FlashTrackL(KIndex) - 1
  711.                     FlashNMove = FlashNMove + 1
  712.                 ELSE
  713.                     EXIT DO
  714.                 END IF
  715.             END IF
  716.         LOOP
  717.     WEND
  718.     '================= Insertion Sort============
  719.     FOR SIndex = LBOUND(FlashtrackL) + 1 TO MIndex
  720.         '* sort subranges
  721.         '********************* insertionsortz CGSortLibArr(), FlashTrackL(SIndex - 1), FlashTrackL(SIndex) - 1, order&
  722.         FOR FlashI = FlashTrackL(SIndex) - 1 TO FlashTrackL(SIndex - 1) STEP -1
  723.             IF (CGSortLibArr(FlashI + 1) < CGSortLibArr(FlashI)) THEN
  724.                 hold = CGSortLibArr(FlashI)
  725.                 NextFlashJ = FlashI
  726.                 DO
  727.                     FlashJ = NextFlashJ
  728.                     IF FlashJ < FlashTrackL(SIndex) THEN
  729.                         NextFlashJ = FlashJ + 1
  730.                         IF (CGSortLibArr(NextFlashJ) < hold) THEN
  731.                             SWAP CGSortLibArr(FlashJ), CGSortLibArr(NextFlashJ)
  732.                         ELSE
  733.                             EXIT DO
  734.                         END IF
  735.                     ELSE
  736.                         EXIT DO
  737.                     END IF
  738.                 LOOP
  739.                 CGSortLibArr(FlashJ) = hold
  740.             END IF
  741.         NEXT
  742.         '* 914k/Ghz when it reaches this point, assuming this array is mostly sorted.
  743.     NEXT
  744.     EXIT SUB
  745.     IF order& = 1 THEN EXIT SUB
  746.     FlashI = start
  747.     FlashJ = finish
  748.     WHILE FlashJ > FlashI
  749.         SWAP CGSortLibArr(FlashI), CGSortLibArr(FlashJ)
  750.         FlashI = FlashI + 1
  751.         FlashJ = FlashJ - 1
  752.     WEND
  753.  
  754. '********************
  755. '* InsertionSort is a simple to construct sort. Generally because of its O(n^2) running time, it's usually limited to VERY short runs
  756. '* or used as a final sorting stage of many sorts. it is stable. The advantage of this sort for nearly sorted arrays is it runs in nearly O(n) time.
  757. '* InsertionSort is adaptive, meaning it takes advantage of pre-existing order. Modified for faster performance on already-sorted data 21 Apr 2018.
  758. '********************
  759. SUB InsertionSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  760.     DIM InSort_Local_ArrayTemp AS DOUBLE
  761.     DIM InSort_Local_i AS LONG
  762.     DIM InSort_Local_j AS LONG
  763.     SELECT CASE order&
  764.         CASE 1
  765.             FOR InSort_Local_i = start + 1 TO finish
  766.                 InSort_Local_j = InSort_Local_i - 1
  767.                 IF CGSortLibArr(InSort_Local_i) < CGSortLibArr(InSort_Local_j) THEN
  768.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  769.                     DO UNTIL InSort_Local_j < start
  770.                         IF (InSort_Local_ArrayTemp < CGSortLibArr(InSort_Local_j)) THEN
  771.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  772.                             InSort_Local_j = InSort_Local_j - 1
  773.                         ELSE
  774.                             EXIT DO
  775.                         END IF
  776.                     LOOP
  777.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  778.                 END IF
  779.             NEXT
  780.         CASE ELSE
  781.             FOR InSort_Local_i = start + 1 TO finish
  782.                 InSort_Local_j = InSort_Local_i - 1
  783.                 IF CGSortLibArr(InSort_Local_i) > CGSortLibArr(InSort_Local_j) THEN
  784.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  785.                     DO UNTIL InSort_Local_j < start
  786.                         IF (InSort_Local_ArrayTemp > CGSortLibArr(InSort_Local_j)) THEN
  787.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  788.                             InSort_Local_j = InSort_Local_j - 1
  789.                         ELSE
  790.                             EXIT DO
  791.                         END IF
  792.                     LOOP
  793.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  794.                 END IF
  795.             NEXT
  796.     END SELECT
  797.  
  798.  
  799. 'SUB InsertionSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  800. '    DIM InSort_Local_ArrayTemp AS DOUBLE
  801. '    DIM InSort_Local_i AS LONG
  802. '    DIM InSort_Local_j AS LONG
  803. '    SELECT CASE order&
  804. '        CASE 1
  805. '            FOR InSort_Local_i = start + 1 TO finish
  806. '                InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  807. '                InSort_Local_j = InSort_Local_i - 1
  808. '                DO UNTIL InSort_Local_j < start
  809. '                    IF (InSort_Local_ArrayTemp < CGSortLibArr(InSort_Local_j)) THEN
  810. '                        CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  811. '                        InSort_Local_j = InSort_Local_j - 1
  812. '                    ELSE
  813. '                        EXIT DO
  814. '                    END IF
  815. '                LOOP
  816. '                CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  817. '            NEXT
  818. '        CASE ELSE
  819. '            FOR InSort_Local_i = start + 1 TO finish
  820. '                InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  821. '                InSort_Local_j = InSort_Local_i - 1
  822. '                DO UNTIL InSort_Local_j < start
  823. '                    IF (InSort_Local_ArrayTemp > CGSortLibArr(InSort_Local_j)) THEN
  824. '                        CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  825. '                        InSort_Local_j = InSort_Local_j - 1
  826. '                    ELSE
  827. '                        EXIT DO
  828. '                    END IF
  829. '                LOOP
  830. '                CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  831. '            NEXT
  832. '    END SELECT
  833. 'END SUB
  834.  
  835. '**********************************
  836. '* Asymmetric performance and stack overflows make this algorithm a dog. BinaaryInsertionSort is almost twice
  837. '* twice as fast and does not cause recursion problems. Time for descending sort is twice that of ascending.
  838. '* mostly a conversation piece.
  839. '* [s+][i+][n^2  ]
  840. SUB InsertionSortRecursive (CgSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  841.     IF finish - start > 8191 THEN
  842.         '* this will help prevent stack overflows
  843.         InsertionSort CgSortLibArr(), start, finish, order&
  844.     ELSE
  845.         IF (finish > start) THEN
  846.             InsertionSortRecursive CgSortLibArr(), start, finish - 1, order&
  847.             DIM last AS DOUBLE
  848.             DIM j AS LONG
  849.             SELECT CASE order&
  850.                 CASE 1
  851.                     last = CgSortLibArr(finish)
  852.                     j = finish - 1
  853.                     DO
  854.                         IF j < start THEN
  855.                             EXIT DO
  856.                         ELSE
  857.                             IF CgSortLibArr(j) > last THEN
  858.                                 CgSortLibArr(j + 1) = CgSortLibArr(j)
  859.                                 j = j - 1
  860.                             ELSE
  861.                                 EXIT DO
  862.                             END IF
  863.                         END IF
  864.                     LOOP
  865.                     CgSortLibArr(j + 1) = last
  866.                 CASE ELSE
  867.                     last = CgSortLibArr(finish)
  868.                     j = finish - 1
  869.                     DO
  870.                         IF j < start THEN
  871.                             EXIT DO
  872.                         ELSE
  873.                             IF CgSortLibArr(j) < last THEN
  874.                                 CgSortLibArr(j + 1) = CgSortLibArr(j)
  875.                                 j = j - 1
  876.                             ELSE
  877.                                 EXIT DO
  878.                             END IF
  879.                         END IF
  880.                     LOOP
  881.                     CgSortLibArr(j + 1) = last
  882.             END SELECT
  883.         END IF
  884.     END IF
  885.  
  886. '******************************
  887. '* ShellSort compares elements a gap distance apart, scans the array for out-of-order elements until none are
  888. '* found and then continues reducing this gap distance until it reaches 0. It is not a stable sort, meaning elements
  889. '* of equal value may appear in a position not the same order as it appears in the original array. It is reasonably easy to
  890. '* code, adaptable for any data type and runs in reasonable time, thought to be around O(n^(5/4)). There are Numerous gap
  891. '* reduction methods. The most "popular" being the (Gap/2) method. I have made several modifications to aid running time,
  892. '* namely tracking the first and last position a swap occurred and using this to only scan to that point or less on successive
  893. '* passes. The last pass of shellsort is the same as InsertionSort.
  894. '******************************
  895. SUB ShellSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  896.     SELECT CASE finish& - start&
  897.         CASE 1
  898.             IF CGSortLibArr(start&) > CGSortLibArr(finish&) THEN
  899.                 IF order& = 1 THEN
  900.                     SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  901.                 END IF
  902.             END IF
  903.         CASE IS > 1
  904.             IF order& = 1 THEN
  905.                 ShellSortGap& = (finish& - start&) \ 2
  906.                 DO
  907.                     IF ShellSortGap& > 1 THEN
  908.                         LoopCount& = 0
  909.                         xstart& = start&
  910.                         xfinish& = finish& - ShellSortGap&
  911.                         MaxPasses& = (finish& - start&) \ ShellSortGap&
  912.                         DO
  913.                             xfirst& = xfinish&
  914.                             FOR ShellSortS& = xstart& TO xfinish&
  915.                                 IF CGSortLibArr(ShellSortS&) > CGSortLibArr(ShellSortS& + ShellSortGap&) THEN
  916.                                     SWAP CGSortLibArr(ShellSortS&), CGSortLibArr(ShellSortS& + ShellSortGap&)
  917.                                     Last& = ShellSortS&
  918.                                     IF ShellSortS& < xfirst& THEN
  919.                                         xfirst& = ShellSortS&
  920.                                     END IF
  921.                                 END IF
  922.                             NEXT
  923.                             xfinish& = Last&
  924.                             xstart& = xfirst&
  925.                             LoopCount& = LoopCount& + 1
  926.                         LOOP WHILE LoopCount& < MaxPasses& AND (xfinish& - xstart&) >= ShellSortGap&
  927.                         ShellSortGap& = ShellSortGap& \ 2
  928.                     ELSE
  929.                         InsertionSort CGSortLibArr(), start&, finish&, order&
  930.                         EXIT DO
  931.                     END IF
  932.                 LOOP
  933.             ELSE
  934.                 ShellSortGap& = (finish& - start&) \ 2
  935.                 DO
  936.                     IF ShellSortGap& > 1 THEN
  937.                         LoopCount& = 0
  938.                         xstart& = start&
  939.                         xfinish& = finish& - ShellSortGap&
  940.                         MaxPasses& = (finish& - start&) \ ShellSortGap&
  941.                         DO
  942.                             xfirst& = xfinish&
  943.                             FOR ShellSortS& = xstart& TO xfinish&
  944.                                 IF CGSortLibArr(ShellSortS&) < CGSortLibArr(ShellSortS& + ShellSortGap&) THEN
  945.                                     SWAP CGSortLibArr(ShellSortS&), CGSortLibArr(ShellSortS& + ShellSortGap&)
  946.                                     Last& = ShellSortS&
  947.                                     IF ShellSortS& < xfirst& THEN
  948.                                         xfirst& = ShellSortS&
  949.                                     END IF
  950.                                 END IF
  951.                             NEXT
  952.                             xfinish& = Last&
  953.                             xstart& = xfirst&
  954.                             LoopCount& = LoopCount& + 1
  955.                         LOOP WHILE LoopCount& < MaxPasses& AND (xfinish& - xstart&) >= ShellSortGap&
  956.                         ShellSortGap& = ShellSortGap& \ 2
  957.                     ELSE
  958.                         InsertionSort CGSortLibArr(), start&, finish&, order&
  959.                         EXIT DO
  960.                     END IF
  961.                 LOOP
  962.  
  963.             END IF
  964.     END SELECT
  965.  
  966. '*******************************************
  967. '* this has been modified to become a bidirectional shellsort, which is far faster than the bubblesort version, which is a special case where
  968. '* gap& is 1, and runs in polynomial o(n^1(5/4)) time when like its unidirectional predecessor. Not Stable. No practical use in real life I've
  969. '* seen, but entertaining if visualized.
  970. '*******************************************
  971. SUB ShellSortBidirectional (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  972.     SELECT CASE order&
  973.         CASE 1
  974.             gap& = (finish& - start& + 1) \ 2
  975.             DO UNTIL gap& < 1
  976.                 up% = -1: down% = -1: passes& = 0: maxpasses& = (finish& - start& + 1) \ gap& - 1
  977.                 startup& = start&: endup& = finish& - gap&: FirstUp& = finish& - gap&: LastUp& = start&
  978.                 startdn& = finish&: enddown& = start& + gap&: FirstDown& = start& + gap&: LastDown& = finish&
  979.                 passes& = 0
  980.                 DO
  981.                     IF up% THEN
  982.                         up% = 0
  983.                         FOR i& = startup& TO endup&
  984.                             IF CGSortLibArr(i&) > CGSortLibArr(i& + gap&) THEN
  985.                                 SWAP CGSortLibArr(i&), CGSortLibArr(i& + gap&)
  986.                                 IF i& < FirstUp& THEN
  987.                                     FirstUp& = i&
  988.                                 END IF
  989.                                 LastUp& = i&
  990.                                 up% = -1
  991.                             END IF
  992.                         NEXT
  993.                         startup& = FirstUp&
  994.                         endup& = LastUp&
  995.                         SWAP FirstUp&, LastUp&
  996.                     END IF
  997.                     '*******************************
  998.                     IF down% THEN
  999.                         down% = 0
  1000.                         FOR i& = startdn& TO enddown& STEP -1
  1001.                             IF CGSortLibArr(i&) < CGSortLibArr(i& - gap&) THEN
  1002.                                 SWAP CGSortLibArr(i&), CGSortLibArr(i& - gap&)
  1003.                                 IF i& > FirstDown& THEN
  1004.                                     FirstDown& = i&
  1005.                                 END IF
  1006.                                 LastDown& = i&
  1007.                                 down% = -1
  1008.                             END IF
  1009.                         NEXT
  1010.                         startdn& = FirstDown&
  1011.                         enddown& = LastDown&
  1012.                         SWAP FirstDown&, LastDown&
  1013.                     END IF
  1014.                     IF passes& < maxpasses& THEN
  1015.                         IF up% OR down% THEN
  1016.                             IF passes& < (enddown& - startdown&) \ gap& - 1 OR passes& < (endup& - startup&) \ gap& - 1 THEN
  1017.                                 passes& = passes& + 1
  1018.                             ELSE
  1019.                                 EXIT DO
  1020.                             END IF
  1021.                         ELSE
  1022.                             EXIT DO
  1023.                         END IF
  1024.                     ELSE
  1025.                         EXIT DO
  1026.                     END IF
  1027.                 LOOP
  1028.                 gap& = gap& \ 2
  1029.             LOOP
  1030.         CASE ELSE
  1031.             gap& = (finish& - start& + 1) \ 2
  1032.             DO UNTIL gap& < 1
  1033.                 up% = -1: down% = -1: passes& = 0: maxpasses& = (finish& - start& + 1) \ gap& - 1
  1034.                 startup& = start&: endup& = finish& - gap&: FirstUp& = finish& - gap&: LastUp& = start&
  1035.                 startdn& = finish&: enddown& = start& + gap&: FirstDown& = start& + gap&: LastDown& = finish&
  1036.                 DO
  1037.                     IF up% THEN
  1038.                         up% = 0
  1039.                         FOR i& = startup& TO endup&
  1040.                             IF CGSortLibArr(i&) < CGSortLibArr(i& + gap&) THEN
  1041.                                 SWAP CGSortLibArr(i&), CGSortLibArr(i& + gap&)
  1042.                                 IF i& < FirstUp& THEN
  1043.                                     FirstUp& = i&
  1044.                                 END IF
  1045.                                 LastUp& = i&
  1046.                                 up% = -1
  1047.                             END IF
  1048.                         NEXT
  1049.                         startup& = FirstUp&
  1050.                         endup& = LastUp&
  1051.                         SWAP FirstUp&, LastUp&
  1052.                     END IF
  1053.                     '*******************************
  1054.                     IF down% THEN
  1055.                         down% = 0
  1056.                         FOR i& = startdn& TO enddown& STEP -1
  1057.                             IF CGSortLibArr(i&) > CGSortLibArr(i& - gap&) THEN
  1058.                                 SWAP CGSortLibArr(i&), CGSortLibArr(i& - gap&)
  1059.                                 IF i& > FirstDown& THEN
  1060.                                     FirstDown& = i&
  1061.                                 END IF
  1062.                                 LastDown& = i&
  1063.                                 down% = -1
  1064.                             END IF
  1065.                         NEXT
  1066.                         startdn& = FirstDown&
  1067.                         enddown& = LastDown&
  1068.                         SWAP FirstDown&, LastDown&
  1069.                     END IF
  1070.                     IF passes& < maxpasses& THEN
  1071.                         IF up% OR down% THEN
  1072.                             IF passes& < (enddown& - startdown&) \ gap& - 1 OR passes& < (endup& - startup&) \ gap& - 1 THEN
  1073.                                 passes& = passes& + 1
  1074.                             ELSE
  1075.                                 EXIT DO
  1076.                             END IF
  1077.                         ELSE
  1078.                             EXIT DO
  1079.                         END IF
  1080.                     ELSE
  1081.                         EXIT DO
  1082.                     END IF
  1083.                 LOOP
  1084.                 gap& = gap& \ 2
  1085.             LOOP
  1086.     END SELECT
  1087.  
  1088. '*******************************************
  1089. '* TESTED -- WORKS
  1090. '* QuickSortRecursive is reputedly the "fastest sort." This is not true in all cases. One way to defeat QuickSort and send it into
  1091. '* polynomial time O(n^2) is to present it with an already-sorted array. There are safeguards to this. One to shuffle the array
  1092. '* before executing quicksort or in the case of IntroSort, revert to MergeSort once a certain level of recursion or InsertionSort
  1093. '* once a small (usually 15-31) sublist size is reached.
  1094. '* Often mistakenly referred to as the fastest sort, it does around NLogN comparisons, which is the lower bound for
  1095. '* comparison sorts. Fast? Generally, but not always. This is the recursive version, fine for most modern processors that support
  1096. '* the use of hardware stacks. This is a divide-and-conquer algorithm as is MergeSort.
  1097.  
  1098. SUB QuickSortRecursive (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1099.     SELECT CASE finish& - start&
  1100.         CASE 1
  1101.             '* This is CRITICAL
  1102.             SELECT CASE order&
  1103.                 CASE 1
  1104.                     IF CGSortLibArr(start&) > CGSortLibArr(finish&) THEN
  1105.                         SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  1106.                     END IF
  1107.                 CASE ELSE
  1108.                     IF CGSortLibArr(start&) < CGSortLibArr(finish&) THEN
  1109.                         SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  1110.                     END IF
  1111.             END SELECT
  1112.         CASE IS > 1
  1113.             QuickSortIJ CGSortLibArr(), start&, finish&, i&, j&, order&
  1114.             IF (i& - start&) < (finish& - j&) THEN
  1115.                 QuickSortRecursive CGSortLibArr(), start&, j&, order&
  1116.                 QuickSortRecursive CGSortLibArr(), i&, finish&, order&
  1117.             ELSE
  1118.                 QuickSortRecursive CGSortLibArr(), i&, finish&, order&
  1119.                 QuickSortRecursive CGSortLibArr(), start&, j&, order&
  1120.             END IF
  1121.     END SELECT
  1122.  
  1123. '*********************************
  1124. '* TESTED -- WORKS
  1125. '* This is the iterative version of QuickSort, using a software stack, useful for OLD processors lacking hardware registers to support
  1126. '* recursion. Operationally, it is very much the same as the recursive version except the "stack" is software-based.
  1127. '* Modified 2018 March 13 for stack bounds correction. Also modified to indicate local variables, and make changing variables as
  1128. '* necessary to accommodate range and type more straightforward.
  1129.  
  1130. SUB QuickSortIterative (CGSortLibArr() AS DOUBLE, QSIStart AS LONG, QSIFinish AS LONG, order&)
  1131.     DIM QSI_Local_Compare AS DOUBLE '* MUST be same type as element of CGSortLibArr()
  1132.     '* These MUST be the appropriate type for the range being sorted
  1133.     DIM QSI_Local_I AS LONG
  1134.     DIM QSI_local_J AS LONG
  1135.     DIM QSI_Local_Hi AS LONG
  1136.     DIM QSI_Local_Low AS LONG
  1137.     DIM QSI_Local_Mid AS LONG
  1138.     '****************************************************************
  1139.  
  1140.     '* Integer suffices for QSI_Local_MinStackPtr unless you're sorting more than 2^32767 elements.
  1141.     DIM QSI_Local_MinStackPtr AS INTEGER: QSI_Local_MinStackPtr = 0
  1142.     DIM QSI_Local_QSI_local_CurrentStackPtr AS INTEGER: QSI_Local_QSI_local_CurrentStackPtr = 0
  1143.     DIM QSI_Local_FinishMinusStart AS LONG: QSI_Local_FinishMinusStart = QSIFinish - QSIStart
  1144.     DIM QSI_local_Remainder AS INTEGER
  1145.  
  1146.     '* yes, the equation log(QSIfinish-QSIstart)/log(2)+1 works too
  1147.     DO
  1148.         QSI_local_Remainder = QSI_Local_FinishMinusStart - (2 * INT(QSI_Local_FinishMinusStart / 2))
  1149.         QSI_Local_FinishMinusStart = (QSI_Local_FinishMinusStart - QSI_local_Remainder) / 2
  1150.         QSI_Local_MinStackPtr = QSI_Local_MinStackPtr + 1
  1151.     LOOP UNTIL QSI_Local_FinishMinusStart < 1
  1152.  
  1153.     '* MUST be appropriate type to handle the range (QSIfinish-QSIstart) being sorted
  1154.     DIM QSI_LStack(0 TO QSI_Local_MinStackPtr, 0 TO 1) AS LONG
  1155.  
  1156.     QSI_local_CurrentStackPtr = 0
  1157.     QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSIStart
  1158.     QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSIFinish
  1159.     DO
  1160.         QSI_Local_Low = QSI_LStack(QSI_local_CurrentStackPtr, 0)
  1161.         QSI_Local_Hi = QSI_LStack(QSI_local_CurrentStackPtr, 1)
  1162.         DO
  1163.             QSI_Local_I = QSI_Local_Low
  1164.             QSI_local_J = QSI_Local_Hi
  1165.             QSI_Local_Mid = QSI_Local_Low + (QSI_Local_Hi - QSI_Local_Low) \ 2
  1166.             QSI_Local_Compare = CGSortLibArr(QSI_Local_Mid)
  1167.             SELECT CASE order&
  1168.                 CASE 1
  1169.                     DO
  1170.                         DO WHILE CGSortLibArr(QSI_Local_I) < QSI_Local_Compare
  1171.                             QSI_Local_I = QSI_Local_I + 1
  1172.                         LOOP
  1173.                         DO WHILE CGSortLibArr(QSI_local_J) > QSI_Local_Compare
  1174.                             QSI_local_J = QSI_local_J - 1
  1175.                         LOOP
  1176.                         IF QSI_Local_I <= QSI_local_J THEN
  1177.                             SWAP CGSortLibArr(QSI_Local_I), CGSortLibArr(QSI_local_J)
  1178.                             QSI_Local_I = QSI_Local_I + 1
  1179.                             QSI_local_J = QSI_local_J - 1
  1180.                         END IF
  1181.                     LOOP UNTIL QSI_Local_I > QSI_local_J
  1182.                 CASE ELSE
  1183.                     DO
  1184.                         DO WHILE CGSortLibArr(QSI_Local_I) > QSI_Local_Compare
  1185.                             QSI_Local_I = QSI_Local_I + 1
  1186.                         LOOP
  1187.                         DO WHILE CGSortLibArr(QSI_local_J) < QSI_Local_Compare
  1188.                             QSI_local_J = QSI_local_J - 1
  1189.                         LOOP
  1190.                         IF QSI_Local_I <= QSI_local_J THEN
  1191.                             SWAP CGSortLibArr(QSI_Local_I), CGSortLibArr(QSI_local_J)
  1192.                             QSI_Local_I = QSI_Local_I + 1
  1193.                             QSI_local_J = QSI_local_J - 1
  1194.                         END IF
  1195.                     LOOP UNTIL QSI_Local_I > QSI_local_J
  1196.             END SELECT
  1197.             IF QSI_local_J - QSI_Local_Low < QSI_Local_Hi - QSI_Local_I THEN
  1198.                 IF QSI_Local_I < QSI_Local_Hi THEN
  1199.                     QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSI_Local_I
  1200.                     QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSI_Local_Hi
  1201.                     QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr + 1
  1202.                 END IF
  1203.                 QSI_Local_Hi = QSI_local_J
  1204.             ELSE
  1205.                 IF QSI_Local_Low < QSI_local_J THEN
  1206.                     QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSI_Local_Low
  1207.                     QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSI_local_J
  1208.                     QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr + 1
  1209.                 END IF
  1210.                 QSI_Local_Low = QSI_Local_I
  1211.             END IF
  1212.         LOOP WHILE QSI_Local_Low < QSI_Local_Hi
  1213.         QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr - 1
  1214.     LOOP UNTIL QSI_local_CurrentStackPtr < 0
  1215.  
  1216. '************************
  1217. '* TESTED -- WORKS
  1218. '* Yaroslavsky Dual-pivot QuickSort is useful for arrays having many repeating elements. Will still fail on some inputs but better than standard QuickSort
  1219. '* for the same lack of entropy in an array. This is a version of the standard Java QuickSort. There is a 3-pivot version, also adaptive.
  1220. '************************
  1221. SUB QuickSortDualPivot (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1222.     DIM CompareP AS DOUBLE
  1223.     DIM CompareQ AS DOUBLE
  1224.     IF start& < finish& THEN
  1225.         CompareP = CGSortLibArr(start&)
  1226.         CompareQ = CGSortLibArr(finish&)
  1227.         IF order& = 1 THEN
  1228.             IF CompareP > CompareQ THEN
  1229.                 SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  1230.                 SWAP CompareP, CompareQ
  1231.             END IF
  1232.         ELSE
  1233.             IF CompareP < CompareQ THEN
  1234.                 SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  1235.                 SWAP CompareP, CompareQ
  1236.             END IF
  1237.         END IF
  1238.         l& = start& + 1
  1239.         k& = l&
  1240.         g& = finish& - 1
  1241.         SELECT CASE order&
  1242.             CASE 1
  1243.                 WHILE k& <= g&
  1244.                     IF CGSortLibArr(k&) < CompareP THEN
  1245.                         SWAP CGSortLibArr(k&), CGSortLibArr(l&)
  1246.                         l& = l& + 1
  1247.                     ELSE
  1248.                         IF CGSortLibArr(k&) >= CompareQ THEN
  1249.                             WHILE CGSortLibArr(g&) >= CompareQ AND k& < g&
  1250.                                 g& = g& - 1
  1251.                             WEND
  1252.                             SWAP CGSortLibArr(k&), CGSortLibArr(g&)
  1253.                             g& = g& - 1
  1254.                             IF CGSortLibArr(k&) <= CompareP THEN
  1255.                                 SWAP CGSortLibArr(k&), CGSortLibArr(l&)
  1256.                                 l& = l& + 1
  1257.                             END IF
  1258.                         END IF
  1259.                     END IF
  1260.                     k& = k& + 1
  1261.                 WEND
  1262.             CASE ELSE
  1263.                 WHILE k& <= g&
  1264.                     IF CGSortLibArr(k&) > CompareP THEN
  1265.                         SWAP CGSortLibArr(k&), CGSortLibArr(l&)
  1266.                         l& = l& + 1
  1267.                     ELSE
  1268.                         IF CGSortLibArr(k&) <= CompareQ THEN
  1269.                             WHILE CGSortLibArr(g&) <= CompareQ AND k& < g&
  1270.                                 g& = g& - 1
  1271.                             WEND
  1272.                             SWAP CGSortLibArr(k&), CGSortLibArr(g&)
  1273.                             g& = g& - 1
  1274.                             IF CGSortLibArr(k&) >= CompareP THEN
  1275.                                 SWAP CGSortLibArr(k&), CGSortLibArr(l&)
  1276.                                 l& = l& + 1
  1277.                             END IF
  1278.                         END IF
  1279.                     END IF
  1280.                     k& = k& + 1
  1281.                 WEND
  1282.         END SELECT
  1283.         l& = l& - 1
  1284.         g& = g& + 1
  1285.         SWAP CGSortLibArr(start&), CGSortLibArr(l&)
  1286.         SWAP CGSortLibArr(finish&), CGSortLibArr(g&)
  1287.         QuickSortDualPivot CGSortLibArr(), start&, l& - 1, order&
  1288.         QuickSortDualPivot CGSortLibArr(), l& + 1, g& - 1, order&
  1289.         QuickSortDualPivot CGSortLibArr(), g& + 1, finish&, order&
  1290.     END IF
  1291.  
  1292. '***********************
  1293. '* MergeSort is an O(NlogN) complexity divide and conquer stable sorting algorithm. The primary source of lag is the array copying.
  1294. '* The number of recurive calls is the same as the number of elements. If stability and predictable, undefeatable running time are
  1295. '* your sorting goals, this is an excellent choice. The memory overhead is approximately N/2 plus a few variables. With
  1296. '* EfficientMerge, memory overhead is halved, thus saving costly array copying. On my machine, this method is faster than the
  1297. '* standard MergeSort using the MergeRoutine() method.
  1298. '***********************
  1299. SUB MergeSortEmerge (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1300.     SELECT CASE finish& - start&
  1301.         CASE IS > 31
  1302.             middle& = start& + (finish& - start&) \ 2
  1303.             MergeSortEmerge CGSortLibArr(), start&, middle&, order&
  1304.             MergeSortEmerge CGSortLibArr(), middle& + 1, finish&, order&
  1305.             EfficientMerge CGSortLibArr(), start&, finish&, order&
  1306.         CASE IS > 0
  1307.             InsertionSort CGSortLibArr(), start&, finish&, order&
  1308.     END SELECT
  1309.  
  1310. '*******************************
  1311. '* This is the standard MergeSort using the MergeRoutine() method. This is an example of head recursion, where recursive calls precede
  1312. '* other procedures.
  1313. '*******************************
  1314. SUB MergeSortRoutine (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1315.     SELECT CASE finish& - start&
  1316.         CASE IS > 31
  1317.             middle& = start& + (finish& - start&) \ 2
  1318.             MergeSortRoutine CGSortLibArr(), start&, middle&, order&
  1319.             MergeSortRoutine CGSortLibArr(), middle& + 1, finish&, order&
  1320.             MergeRoutine CGSortLibArr(), start&, finish&, order&
  1321.         CASE IS > 0
  1322.             InsertionSort CGSortLibArr(), start&, finish&, order&
  1323.     END SELECT
  1324.  
  1325. '**********************************************
  1326. '* BubbleSort is a terrible performer on random arrays. It is good for nearly sorted arrays.
  1327. '* Average-case quadratic performance that is not adaptive makes this sort unsuitable for even small N
  1328. '* (here, finish - start).
  1329. '**********************************************
  1330. SUB BubbleSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1331.     SELECT CASE order&
  1332.         CASE 1
  1333.             DO
  1334.                 changed& = 0
  1335.                 FOR I& = start& TO finish& - 1
  1336.                     IF CGSortLibArr(I&) > CGSortLibArr(I& + 1) THEN
  1337.                         SWAP CGSortLibArr(I&), CGSortLibArr(I& + 1)
  1338.                         changed& = -1
  1339.                     END IF
  1340.                 NEXT
  1341.             LOOP WHILE changed&
  1342.         CASE ELSE
  1343.             DO
  1344.                 changed& = 0
  1345.                 FOR I& = start& TO finish& - 1
  1346.                     IF CGSortLibArr(I&) < CGSortLibArr(I& + 1) THEN
  1347.                         SWAP CGSortLibArr(I&), CGSortLibArr(I& + 1)
  1348.                         changed& = -1
  1349.                     END IF
  1350.                 NEXT
  1351.             LOOP WHILE changed&
  1352.     END SELECT
  1353.  
  1354. '**************************
  1355. '* another variation of bubblesort, CocktailSort also runs in o(n^2) and essentially scans up and down the array swapping out-of-order
  1356. '* elements until none are found. Stable, mostly a conversation piece.
  1357. '**************************
  1358. SUB CocktailSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1359.     SELECT CASE order&
  1360.         CASE 1
  1361.             runs& = 0
  1362.             DO
  1363.                 p& = finish& - runs&
  1364.                 done& = 1 '* assume it's sorted
  1365.                 FOR i& = start& + runs& TO finish& - runs& - 1
  1366.                     IF CGSortLibArr(i&) > CGSortLibArr(i& + 1) THEN
  1367.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + 1)
  1368.                         done& = 0
  1369.                     END IF
  1370.                     IF CGSortLibArr(p&) < CGSortLibArr(p& - 1) THEN
  1371.                         SWAP CGSortLibArr(p&), CGSortLibArr(p& - 1)
  1372.                         done& = 0
  1373.                     END IF
  1374.                     p& = p& - 1
  1375.                 NEXT
  1376.                 runs& = runs& + 1
  1377.             LOOP UNTIL done&
  1378.         CASE ELSE
  1379.             runs& = 0
  1380.             DO
  1381.                 p& = finish& - runs&
  1382.                 done& = 1 '* assume it's sorted
  1383.                 FOR i& = start& + runs& TO finish& - runs& - 1
  1384.                     IF CGSortLibArr(i&) < CGSortLibArr(i& + 1) THEN
  1385.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + 1)
  1386.                         done& = 0
  1387.                     END IF
  1388.                     IF CGSortLibArr(p&) > CGSortLibArr(p& - 1) THEN
  1389.                         SWAP CGSortLibArr(p&), CGSortLibArr(p& - 1)
  1390.                         done& = 0
  1391.                     END IF
  1392.                     p& = p& - 1
  1393.                 NEXT
  1394.                 runs& = runs& + 1
  1395.             LOOP UNTIL done&
  1396.     END SELECT
  1397. '******************************
  1398. '* this one is horrible with stack. No speed improvement and generally quite limited
  1399. '* because of its extremenly heavy use of stack.
  1400. '******************************
  1401. SUB BubbleSortRecursive (CGSortLibArr() AS DOUBLE, startIndex AS LONG, endIndex AS LONG, order&)
  1402.     IF startIndex < endIndex THEN
  1403.         IF order& = 1 THEN
  1404.             FOR c& = startIndex TO endIndex - 1
  1405.                 IF CGSortLibArr(c&) > CGSortLibArr(c& + 1) THEN
  1406.                     SWAP CGSortLibArr(c&), CGSortLibArr(c& + 1)
  1407.                 END IF
  1408.             NEXT
  1409.             BubbleSortRecursive CGSortLibArr(), startIndex, endIndex - 1, order&
  1410.         ELSE
  1411.             FOR c& = startIndex TO endIndex - 1
  1412.                 IF CGSortLibArr(c&) < CGSortLibArr(c& + 1) THEN
  1413.                     SWAP CGSortLibArr(c&), CGSortLibArr(c& + 1)
  1414.                 END IF
  1415.             NEXT
  1416.             BubbleSortRecursive CGSortLibArr(), startIndex, endIndex - 1, order&
  1417.         END IF
  1418.     END IF
  1419.  
  1420.  
  1421. 'SUB CountingSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1422. '    DIM CountingSortMinMax AS MinMaxRec
  1423. '    GetMinMaxArray CGSortLibArr(), start&, finish&, CountingSortMinMax
  1424. '    IF CGSortLibArr(CountingSortMinMax.min) < CGSortLibArr(CountingSortMinMax.max) THEN
  1425. '        REDIM csCounts(0 TO (finish& - start&)) AS LONG
  1426. '        FOR s& = start& TO finish&
  1427. '            '*       NthPlace& (a() AS DOUBLE, NPMMrec AS MinMaxRec, start AS LONG, finish AS LONG, order&, npindex AS LONG)
  1428. '            index& = NthPlace&(CGSortLibArr(), CountingSortMinMax, 0, finish& - start&, order&, s&)
  1429. '            PRINT CGSortLibArr(s&); finish& - start&; index& > (finish& - start&); index&; CGSortLibArr(s&) - CGSortLibArr(CountingSortMinMax.min)
  1430. '            csCounts(index&) = csCounts(index&) + 1
  1431. '            '_DELAY .25
  1432. '        NEXT
  1433. '        index& = start&
  1434. '        'FOR s# = cs_Min TO cs_max
  1435. '        '    WHILE csCounts(s#)
  1436. '        '        CGSortLibArr(index&) = s#
  1437. '        '        index& = index& + 1
  1438. '        '        csCounts(s#) = csCounts(s#) - 1
  1439. '        '    WEND
  1440. '        'NEXT
  1441. '        ERASE csCounts
  1442. '        InsertionSort CGSortLibArr(), start&, finish&, order&
  1443. '    END IF
  1444. 'END SUB
  1445.  
  1446. '**********************
  1447. '* helper function for InsertionSortBinary exactly the same as a binary search which runs in O(LogN) time.
  1448. 'FUNCTION BinaryB& (CGSortLibArr() AS DOUBLE, start&, Nio&)
  1449. '    Bsrcha& = start&
  1450. '    BsrchB& = start& + Nio&
  1451. '    DO
  1452. '        BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  1453. '        IF CGSortLibArr(BsrchC&) < CGSortLibArr(Nio&) THEN
  1454. '            Bsrcha& = BsrchC& + 1
  1455. '        ELSE
  1456. '            BsrchB& = BsrchC&
  1457. '        END IF
  1458. '    LOOP WHILE Bsrcha& < BsrchB&
  1459. '    BinaryB& = BsrchB&
  1460. 'END FUNCTION
  1461.  
  1462. '*****************************
  1463. '* InsertionSortBinary uses Binary Search to find the correct position of an array element in the portion already sorted.
  1464. '* It's approximately 25 percent faster than standard InsertionSort in SOME cases.
  1465. '*****************************
  1466. 'SUB InsertionSortBinary (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1467. '    SELECT CASE order&
  1468. '        CASE 1
  1469. '            Nsorted& = 0
  1470. '            DO
  1471. '                f& = BinaryB&(CGSortLibArr(), start&, Nsorted&)
  1472. '                p& = start& + Nsorted&
  1473. '                WHILE p& > f&
  1474. '                    x& = p& - 1
  1475. '                    SWAP CGSortLibArr(p&), CGSortLibArr(x&)
  1476. '                    p& = x&
  1477. '                WEND
  1478. '                Nsorted& = Nsorted& + 1
  1479. '            LOOP UNTIL Nsorted& > finish& - start&
  1480. '        CASE ELSE
  1481. '            Nsorted& = 0
  1482. '            DO
  1483. '                f& = BinaryB&(CGSortLibArr(), start&, Nsorted&)
  1484. '                p& = start& + Nsorted&
  1485. '                WHILE p& > f&
  1486. '                    x& = p& - 1
  1487. '                    SWAP CGSortLibArr(p&), CGSortLibArr(x&)
  1488. '                    p& = x&
  1489. '                WEND
  1490. '                Nsorted& = Nsorted& + 1
  1491. '            LOOP UNTIL Nsorted& > finish& - start&
  1492. '            IF CGSortLibArr(start&) <> CGSortLibArr(finish&) THEN
  1493. '                StableInvert CGSortLibArr(), start&, finish&, 1
  1494. '            END IF
  1495. '    END SELECT
  1496. 'END SUB
  1497.  
  1498. 'SUB InsertionSortBinary (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1499. '    DIM InSortBinary_NSorted AS LONG
  1500. '    DIM InSortBinary_F AS LONG
  1501. '    DIM InSortBinary_P AS LONG
  1502. '    DIM InSortBinary_X AS LONG
  1503. '    SELECT CASE order&
  1504. '        CASE 1
  1505. '            InSortBinary_NSorted = 0
  1506. '            DO
  1507. '                InSortBinary_F = BinaryB&(CGSortLibArr(), start&, InSortBinary_NSorted, order&)
  1508. '                InSortBinary_P = start& + InSortBinary_NSorted
  1509. '                WHILE InSortBinary_P > InSortBinary_F
  1510. '                    InSortBinary_X = InSortBinary_P - 1
  1511. '                    SWAP CGSortLibArr(InSortBinary_P), CGSortLibArr(InSortBinary_X)
  1512. '                    InSortBinary_P = InSortBinary_X
  1513. '                WEND
  1514. '                InSortBinary_NSorted = InSortBinary_NSorted + 1
  1515. '            LOOP UNTIL InSortBinary_NSorted > finish& - start&
  1516. '        CASE ELSE
  1517. '            InSortBinary_NSorted = 0
  1518. '            DO
  1519. '                InSortBinary_F = BinaryB&(CGSortLibArr(), start&, InSortBinary_NSorted, order&)
  1520. '                InSortBinary_P = start& + InSortBinary_NSorted
  1521. '                WHILE InSortBinary_P > InSortBinary_F
  1522. '                    InSortBinary_X = InSortBinary_P - 1
  1523. '                    SWAP CGSortLibArr(InSortBinary_P), CGSortLibArr(InSortBinary_X)
  1524. '                    InSortBinary_P = InSortBinary_X
  1525. '                WEND
  1526. '                InSortBinary_NSorted = InSortBinary_NSorted + 1
  1527. '            LOOP UNTIL InSortBinary_NSorted > finish& - start&
  1528. '    END SELECT
  1529. 'END SUB
  1530.  
  1531. 'FUNCTION BinaryB& (CGSortLibArr() AS DOUBLE, start&, Nio&, order&)
  1532. '    IF order& = 1 THEN
  1533. '        Bsrcha& = start&
  1534. '        BsrchB& = start& + Nio&
  1535. '        DO
  1536. '            BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  1537. '            IF CGSortLibArr(BsrchC&) < CGSortLibArr(Nio&) THEN
  1538. '                Bsrcha& = BsrchC& + 1
  1539. '            ELSE
  1540. '                BsrchB& = BsrchC&
  1541. '            END IF
  1542. '        LOOP WHILE Bsrcha& < BsrchB&
  1543. '        BinaryB& = BsrchB&
  1544. '    ELSE
  1545. '        Bsrcha& = start&
  1546. '        BsrchB& = start& + Nio&
  1547. '        DO
  1548. '            BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  1549. '            IF CGSortLibArr(BsrchC&) > CGSortLibArr(Nio&) THEN
  1550. '                Bsrcha& = BsrchC& + 1
  1551. '            ELSE
  1552. '                BsrchB& = BsrchC&
  1553. '            END IF
  1554. '        LOOP WHILE Bsrcha& < BsrchB&
  1555. '        BinaryB& = BsrchB&
  1556. '    END IF
  1557. 'END FUNCTION
  1558.  
  1559. '**************************************
  1560. '* Reworked to present correct results. Approximately (20-30)% faster than the standard version for unordered data.
  1561. '* Recommended uses: sorting mostly ordered data or runs that are 1024 or less (about 3.9ms/GHz for doubl-precision).
  1562. '* Yes, there are faster, but they are not strictly in-place (some require stack (software or hardware), or auxiliary
  1563. '* storage for copies of the array range to be sorted. and can be made to go quadratic, thus being no better or
  1564. '* actually worse than InsertionSortBinary(). The complexity class is still O(n^2), but for the use cases listed prior,
  1565. '* InsertionSortBinary() provides a nice performance profile. This algorithm can be adapted EASILY to other data types.
  1566. '**************************************
  1567. SUB InsertionSortBinary (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  1568.     DIM InSortBinary_NSorted AS LONG
  1569.     DIM InSortBinary_F AS LONG
  1570.     DIM InSortBinary_P AS LONG
  1571.     DIM InSortBinary_X AS LONG
  1572.     InSortBinary_NSorted = 0
  1573.     DO
  1574.         InSortBinary_F = InsertionBinaryB&(CGSortLibArr(), start&, InSortBinary_NSorted, order&)
  1575.         InSortBinary_P = start& + InSortBinary_NSorted
  1576.         WHILE InSortBinary_P > InSortBinary_F
  1577.             InSortBinary_X = InSortBinary_P - 1
  1578.             SWAP CGSortLibArr(InSortBinary_P), CGSortLibArr(InSortBinary_X)
  1579.             InSortBinary_P = InSortBinary_X
  1580.         WEND
  1581.         InSortBinary_NSorted = InSortBinary_NSorted + 1
  1582.     LOOP UNTIL InSortBinary_NSorted > finish& - start&
  1583.  
  1584. FUNCTION InsertionBinaryB& (CGSortLibArr() AS DOUBLE, start&, NumberAlreadyOrdered&, order&)
  1585.     IF NumberAlreadyOrdered& > 0 THEN
  1586.         IF order& = 1 THEN
  1587.             Bsrcha& = start&
  1588.             BsrchB& = start& + NumberAlreadyOrdered&
  1589.             IF CGSortLibArr(start& + NumberAlreadyOrdered&) < CGSortLibArr(start& + NumberAlreadyOrdered& - 1) THEN
  1590.                 DO
  1591.                     BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  1592.                     IF CGSortLibArr(BsrchC&) < CGSortLibArr(NumberAlreadyOrdered&) THEN
  1593.                         Bsrcha& = BsrchC& + 1
  1594.                     ELSE
  1595.                         BsrchB& = BsrchC&
  1596.                     END IF
  1597.                 LOOP WHILE Bsrcha& < BsrchB&
  1598.             END IF
  1599.             InsertionBinaryB& = BsrchB&
  1600.         ELSE
  1601.             Bsrcha& = start&
  1602.             BsrchB& = start& + NumberAlreadyOrdered&
  1603.             IF CGSortLibArr(start& + NumberAlreadyOrdered&) > CGSortLibArr(start& + NumberAlreadyOrdered& - 1) THEN
  1604.                 DO
  1605.                     BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  1606.                     IF CGSortLibArr(BsrchC&) > CGSortLibArr(NumberAlreadyOrdered&) THEN
  1607.                         Bsrcha& = BsrchC& + 1
  1608.                     ELSE
  1609.                         BsrchB& = BsrchC&
  1610.                     END IF
  1611.                 LOOP WHILE Bsrcha& < BsrchB&
  1612.             END IF
  1613.             InsertionBinaryB& = BsrchB&
  1614.         END IF
  1615.     ELSE
  1616.         InsertionBinaryB& = start&
  1617.     END IF
  1618.  
  1619.  
  1620. SUB StableInvert (CGSortLibArr() AS DOUBLE, start&, finish&, dorecurse&)
  1621.     '* first invert then invert the equal elements
  1622.     a& = start&
  1623.     b& = finish&
  1624.     WHILE a& < b&
  1625.         SWAP CGSortLibArr(a&), CGSortLibArr(b&)
  1626.         a& = a& + 1
  1627.         b& = b& - 1
  1628.     WEND
  1629.     IF dorecurse& THEN
  1630.         '* then scan the array for runs of equal elements
  1631.         p& = start&
  1632.         DO
  1633.             IF p& < finish& THEN
  1634.                 y& = p& + 1
  1635.                 DO
  1636.                     IF CGSortLibArr(p&) = CGSortLibArr(y&) THEN
  1637.                         IF y& < finish& THEN
  1638.                             y& = y& + 1
  1639.                         ELSE
  1640.                             StableInvert CGSortLibArr(), p&, y&, 0
  1641.                             EXIT DO
  1642.                         END IF
  1643.                     ELSE
  1644.                         EXIT DO
  1645.                     END IF
  1646.                 LOOP
  1647.                 p& = y&
  1648.             ELSE
  1649.                 EXIT DO
  1650.             END IF
  1651.         LOOP
  1652.     END IF
  1653.  
  1654. '*****************************************
  1655. '* BucketSort (refactored)
  1656. '*****************************************
  1657. '*****************************************
  1658. '* BucketSort (modified 2018 march 14 (pi day 2018 to recurse), speeding things SIGNIFICANTLY.
  1659. '* by making a recursive single non-repeating call to BucketSort(), it speeds this up IMMENSELY. In fact, by 30 times.
  1660. '* From 10s down to 350ms, verified and correct, quite an improvement.
  1661. '* BucketSort() works by making fixed-size containers to hold ranges of elements. Much like Postman's Sort.
  1662. '* refactored to prevent inadvertent use of variables that MAY be present as constants or shared variables in MAIN.
  1663. '*****************************************
  1664. SUB BucketSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&, recurse%)
  1665.     DIM BS_Local_NBuckets AS INTEGER
  1666.     '* DIM BS_Local_ArrayRange AS DOUBLE
  1667.     DIM BS_Local_ArrayMinValue AS DOUBLE
  1668.     DIM BS_Local_N AS LONG
  1669.     DIM BS_Local_S AS LONG
  1670.     DIM BS_Local_Z AS LONG
  1671.     DIM BS_Local_Remainder AS INTEGER
  1672.     DIM BS_Local_Index AS INTEGER
  1673.     DIM BS_Local_Last_Insert_Index AS LONG
  1674.     DIM BS_Local_Current_Insert_Index AS LONG
  1675.     DIM BS_Local_BucketIndex AS INTEGER
  1676.     REDIM BSMMrec AS MinMaxRec
  1677.     BSMMrec.min = start
  1678.     BSMMrec.max = start
  1679.     FOR x& = tstart TO finish
  1680.         IF CGSortLibArr(x&) < CGSortLibArr(BSMMrec.min) THEN BSMMrec.min = x&
  1681.         IF CGSortLibArr(x&) > CGSortLibArr(BSMMrec.max) THEN BSMMrec.max = x&
  1682.     NEXT
  1683.     '* ------------------- GetMinMaxArray CGSortLibArr(), start, finish, BSMMrec
  1684.  
  1685.     IF (CGSortLibArr(BSMMrec.max) - CGSortLibArr(BSMMrec.min)) <> 0 THEN
  1686.         '* BS_Local_ArrayRange = CGSortLibArr(BSMMrec.max) - CGSortLibArr(BSMMrec.min)
  1687.         BS_Local_ArrayMinValue = CGSortLibArr(BSMMrec.min)
  1688.         BS_Local_NBuckets = INT(LOG(finish - start + 1) / LOG(2)) + 1
  1689.         BS_Local_N = (finish - start + 1)
  1690.         BS_Local_Remainder = BS_Local_N MOD BS_Local_NBuckets
  1691.         BS_Local_NBuckets = BS_Local_NBuckets - 1
  1692.         REDIM BS_Buckets_CGSortLibArr(0 TO BS_Local_NBuckets, 0 TO BS_Local_NBuckets * (1 + (BS_Local_N - BS_Local_Remainder) / BS_Local_NBuckets)) AS DOUBLE
  1693.         REDIM BS_Count_CGSortLibArr(0 TO BS_Local_NBuckets) AS LONG
  1694.         FOR BS_Local_S = start TO finish
  1695.             BS_Local_BucketIndex = INT((BS_Local_NBuckets - 1) * ((CGSortLibArr(BS_Local_S) - BS_Local_ArrayMinValue) / (CGSortLibArr(BSMMrec.max) - CGSortLibArr(BSMMrec.min))))
  1696.             'IF BS_Count_CGSortLibArr(BS_Local_BucketIndex) > UBOUND(BS_Buckets_CGSortLibArr, 2) THEN
  1697.             '   REDIM _PRESERVE BS_Buckets_CGSortLibArr(BS_Local_BucketIndex, BS_Count_CGSortLibArr(BS_Local_BucketIndex)) AS DOUBLE
  1698.             'END IF
  1699.             BS_Buckets_CGSortLibArr(BS_Local_BucketIndex, BS_Count_CGSortLibArr(BS_Local_BucketIndex)) = CGSortLibArr(BS_Local_S)
  1700.             BS_Count_CGSortLibArr(BS_Local_BucketIndex) = BS_Count_CGSortLibArr(BS_Local_BucketIndex) + 1
  1701.         NEXT
  1702.         BS_Local_Last_Insert_Index = start
  1703.         BS_Local_Current_Insert_Index = start
  1704.         FOR BS_Local_S = 0 TO BS_Local_NBuckets
  1705.             IF BS_Count_CGSortLibArr(BS_Local_S) > 0 THEN
  1706.                 BS_Local_Last_Insert_Index = BS_Local_Current_Insert_Index
  1707.                 FOR BS_Local_Z = 0 TO BS_Count_CGSortLibArr(BS_Local_S) - 1
  1708.                     CGSortLibArr(BS_Local_Current_Insert_Index) = BS_Buckets_CGSortLibArr(BS_Local_S, BS_Local_Z)
  1709.                     BS_Local_Current_Insert_Index = BS_Local_Current_Insert_Index + 1
  1710.                 NEXT
  1711.                 IF recurse% THEN
  1712.                     '* Without this, 28s+ at (0, 131071)
  1713.                     recurse% = 0
  1714.                     BucketSort CGSortLibArr(), BS_Local_Last_Insert_Index, BS_Local_Current_Insert_Index - 1, order&, recurse%
  1715.                 ELSE
  1716.                     MergeSortEmerge CGSortLibArr(), BS_Local_Last_Insert_Index, BS_Local_Current_Insert_Index - 1, order&
  1717.                 END IF
  1718.             END IF
  1719.         NEXT
  1720.         ERASE BS_Buckets_CGSortLibArr, BS_Count_CGSortLibArr
  1721.     END IF
  1722.  
  1723. '* adapted for use with qb64. This method is roughly 20 percent more efficient than the standard vector scan algorithm for min/max
  1724. '* Roughly 6.19s versus 7.93 for n=134217728 (0 counts as 1, of course)
  1725. '* This may be a nice addition for perhaps _CGArrayMax() in qb64. Of course, I am not so vain as to insist about the CG part.
  1726. '* simply meant as a faster tool for a common array problem to be solved. Also adaptable to string types.
  1727. 'SUB GetMinMaxArray (CGSortLibArr() AS DOUBLE, Start&, Finish&, GetMinMaxArray_minmax AS MinMaxRec)
  1728. '    DIM GetGetMinMaxArray_minmaxArray_i AS LONG
  1729. '    DIM GetMinMaxArray_n AS LONG
  1730. '    DIM GetMinMaxArray_TT AS LONG
  1731. '    DIM GetMinMaxArray_NMod2 AS INTEGER
  1732. '    '* this is a workaround for the irritating malfunction
  1733. '    '* of MOD using larger numbers and small divisors
  1734. '    GetMinMaxArray_n = Finish& - Start&
  1735. '    GetMinMaxArray_TT = GetMinMaxArray_n MOD 10000
  1736. '    GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  1737. '    IF (GetMinMaxArray_NMod2 MOD 2) THEN
  1738. '        GetMinMaxArray_minmax.min = Start&
  1739. '        GetMinMaxArray_minmax.max = Start&
  1740. '        GetGetMinMaxArray_minmaxArray_i = Start& + 1
  1741. '    ELSE
  1742. '        IF CGSortLibArr(Start&) > CGSortLibArr(Finish&) THEN
  1743. '            GetMinMaxArray_minmax.max = Start&
  1744. '            GetMinMaxArray_minmax.min = Finish&
  1745. '        ELSE
  1746. '            GetMinMaxArray_minmax.min = Finish&
  1747. '            GetMinMaxArray_minmax.max = Start&
  1748. '        END IF
  1749. '        GetGetMinMaxArray_minmaxArray_i = Start& + 2
  1750. '    END IF
  1751.  
  1752. '    WHILE GetGetMinMaxArray_minmaxArray_i < Finish&
  1753. '        IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) > CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) THEN
  1754. '            IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) > CGSortLibArr(GetMinMaxArray_minmax.max) THEN
  1755. '                GetMinMaxArray_minmax.max = GetGetMinMaxArray_minmaxArray_i
  1756. '            END IF
  1757. '            IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) < CGSortLibArr(GetMinMaxArray_minmax.min) THEN
  1758. '                GetMinMaxArray_minmax.min = GetGetMinMaxArray_minmaxArray_i + 1
  1759. '            END IF
  1760. '        ELSE
  1761. '            IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) > CGSortLibArr(GetMinMaxArray_minmax.max) THEN
  1762. '                GetMinMaxArray_minmax.max = GetGetMinMaxArray_minmaxArray_i + 1
  1763. '            END IF
  1764. '            IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) < CGSortLibArr(GetMinMaxArray_minmax.min) THEN
  1765. '                GetMinMaxArray_minmax.min = GetGetMinMaxArray_minmaxArray_i
  1766. '            END IF
  1767. '        END IF
  1768. '        GetGetMinMaxArray_minmaxArray_i = GetGetMinMaxArray_minmaxArray_i + 2
  1769. '    WEND
  1770. 'END SUB
  1771.  
  1772. 'SUB GetMinMaxArray (CGSortLibArr() AS DOUBLE, Start&, Finish&, GetMinMaxArray_minmax AS MinMaxRec)
  1773. '    DIM GetGetMinMaxArray_minmaxArray_i AS LONG
  1774. '    SELECT CASE Finish& - Start&
  1775. '        CASE IS < 31
  1776. '            GetMinMaxArray_minmax.min = start
  1777. '            GetMinMaxArray_minmax.max = start
  1778. '            FOR GetGetMinMaxArray_minmaxArray_i = Start& TO Finish&
  1779. '                IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) < CGSortLibArr(GetMinMaxArray_minmax.min) THEN GetMinMaxArray_minmax.min = GetGetMinMaxArray_minmaxArray_i
  1780. '                IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) > CGSortLibArr(GetMinMaxArray_minmax.max) THEN GetMinMaxArray_minmax.max = GetGetMinMaxArray_minmaxArray_i
  1781. '            NEXT
  1782. '        CASE ELSE
  1783. '            'DIM GetGetMinMaxArray_minmaxArray_i AS LONG
  1784. '            DIM GetMinMaxArray_n AS LONG
  1785. '            DIM GetMinMaxArray_TT AS LONG
  1786. '            DIM GetMinMaxArray_NMod2 AS INTEGER
  1787. '            '* this is a workaround for the irritating malfunction
  1788. '            '* of MOD using larger numbers and small divisors
  1789. '            GetMinMaxArray_n = Finish& - Start&
  1790. '            GetMinMaxArray_TT = GetMinMaxArray_n MOD 10000
  1791. '            GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  1792. '            IF (GetMinMaxArray_NMod2 MOD 2) THEN
  1793. '                GetMinMaxArray_minmax.min = Start&
  1794. '                GetMinMaxArray_minmax.max = Start&
  1795. '                GetGetMinMaxArray_minmaxArray_i = Start& + 1
  1796. '            ELSE
  1797. '                IF CGSortLibArr(Start&) > CGSortLibArr(Finish&) THEN
  1798. '                    GetMinMaxArray_minmax.max = Start&
  1799. '                    GetMinMaxArray_minmax.min = Finish&
  1800. '                ELSE
  1801. '                    GetMinMaxArray_minmax.min = Finish&
  1802. '                    GetMinMaxArray_minmax.max = Start&
  1803. '                END IF
  1804. '                GetGetMinMaxArray_minmaxArray_i = Start& + 2
  1805. '            END IF
  1806.  
  1807. '            WHILE GetGetMinMaxArray_minmaxArray_i < Finish&
  1808. '                IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) > CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) THEN
  1809. '                    IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) > CGSortLibArr(GetMinMaxArray_minmax.max) THEN
  1810. '                        GetMinMaxArray_minmax.max = GetGetMinMaxArray_minmaxArray_i
  1811. '                    END IF
  1812. '                    IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) < CGSortLibArr(GetMinMaxArray_minmax.min) THEN
  1813. '                        GetMinMaxArray_minmax.min = GetGetMinMaxArray_minmaxArray_i + 1
  1814. '                    END IF
  1815. '                ELSE
  1816. '                    IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i + 1) > CGSortLibArr(GetMinMaxArray_minmax.max) THEN
  1817. '                        GetMinMaxArray_minmax.max = GetGetMinMaxArray_minmaxArray_i + 1
  1818. '                    END IF
  1819. '                    IF CGSortLibArr(GetGetMinMaxArray_minmaxArray_i) < CGSortLibArr(GetMinMaxArray_minmax.min) THEN
  1820. '                        GetMinMaxArray_minmax.min = GetGetMinMaxArray_minmaxArray_i
  1821. '                    END IF
  1822. '                END IF
  1823. '                GetGetMinMaxArray_minmaxArray_i = GetGetMinMaxArray_minmaxArray_i + 2
  1824. '            WEND
  1825. '    END SELECT
  1826. 'END SUB
  1827. SUB GetArrayMinmax (a() AS DOUBLE, start&, finish&, arec AS MinMaxRec)
  1828.     arec.min = start&
  1829.     arec.max = start&
  1830.     DIM GetArrayMinmax_u AS LONG
  1831.     FOR GetArrayMinmax_u = start& + 1 TO finish&
  1832.         IF a(GetArrayMinmax_u) < a(arec.min) THEN arec.min = GetArrayMinmax_u
  1833.         IF a(GetArrayMinmax_u) > a(arec.max) THEN arec.max = GetArrayMinmax_u
  1834.     NEXT
  1835.  
  1836. SUB GetMinMaxArray (cg() AS DOUBLE, start&, finish&, MinMaxArray AS MinMaxRec)
  1837.     IF finish& - start& > 31 THEN
  1838.         'DIM GetMinMaxArray_i AS LONG
  1839.         DIM GetMinMaxArray_i AS LONG
  1840.         DIM GetMinMaxArray_n AS LONG
  1841.         DIM GetMinMaxArray_TT AS LONG
  1842.         DIM GetMinMaxArray_NMod2 AS INTEGER
  1843.         '* this is a workaround for the irritating malfunction
  1844.         '* of MOD using larger numbers and small divisors
  1845.         GetMinMaxArray_n = finish& - start&
  1846.         int10000& = (finish& - start&) \ 10000
  1847.         GetMinMaxArray_NMod2 = (finish& - start&) - 10000 * int10000&
  1848.         '* GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  1849.         IF (GetMinMaxArray_NMod2 MOD 2) THEN
  1850.             MinMaxArray.min = start&
  1851.             MinMaxArray.max = start&
  1852.             GetMinMaxArray_i = start& + 1
  1853.         ELSE
  1854.             IF cg(start&) > cg(finish&) THEN
  1855.                 MinMaxArray.max = start&
  1856.                 MinMaxArray.min = finish&
  1857.             ELSE
  1858.                 MinMaxArray.min = finish&
  1859.                 MinMaxArray.max = start&
  1860.             END IF
  1861.             GetMinMaxArray_i = start& + 2
  1862.         END IF
  1863.  
  1864.         WHILE GetMinMaxArray_i < finish&
  1865.             IF cg(GetMinMaxArray_i) > cg(GetMinMaxArray_i + 1) THEN
  1866.                 IF cg(GetMinMaxArray_i) > cg(MinMaxArray.max) THEN
  1867.                     MinMaxArray.max = GetMinMaxArray_i
  1868.                 END IF
  1869.                 IF cg(GetMinMaxArray_i + 1) < cg(MinMaxArray.min) THEN
  1870.                     MinMaxArray.min = GetMinMaxArray_i + 1
  1871.                 END IF
  1872.             ELSE
  1873.                 IF cg(GetMinMaxArray_i + 1) > cg(MinMaxArray.max) THEN
  1874.                     MinMaxArray.max = GetMinMaxArray_i + 1
  1875.                 END IF
  1876.                 IF cg(GetMinMaxArray_i) < cg(MinMaxArray.min) THEN
  1877.                     MinMaxArray.min = GetMinMaxArray_i
  1878.                 END IF
  1879.             END IF
  1880.             GetMinMaxArray_i = GetMinMaxArray_i + 2
  1881.         WEND
  1882.     ELSE
  1883.         GetArrayMinmax cg(), start&, finish&, MinMaxArray
  1884.     END IF
  1885.  
  1886. SUB HeapSort (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)
  1887.     FOR i& = Start& + 1 TO Finish&
  1888.         PercolateUp CGSortLibArr(), Start&, i&, order&
  1889.     NEXT i&
  1890.  
  1891.     FOR i& = Finish& TO Start& + 1 STEP -1
  1892.         SWAP CGSortLibArr(Start&), CGSortLibArr(i&)
  1893.         PercolateDown CGSortLibArr(), Start&, i& - 1, order&
  1894.     NEXT i&
  1895.  
  1896. SUB PercolateDown (CGSortLibArr() AS DOUBLE, Start&, MaxLevel&, order&)
  1897.     i& = Start&
  1898.     '* Move the value in GetPixel&(Start&) down the heap until it has
  1899.     '* reached its proper node (that is, until it is less than its parent
  1900.     '* node or until it has reached MaxLevel&, the bottom of the current heap):
  1901.     DO
  1902.         Child& = 2 * (i& - Start&) + Start& ' Get the subscript for the Child& node.
  1903.         '* Reached the bottom of the heap, so exit this procedure:
  1904.         IF Child& > MaxLevel& THEN EXIT DO
  1905.         SELECT CASE order&
  1906.             CASE 1
  1907.                 '* If there are two Child nodes, find out which one is bigger:
  1908.                 ax& = Child& + 1
  1909.                 IF ax& <= MaxLevel& THEN
  1910.                     IF CGSortLibArr(ax&) > CGSortLibArr(Child&) THEN
  1911.                         Child& = ax&
  1912.                     END IF
  1913.                 END IF
  1914.  
  1915.                 '* Move the value down if it is still not bigger than either one of
  1916.                 '* its Child&ren:
  1917.                 IF CGSortLibArr(i&) < CGSortLibArr(Child&) THEN
  1918.                     SWAP CGSortLibArr(i&), CGSortLibArr(Child&)
  1919.                     i& = Child&
  1920.                 ELSE
  1921.                     '* Otherwise, CGSortLibArr() has been restored to a heap from start& to MaxLevel&,
  1922.                     '* so exit:
  1923.                     EXIT DO
  1924.                 END IF
  1925.             CASE ELSE
  1926.                 '* If there are two Child nodes, find out which one is smaller:
  1927.                 ax& = Child& + 1
  1928.                 IF ax& <= MaxLevel& THEN
  1929.                     IF CGSortLibArr(ax&) < CGSortLibArr(Child&) THEN
  1930.                         Child& = ax&
  1931.                     END IF
  1932.                 END IF
  1933.                 '* Move the value down if it is still not smaller than either one of
  1934.                 '* its Child&ren:
  1935.                 IF CGSortLibArr(i&) > CGSortLibArr(Child&) THEN
  1936.                     SWAP CGSortLibArr(i&), CGSortLibArr(Child&)
  1937.                     i& = Child&
  1938.                 ELSE
  1939.                     '* Otherwise, CGSortLibArr() has been restored to a heap from start& to MaxLevel&,
  1940.                     '* so exit:
  1941.                     EXIT DO
  1942.                 END IF
  1943.         END SELECT
  1944.     LOOP
  1945.  
  1946. SUB PercolateUp (CGSortLibArr() AS DOUBLE, Start&, MaxLevel&, order&)
  1947.     SELECT CASE order&
  1948.         CASE 1
  1949.             i& = MaxLevel&
  1950.             '* Move the value in CGSortLibArr(MaxLevel&) up the heap until it has
  1951.             '* reached its proper node (that is, until it is greater than either
  1952.             '* of its Child& nodes, or until it has reached 1, the top of the heap):
  1953.             DO UNTIL i& = Start&
  1954.                 '* Get the subscript for the parent node.
  1955.                 Parent& = Start& + (i& - Start&) \ 2
  1956.                 '* The value at the current node is still bigger than the value at
  1957.                 '* its parent node, so swap these two array elements:
  1958.                 IF CGSortLibArr(i&) > CGSortLibArr(Parent&) THEN
  1959.                     SWAP CGSortLibArr(Parent&), CGSortLibArr(i&)
  1960.                     i& = Parent&
  1961.                 ELSE
  1962.                     '* Otherwise, the element has reached its proper place in the heap,
  1963.                     '* so exit this procedure:
  1964.                     EXIT DO
  1965.                 END IF
  1966.             LOOP
  1967.         CASE ELSE
  1968.             i& = MaxLevel&
  1969.             '* Move the value in CGSortLibArr(MaxLevel&) up the heap until it has
  1970.             '* reached its proper node (that is, until it is greater than either
  1971.             '* of its Child& nodes, or until it has reached 1, the top of the heap):
  1972.             DO UNTIL i& = Start&
  1973.                 '* Get the subscript for the parent node.
  1974.                 Parent& = Start& + (i& - Start&) \ 2
  1975.                 '* The value at the current node is still smaller than the value at
  1976.                 '* its parent node, so swap these two array elements:
  1977.                 IF CGSortLibArr(i&) < CGSortLibArr(Parent&) THEN
  1978.                     SWAP CGSortLibArr(Parent&), CGSortLibArr(i&)
  1979.                     i& = Parent&
  1980.                 ELSE
  1981.                     '* Otherwise, the element has reached its proper place in the heap,
  1982.                     '* so exit this procedure:
  1983.                     EXIT DO
  1984.                 END IF
  1985.             LOOP
  1986.     END SELECT
  1987.  
  1988. '****************************************
  1989. '* The IntroSort() algorithm extended to QBxx because there is no qbxx-compatible code
  1990. '* The IntroSort algorithm extended to qb64 because i could find no pure qbxx code
  1991. '* 03Jun2017, by CodeGuy -- further mods for use in sorting library 03 Aug 2017
  1992. '* Introspective Sort (IntroSort) falls back to MergeSort after so many levels of
  1993. '* recursion and is good for highly redundant data (aka few unique)
  1994. '* for very short runs, it falls back to InsertionSort.
  1995.  
  1996. SUB QuickSortIntrospective (CGSortLibArr() AS DOUBLE, IntroSort_start AS LONG, IntroSort_finish AS LONG, order&)
  1997.     DIM IntroSort_i AS LONG
  1998.     DIM IntroSort_J AS LONG
  1999.     STATIC IntroSort_level&
  2000.     STATIC IntroSort_MaxRecurseLevel&
  2001.     IntroSort_MaxRecurseLevel& = 15
  2002.     IF IntroSort_start < IntroSort_finish THEN
  2003.         IF IntroSort_finish - IntroSort_start > 31 THEN
  2004.             IF IntroSort_level& > IntroSort_MaxRecurseLevel& THEN
  2005.                 HeapSort CGSortLibArr(), IntroSort_start, IntroSort_finish, order&
  2006.             ELSE
  2007.                 IntroSort_level& = IntroSort_level& + 1
  2008.                 QuickSortIJ CGSortLibArr(), IntroSort_start, IntroSort_finish, IntroSort_i, IntroSort_J, order&
  2009.                 QuickSortIntrospective CGSortLibArr(), IntroSort_start, IntroSort_J, order&
  2010.                 QuickSortIntrospective CGSortLibArr(), IntroSort_i, IntroSort_finish, order&
  2011.                 IntroSort_level& = IntroSort_level& - 1
  2012.             END IF
  2013.         ELSE
  2014.             InsertionSort CGSortLibArr(), IntroSort_start, IntroSort_finish, order&
  2015.         END IF
  2016.     END IF
  2017.  
  2018. SUB QuickSortIJ (CGSortLibArr() AS DOUBLE, start&, finish&, i&, j&, order&)
  2019.     DIM Compare AS DOUBLE '* MUST be the same type as CGSortLibArr()
  2020.     i& = start&
  2021.     j& = finish&
  2022.     Compare = CGSortLibArr(i& + (j& - i&) \ 2)
  2023.     SELECT CASE order&
  2024.         CASE 1
  2025.             DO
  2026.                 DO WHILE CGSortLibArr(i&) < Compare
  2027.                     i& = i& + 1
  2028.                 LOOP
  2029.                 DO WHILE CGSortLibArr(j&) > Compare
  2030.                     j& = j& - 1
  2031.                 LOOP
  2032.                 IF i& <= j& THEN
  2033.                     IF i& <> j& THEN
  2034.                         SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  2035.                     END IF
  2036.                     i& = i& + 1
  2037.                     j& = j& - 1
  2038.                 END IF
  2039.             LOOP UNTIL i& > j&
  2040.         CASE ELSE
  2041.             DO
  2042.                 DO WHILE CGSortLibArr(i&) > Compare
  2043.                     i& = i& + 1
  2044.                 LOOP
  2045.                 DO WHILE CGSortLibArr(j&) < Compare
  2046.                     j& = j& - 1
  2047.                 LOOP
  2048.                 IF i& <= j& THEN
  2049.                     IF i& <> j& THEN
  2050.                         SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  2051.                     END IF
  2052.                     i& = i& + 1
  2053.                     j& = j& - 1
  2054.                 END IF
  2055.             LOOP UNTIL i& > j&
  2056.     END SELECT
  2057.  
  2058. '*********************************
  2059. '* The Standard Merge Algorithm extended to ascending or descending order
  2060. '* same tactic as MergeSort, but only MergeSorts halves amd then merges, with o(NlogN) for each half with straight Merge
  2061. '* the benefit of this meOhod is not only faster completion but also a 50% reduction in array allocation and copying.
  2062. '* this approach can be used in pretty much any sort to yield a faster sort, including the already-fast FlashSort. I will
  2063. '* attempt a string version of FlashSort. It will be complex.
  2064. '*********************************
  2065. SUB MergeSortTwoWay (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2066.     middle& = start& + (finish& - start&) \ 2
  2067.     MergeSortEmerge CGSortLibArr(), start&, middle&, order&
  2068.     MergeSortEmerge CGSortLibArr(), middle& + 1, finish&, order&
  2069.     'IF order& = 1 THEN
  2070.     '    EfficientMerge CGSortLibArr(), start&, finish&, order&
  2071.     'ELSE
  2072.     '    MergeRoutine CGSortLibArr(), start&, finish&, order&
  2073.     'END IF
  2074.  
  2075. '**********************
  2076. '* Standardized Merge procedure. Assumes CGSortLibArr(start to middle), CGSortLibArr(middle+1 to finish) is already sorted on arrival.
  2077. '**********************
  2078. SUB MergeRoutine (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2079.     length& = finish& - start&
  2080.     middle& = start& + length& \ 2
  2081.     DIM temp(0 TO length&) AS DOUBLE
  2082.     FOR i& = 0 TO length&
  2083.         temp(i&) = CGSortLibArr(start& + i&)
  2084.     NEXT
  2085.     '* for refactoring purposes,
  2086.     '* mptr& = 0
  2087.     '* sptr& = middle& - start& + 1
  2088.     '* could be omitted from the select case blocks and declared here instead. However, I am leaving them as is
  2089.     '* so code between SELECT CASE conditional checks can simply be copied for a fully functioning merge.
  2090.  
  2091.     SELECT CASE order&
  2092.         CASE 1
  2093.             mptr& = 0
  2094.             sptr& = middle& - start& + 1
  2095.             FOR i& = 0 TO length&
  2096.                 IF sptr& <= finish& - start& THEN
  2097.                     IF mptr& <= middle& - start& THEN
  2098.                         IF temp(mptr&) > temp(sptr&) THEN
  2099.                             CGSortLibArr(i& + start&) = temp(sptr&)
  2100.                             sptr& = sptr& + 1
  2101.                         ELSE
  2102.                             CGSortLibArr(i& + start&) = temp(mptr&)
  2103.                             mptr& = mptr& + 1
  2104.                         END IF
  2105.                     ELSE
  2106.                         CGSortLibArr(i& + start&) = temp(sptr&)
  2107.                         sptr& = sptr& + 1
  2108.                     END IF
  2109.                 ELSE
  2110.                     CGSortLibArr(i& + start&) = temp(mptr&)
  2111.                     mptr& = mptr& + 1
  2112.                 END IF
  2113.             NEXT
  2114.         CASE ELSE
  2115.             mptr& = 0
  2116.             sptr& = middle& - start& + 1
  2117.             FOR i& = 0 TO length&
  2118.                 IF sptr& <= finish& - start& THEN
  2119.                     IF mptr& <= middle& - start& THEN
  2120.                         '* i see what you did there -- change from
  2121.                         '* temp(mptr&) > temp(sptr&) to temp(sptr&) > temp(mptr&)
  2122.                         IF temp(sptr&) > temp(mptr&) THEN
  2123.                             CGSortLibArr(i& + start&) = temp(sptr&)
  2124.                             sptr& = sptr& + 1
  2125.                         ELSE
  2126.                             CGSortLibArr(i& + start&) = temp(mptr&)
  2127.                             mptr& = mptr& + 1
  2128.                         END IF
  2129.                     ELSE
  2130.                         CGSortLibArr(i& + start&) = temp(sptr&)
  2131.                         sptr& = sptr& + 1
  2132.                     END IF
  2133.                 ELSE
  2134.                     CGSortLibArr(i& + start&) = temp(mptr&)
  2135.                     mptr& = mptr& + 1
  2136.                 END IF
  2137.             NEXT
  2138.     END SELECT
  2139.     ERASE temp
  2140.  
  2141. FUNCTION SequenceCheck& (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2142.     SequenceCheck& = start&
  2143.     i& = start&
  2144.     SELECT CASE order&
  2145.         CASE 1
  2146.             FOR j& = start& + 1 TO finish&
  2147.                 IF CGSortLibArr(j&) > CGSortLibArr(i&) THEN
  2148.                     i& = j& '
  2149.                 ELSEIF CGSortLibArr(j&) < CGSortLibArr(i&) THEN
  2150.                     SequenceCheck& = j&
  2151.                     EXIT FUNCTION
  2152.                 END IF
  2153.             NEXT
  2154.         CASE ELSE
  2155.             FOR j& = start& + 1 TO finish&
  2156.                 IF CGSortLibArr(j&) < CGSortLibArr(i&) THEN
  2157.                     i& = j& '
  2158.                 ELSEIF CGSortLibArr(j&) > CGSortLibArr(i&) THEN
  2159.                     SequenceCheck& = j&
  2160.                     EXIT FUNCTION
  2161.                 END IF
  2162.             NEXT
  2163.     END SELECT
  2164.     SequenceCheck& = finish&
  2165.  
  2166. '***************************************************************************
  2167. '* string-specific code
  2168. '***************************************************************************
  2169. SUB FlashString (StrCGSortLibArr() AS STRING, start&, finish&, order&)
  2170.     TYPE FlashRec
  2171.         Number AS _INTEGER64
  2172.         Index AS LONG
  2173.     END TYPE
  2174.     REDIM FlashStringCGSortLibArr(start& TO finish&) AS FlashRec
  2175.     DIM shift##(0 TO 7)
  2176.     IF order& = 1 THEN
  2177.         shift##(7) = 1
  2178.         FOR z% = 6 TO 0 STEP -1
  2179.             shift##(z%) = shift##(z% + 1) * 256
  2180.         NEXT
  2181.     ELSE
  2182.         shift##(0) = 1
  2183.         FOR z% = 1 TO 7
  2184.             shift##(z%) = shift##(z% - 1) * 256
  2185.         NEXT
  2186.     END IF
  2187.     FOR s& = start& TO finish&
  2188.         acc## = 0
  2189.         WHILE z% < 8
  2190.             zp% = z% + 1
  2191.             p$ = MID$(StrCGSortLibArr(s&), zp%, 1)
  2192.             IF p$ > "" THEN
  2193.                 acc## = acc## + shift##(z%) * ASC(p$)
  2194.                 z% = zp%
  2195.             ELSE
  2196.                 EXIT WHILE
  2197.             END IF
  2198.         WEND
  2199.         FlashStringCGSortLibArr(s&).Number = acc##
  2200.         FlashStringCGSortLibArr(s&).Index = s&
  2201.     NEXT
  2202.     flashSORTType FlashStringCGSortLibArr(), start&, finish&, order&
  2203.  
  2204. SUB flashSORTType (CGSortLibArr() AS FlashRec, start AS LONG, finish AS LONG, order&)
  2205.     '* change these:
  2206.     DIM hold AS FlashRec
  2207.     DIM flash AS FlashRec
  2208.     DIM ANMiN AS FlashRec
  2209.     '* to the same type as the array being sorted
  2210.  
  2211.     '* change these:
  2212.     DIM KIndex AS _UNSIGNED LONG
  2213.     DIM MIndex AS _UNSIGNED LONG
  2214.     DIM SIndex AS _UNSIGNED LONG
  2215.     '* to long for qbxx as qbxx has no _unsigned types
  2216.  
  2217.     '* the original ratio was .125 but i kept getting array bounds errors
  2218.     MIndex = (INT(.128 * (finish - start + 1)) + 1) OR 2
  2219.  
  2220.     '* change these:
  2221.     DIM FlashTrackL(0 TO MIndex) AS LONG
  2222.     DIM FlashI AS DOUBLE
  2223.     DIM FlashJ AS DOUBLE
  2224.     DIM NextFlashJ AS DOUBLE
  2225.     DIM FlashNMove AS DOUBLE
  2226.     DIM MaxValueIndex AS DOUBLE
  2227.     DIM FinishMinusOne AS DOUBLE
  2228.     '* to the appropriate type for the range being sorted (must match start, finish variables)
  2229.  
  2230.     '* don't mess:
  2231.     DIM FlashC1 AS DOUBLE '* for some reason does not work with _float
  2232.     '* with this. it needs to be a double at the very least but float gives this a far greater range
  2233.     '* more than likely more range than is practical. but ya never know (change this to double for qbxx)
  2234.  
  2235.     ' sorts array A with finish elements by use of
  2236.     ' index vector FlashTrackL with MIndex elements, with MIndex ca. 0.125(finish-start).
  2237.     ' Translation of Karl-Dietrich Neubert's FlashSort
  2238.     ' algorithm into BASIC by Erdmann Hess.
  2239.     ' Generalized Numeric Version -- recoded by codeguy
  2240.  
  2241.     '* This is the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  2242.     '* strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  2243.     '* namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  2244.     '* kept popping up. Traced it to FlashTrackL() and added a minor (about 2.4%) increase in the upper bound of FlashTrackL().
  2245.     '* I suppose this could also be used for non-integer and non-string types as well.
  2246.  
  2247.     REM =============== CLASS FORMATION =================
  2248.  
  2249.     ANMiN = CGSortLibArr(start)
  2250.     MaxValueIndex = start
  2251.     FOR FlashI = start TO finish
  2252.         IF (CGSortLibArr(FlashI).Number > CGSortLibArr(MaxValueIndex).Number) THEN MaxValueIndex = FlashI
  2253.         IF (CGSortLibArr(FlashI).Number < ANMiN.Number) THEN
  2254.             ANMiN = CGSortLibArr(FlashI)
  2255.             SWAP CGSortLibArr(start), CGSortLibArr(FlashI)
  2256.         END IF
  2257.     NEXT FlashI
  2258.  
  2259.     IF ANMiN.Number = CGSortLibArr(MaxValueIndex).Number THEN
  2260.         '* this is a monotonic sequence array and by definition is already sorted
  2261.         EXIT SUB
  2262.     END IF
  2263.  
  2264.     FlashC1 = (MIndex - 1) / (CGSortLibArr(MaxValueIndex).Number - ANMiN.Number)
  2265.  
  2266.     FOR FlashI = start + 1 TO finish
  2267.         KIndex = INT(FlashC1 * (CGSortLibArr(FlashI).Number - ANMiN.Number)) + 1
  2268.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + 1
  2269.     NEXT
  2270.  
  2271.     FOR KIndex = LBOUND(FlashTrackL) + 1 TO MIndex
  2272.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + FlashTrackL(KIndex - 1)
  2273.     NEXT KIndex
  2274.  
  2275.     REM ==================== PERMUTATION ================
  2276.     FlashNMove = 0
  2277.     FlashJ = start + 1
  2278.     KIndex = MIndex
  2279.     FinishMinusOne = finish - 1
  2280.     SWAP CGSortLibArr(finish), CGSortLibArr(MaxValueIndex)
  2281.     WHILE (FlashNMove < FinishMinusOne)
  2282.         WHILE (FlashJ > FlashTrackL(KIndex))
  2283.             FlashJ = FlashJ + 1
  2284.             KIndex = INT(FlashC1 * (CGSortLibArr(FlashJ).Number - ANMiN.Number)) + 1
  2285.         WEND
  2286.         flash = CGSortLibArr(FlashJ)
  2287.         DO
  2288.             IF (FlashJ = (FlashTrackL(KIndex) + 1)) THEN
  2289.                 EXIT DO
  2290.             ELSE
  2291.                 IF FlashNMove < (FinishMinusOne) THEN
  2292.                     KIndex = INT(FlashC1 * (flash.Number - ANMiN.Number)) + 1
  2293.                     hold = CGSortLibArr(FlashTrackL(KIndex))
  2294.                     CGSortLibArr(FlashTrackL(KIndex)) = flash
  2295.                     flash = hold
  2296.                     FlashTrackL(KIndex) = FlashTrackL(KIndex) - 1
  2297.                     FlashNMove = FlashNMove + 1
  2298.                 ELSE
  2299.                     EXIT DO
  2300.                 END IF
  2301.             END IF
  2302.         LOOP
  2303.     WEND
  2304.  
  2305.     '================= Insertion Sort============
  2306.     FOR SIndex = LBOUND(FlashtrackL) + 1 TO MIndex
  2307.         '* sort subranges
  2308.         FOR FlashI = FlashTrackL(SIndex) - 1 TO FlashTrackL(SIndex - 1) STEP -1
  2309.             IF (CGSortLibArr(FlashI + 1).Number < CGSortLibArr(FlashI).Number) THEN
  2310.                 hold = CGSortLibArr(FlashI)
  2311.                 NextFlashJ = FlashI
  2312.                 DO
  2313.                     FlashJ = NextFlashJ
  2314.                     IF FlashJ < FlashTrackL(SIndex) THEN
  2315.                         NextFlashJ = FlashJ + 1
  2316.                         IF (CGSortLibArr(NextFlashJ).Number < hold.Number) THEN
  2317.                             SWAP CGSortLibArr(FlashJ), CGSortLibArr(NextFlashJ)
  2318.                         ELSE
  2319.                             EXIT DO
  2320.                         END IF
  2321.                     ELSE
  2322.                         EXIT DO
  2323.                     END IF
  2324.                 LOOP
  2325.                 CGSortLibArr(FlashJ) = hold
  2326.             END IF
  2327.         NEXT
  2328.         '* 914k/Ghz when it reaches this point, assuming this array is mostly sorted.
  2329.     NEXT
  2330.     FOR s& = start& TO finish&
  2331.         SWAP StrCGSortLibArr(s&), StrCGSortLibArr(CGSortLibArr(s&).Index)
  2332.     NEXT
  2333.     FOR s& = start& TO finish& - 1
  2334.         FOR t& = s& + 1 TO finish&
  2335.             IF StrCGSortLibArr(s&) > StrCGSortLibArr(s& + 1) THEN
  2336.                 SWAP StrCGSortLibArr(s&), StrCGSortLibArr(s& + 1)
  2337.             ELSE
  2338.                 EXIT FOR
  2339.             END IF
  2340.         NEXT
  2341.     NEXT
  2342.     IF order <> 1 THEN
  2343.         IF order <> 0 THEN
  2344.             FlashI = start
  2345.             FlashJ = finish
  2346.             WHILE FlashI < FlashJ
  2347.                 SWAP StrCGSortLibArr(FlashI), StrCGSortLibArr(FlashJ)
  2348.                 FlashI = FlashI - 1
  2349.                 FlashJ = FlashJ - 1
  2350.             WEND
  2351.         END IF
  2352.     END IF
  2353.  
  2354. SUB PrimeGapSort2Split (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  2355.     primeGapSort2 CGSortLibArr(), start, start + (finish - start) \ 2, order&
  2356.     primeGapSort2 CGSortLibArr(), start + (finish - start) \ 2 + 1, finish, order&
  2357.     EfficientMerge CGSortLibArr(), start, finish, order&
  2358.  
  2359. '*******************
  2360. '* PrimeGapSort2 uses PrimeNumber&() function to calculate the prime number less than or equal to the gap
  2361. '* this is a variation of shellsort. This variation is thus far the fastest non-recursive, in-place sorting
  2362. '* algorithm. Invented by CodeGuy. Tested, proven and improved by CodeGuy and Zom-B.
  2363. '*******************
  2364. SUB primeGapSort2 (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2365.     SELECT CASE order&
  2366.         CASE 1
  2367.             gap& = (finish& - start& + 1)
  2368.             DO
  2369.                 FOR i& = start& TO finish& - gap&
  2370.                     IF CGSortLibArr(i&) > CGSortLibArr(i& + gap&) THEN
  2371.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + gap&)
  2372.                     END IF
  2373.                 NEXT
  2374.                 gap& = primeNumber&(gap& * 0.727)
  2375.             LOOP WHILE gap& > 1
  2376.             InsertionSort CGSortLibArr(), start&, finish&, order&
  2377.         CASE ELSE
  2378.             gap& = (finish& - start& + 1)
  2379.             DO
  2380.                 FOR i& = start& TO finish& - gap&
  2381.                     IF CGSortLibArr(i&) < CGSortLibArr(i& + gap&) THEN
  2382.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + gap&)
  2383.                     END IF
  2384.                 NEXT
  2385.                 gap& = primeNumber&(gap& * 0.727)
  2386.             LOOP WHILE gap& > 1
  2387.             InsertionSort CGSortLibArr(), start&, finish&, order&
  2388.     END SELECT
  2389.  
  2390. FUNCTION primeNumber& (a&)
  2391.     ' Find a prime number below a& (excluding 3 and 5)
  2392.     '
  2393.     ' Notice that there is a:
  2394.     ' 59,9% chance for a single successive guess,
  2395.     ' 83,9% chance for a successive guess out of two guesses,
  2396.     ' 93,6% chance for a successive guess out of three guesses,
  2397.     ' 97,4% chance for a successive guess out of four guesses,
  2398.     ' 99,98% chance for a successive guess out of ten guesses...
  2399.     '
  2400.     ' Worst bad luck over 10000 tested primes: 19 guesses.
  2401.     STATIC addtoskip5%()
  2402.     STATIC firstCall%
  2403.     STATIC pps%() 'Previous Prime in Sequence. Contains about 59.9% of all primes modulo 30.
  2404.     '* wheel factorization by Zom-B
  2405.     IF firstCall% = 0 THEN
  2406.         firstCall% = -1
  2407.         REDIM pps%(0 TO 29)
  2408.         ' Map numbers from 0 to 29 to the next lower prime in the sequence {1,7,11,13,17,19,23,29}.
  2409.         pps%(0) = -1: pps%(1) = -1 ' -1 = 29 (modulo 30)
  2410.         pps%(2) = 1: pps%(3) = 1: pps%(4) = 1: pps%(5) = 1: pps%(6) = 1: pps%(7) = 1
  2411.         pps%(8) = 7: pps%(9) = 7: pps%(10) = 7: pps%(11) = 7
  2412.         pps%(12) = 11: pps%(13) = 11:
  2413.         pps%(14) = 13: pps%(15) = 13: pps%(16) = 13: pps%(17) = 13
  2414.         pps%(18) = 17: pps%(19) = 17
  2415.         pps%(20) = 19: pps%(21) = 19: pps%(22) = 19: pps%(23) = 19
  2416.         pps%(24) = 23: pps%(25) = 23: pps%(26) = 23: pps%(27) = 23: pps%(28) = 23: pps%(29) = 23
  2417.         REDIM addtoskip5%(3)
  2418.         addtoskip5%(0) = 2
  2419.         addtoskip5%(1) = 4
  2420.         addtoskip5%(2) = 2
  2421.         addtoskip5%(3) = 2
  2422.     END IF
  2423.  
  2424.     b& = a& + 1
  2425.     c& = (b& \ 30) * 30
  2426.     b& = c& + pps%(b& - c&)
  2427.     div& = 3
  2428.     asi% = 1
  2429.     DO
  2430.         IF b& MOD div& THEN
  2431.             IF b& / div& < div& THEN
  2432.                 EXIT DO
  2433.             ELSE
  2434.                 div& = div& + addtoskip5%(asi%)
  2435.                 asi% = (asi% + 1) AND 3
  2436.             END IF
  2437.         ELSE
  2438.             c& = (b& \ 30) * 30
  2439.             b& = c& + pps%(b& - c&)
  2440.             div& = 3
  2441.             asi% = 1
  2442.         END IF
  2443.     LOOP
  2444.     primeNumber& = b&
  2445.  
  2446.  
  2447. '*******************
  2448. '* CombSort is the same as shellsort except a reduction factor of 1.3
  2449. '*******************
  2450. SUB CombSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2451.     SELECT CASE finish& - start&
  2452.         CASE 1
  2453.             IF CGSortLibArr(start&) > CGSortLibArr(finish&) THEN
  2454.                 IF order& = 1 THEN
  2455.                     SWAP CGSortLibArr(start&), CGSortLibArr(finish&)
  2456.                 END IF
  2457.             END IF
  2458.         CASE IS > 1
  2459.             IF order& = 1 THEN
  2460.                 ShellSortGap& = INT(10 * (finish& - start&) / 13)
  2461.                 DO
  2462.                     IF ShellSortGap& > 1 THEN
  2463.                         LoopCount& = 0
  2464.                         xstart& = start&
  2465.                         xfinish& = finish& - ShellSortGap&
  2466.                         MaxPasses& = (finish& - start&) \ ShellSortGap&
  2467.                         DO
  2468.                             xfirst& = xfinish&
  2469.                             FOR ShellSortS& = xstart& TO xfinish&
  2470.                                 IF CGSortLibArr(ShellSortS&) > CGSortLibArr(ShellSortS& + ShellSortGap&) THEN
  2471.                                     SWAP CGSortLibArr(ShellSortS&), CGSortLibArr(ShellSortS& + ShellSortGap&)
  2472.                                     Last& = ShellSortS&
  2473.                                     IF ShellSortS& < xfirst& THEN
  2474.                                         xfirst& = ShellSortS&
  2475.                                     END IF
  2476.                                 END IF
  2477.                             NEXT
  2478.                             xfinish& = Last&
  2479.                             xstart& = xfirst&
  2480.                             LoopCount& = LoopCount& + 1
  2481.                         LOOP WHILE LoopCount& < MaxPasses& AND (xfinish& - xstart&) >= ShellSortGap&
  2482.                         ShellSortGap& = INT(10 * (ShellSortGap& / 13))
  2483.                     ELSE
  2484.                         InsertionSort CGSortLibArr(), start&, finish&, order&
  2485.                         EXIT DO
  2486.                     END IF
  2487.                 LOOP
  2488.             ELSE
  2489.                 ShellSortGap& = INT(10 * (finish& - start&) / 13)
  2490.                 DO
  2491.                     IF ShellSortGap& > 1 THEN
  2492.                         LoopCount& = 0
  2493.                         xstart& = start&
  2494.                         xfinish& = finish& - ShellSortGap&
  2495.                         MaxPasses& = (finish& - start&) \ ShellSortGap&
  2496.                         DO
  2497.                             xfirst& = xfinish&
  2498.                             FOR ShellSortS& = xstart& TO xfinish&
  2499.                                 IF CGSortLibArr(ShellSortS&) < CGSortLibArr(ShellSortS& + ShellSortGap&) THEN
  2500.                                     SWAP CGSortLibArr(ShellSortS&), CGSortLibArr(ShellSortS& + ShellSortGap&)
  2501.                                     Last& = ShellSortS&
  2502.                                     IF ShellSortS& < xfirst& THEN
  2503.                                         xfirst& = ShellSortS&
  2504.                                     END IF
  2505.                                 END IF
  2506.                             NEXT
  2507.                             xfinish& = Last&
  2508.                             xstart& = xfirst&
  2509.                             LoopCount& = LoopCount& + 1
  2510.                         LOOP WHILE LoopCount& < MaxPasses& AND (xfinish& - xstart&) >= ShellSortGap&
  2511.                         ShellSortGap& = INT(10 * (ShellSortGap& / 13))
  2512.                     ELSE
  2513.                         InsertionSort CGSortLibArr(), start&, finish&, order&
  2514.                         EXIT DO
  2515.                     END IF
  2516.                 LOOP
  2517.  
  2518.             END IF
  2519.     END SELECT
  2520.  
  2521. '********************************
  2522. '* EfficientMerge, developed from StackOverflow, a horribly short description of the procedure.
  2523. '* Uses n/2 auxiliary array for a 50% memory reduction used in merging and similar reduction in
  2524. '* time-consuming array copying. Very handly when memory and time is limited.
  2525. '* assumes the array passed has already been sorted. Like all other algorithms, this may be
  2526. '* used recursively. However for the purpose of MergeSort, it is used as a helper procedure.
  2527. '* corrected to use the corresponding EfficientMerge  method for both ascending and descending order.
  2528. '* provides performance symmetry regardless of sortation order. I will leave MergeRoutine as it is
  2529. '* proven stable, even if it is not as fast as EfficientMerge.
  2530. '********************************
  2531. SUB EfficientMerge (right() AS DOUBLE, start&, finish&, order&)
  2532.     half& = start& + (finish& - start&) \ 2
  2533.     REDIM left(start& TO half&) AS DOUBLE '* hold the first half of the array in left() -- must be the same type as right()
  2534.     FOR LoadLeft& = start& TO half&
  2535.         left(LoadLeft&) = right(LoadLeft&)
  2536.     NEXT
  2537.     SELECT CASE order&
  2538.         CASE 1
  2539.             i& = start&
  2540.             j& = half& + 1
  2541.             insert& = start&
  2542.             DO
  2543.                 IF i& > half& THEN '* left() exhausted
  2544.                     IF j& > finish& THEN '* right() exhausted
  2545.                         EXIT DO
  2546.                     ELSE
  2547.                         '* stuff remains in right to be inserted, so flush right()
  2548.                         WHILE j& <= finish&
  2549.                             right(insert&) = right(j&)
  2550.                             j& = j& + 1
  2551.                             insert& = insert& + 1
  2552.                         WEND
  2553.                         EXIT DO
  2554.                         '* and exit
  2555.                     END IF
  2556.                 ELSE
  2557.                     IF j& > finish& THEN
  2558.                         WHILE i& < LoadLeft&
  2559.                             right(insert&) = left(i&)
  2560.                             i& = i& + 1
  2561.                             insert& = insert& + 1
  2562.                         WEND
  2563.                         EXIT DO
  2564.                     ELSE
  2565.                         IF right(j&) < left(i&) THEN
  2566.                             right(insert&) = right(j&)
  2567.                             j& = j& + 1
  2568.                         ELSE
  2569.                             right(insert&) = left(i&)
  2570.                             i& = i& + 1
  2571.                         END IF
  2572.                         insert& = insert& + 1
  2573.                     END IF
  2574.                 END IF
  2575.             LOOP
  2576.         CASE ELSE
  2577.             i& = start&
  2578.             j& = half& + 1
  2579.             insert& = start&
  2580.             DO
  2581.                 IF i& > half& THEN '* left() exhausted
  2582.                     IF j& > finish& THEN '* right() exhausted
  2583.                         EXIT DO
  2584.                     ELSE
  2585.                         '* stuff remains in right to be inserted, so flush right()
  2586.                         WHILE j& <= finish&
  2587.                             right(insert&) = right(j&)
  2588.                             j& = j& + 1
  2589.                             insert& = insert& + 1
  2590.                         WEND
  2591.                         EXIT DO
  2592.                         '* and exit
  2593.                     END IF
  2594.                 ELSE
  2595.                     IF j& > finish& THEN
  2596.                         WHILE i& < LoadLeft&
  2597.                             right(insert&) = left(i&)
  2598.                             i& = i& + 1
  2599.                             insert& = insert& + 1
  2600.                         WEND
  2601.                         EXIT DO
  2602.                     ELSE
  2603.                         IF right(j&) > left(i&) THEN
  2604.                             right(insert&) = right(j&)
  2605.                             j& = j& + 1
  2606.                         ELSE
  2607.                             right(insert&) = left(i&)
  2608.                             i& = i& + 1
  2609.                         END IF
  2610.                         insert& = insert& + 1
  2611.                     END IF
  2612.                 END IF
  2613.             LOOP
  2614.     END SELECT
  2615.     ERASE left
  2616.  
  2617. '**********************
  2618. '* SelectionSort, another o(n^2) sort. generally used only for very short lists. total comparisons is N(N+1)/2,
  2619. '* regardless of the state of sortation, making this only slightly better than bubblesort. This version is stable
  2620. '* Both the stable and unstable variants are VERY slow for large N aka (finish-start)
  2621. '* [s+][i+]{n^2  ]
  2622. '**********************
  2623. SUB SelectionSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2624.     SELECT CASE order&
  2625.         CASE 1
  2626.             FOR s& = start& TO finish& - 1
  2627.                 u& = s&
  2628.                 FOR t& = s& + 1 TO finish&
  2629.                     IF CGSortLibArr(t&) < CGSortLibArr(u&) THEN
  2630.                         u& = t&
  2631.                     END IF
  2632.                 NEXT
  2633.                 IF u& <> s& THEN
  2634.                     SWAP CGSortLibArr(s&), CGSortLibArr(u&)
  2635.                 END IF
  2636.             NEXT
  2637.         CASE ELSE
  2638.             FOR s& = start& TO finish& - 1
  2639.                 u& = s&
  2640.                 FOR t& = s& + 1 TO finish&
  2641.                     IF CGSortLibArr(t&) > CGSortLibArr(u&) THEN
  2642.                         u& = t&
  2643.                     END IF
  2644.                 NEXT
  2645.                 IF u& <> s& THEN
  2646.                     SWAP CGSortLibArr(s&), CGSortLibArr(u&)
  2647.                 END IF
  2648.             NEXT
  2649.     END SELECT
  2650.  
  2651. '*************************
  2652. '* On repetitive arrays, SelectionSortUnstable penalizes both slow reads and writes.
  2653. '* neither stable nor unstable SelectionSort is recommended. It is not adaptive,
  2654. '* performing n(n+1)/2 operations regardless of the state of sortation.
  2655. '*************************
  2656. '* [s-][i+][n^2  ]
  2657. SUB SelectionSortUnstable (CgSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  2658.     '* these MUST match the numeric type of start and finish
  2659.     DIM SelectionSortUnstableQ AS LONG
  2660.     DIM SelectionSortUnstableR AS LONG
  2661.     '*******************************************************
  2662.  
  2663.     SELECT CASE order&
  2664.         CASE 1
  2665.             FOR SelectionSortUnstableQ = start& TO finish& - 1
  2666.                 FOR SelectionSortUnstableR = SelectionSortUnstableQ + 1 TO finish&
  2667.                     IF CgSortLibArr(SelectionSortUnstableR) < CgSortLibArr(SelectionSortUnstableQ) THEN
  2668.                         SWAP CgSortLibArr(SelectionSortUnstableR), CgSortLibArr(SelectionSortUnstableQ)
  2669.                     END IF
  2670.                 NEXT
  2671.             NEXT
  2672.         CASE ELSE
  2673.             FOR SelectionSortUnstableQ = start& TO finish& - 1
  2674.                 FOR r& = SelectionSortUnstableQ + 1 TO finish&
  2675.                     IF CgSortLibArr(SelectionSortUnstableR) > CgSortLibArr(SelectionSortUnstableQ) THEN
  2676.                         SWAP CgSortLibArr(SelectionSortUnstableR), CgSortLibArr(SelectionSortUnstableQ)
  2677.                     END IF
  2678.                 NEXT
  2679.             NEXT
  2680.     END SELECT
  2681.  
  2682. '********************
  2683. '* are writes to memory or disk time-consuming? this algorithm sorts and minimizes writes
  2684. '* complexity class: O(n^2)
  2685. '********************
  2686. SUB cycleSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2687.     length& = finish& - start&
  2688.     IF length& <= 0 THEN EXIT SUB
  2689.     DIM item AS DOUBLE '* MUST be same size and/or type as CGSortLibArr() element
  2690.     DIM position AS LONG
  2691.     '* DIM writes AS LONG
  2692.  
  2693.     ' scan CGSortLibArr() for cycles to rotate
  2694.     FOR cycleStart& = start& TO finish& - 1
  2695.         item = CGSortLibArr(cycleStart&)
  2696.         '* find where to put the item
  2697.         position& = cycleStart&
  2698.         IF order& = 1 THEN
  2699.             FOR i& = cycleStart& + 1 TO UBOUND(CGSortLibArr)
  2700.                 IF CGSortLibArr(i&) < item THEN position& = position& + 1
  2701.             NEXT
  2702.         ELSE
  2703.             FOR i& = cycleStart& + 1 TO UBOUND(CGSortLibArr)
  2704.                 IF CGSortLibArr(i&) > item THEN position& = position& + 1
  2705.             NEXT
  2706.         END IF
  2707.         '* If the item is already in its correct position, this is not a cycle
  2708.         IF position& <> cycleStart& THEN
  2709.  
  2710.             '* Otherwise, put the item there or right after any duplicates
  2711.             WHILE item = CGSortLibArr(position&)
  2712.                 position& = position& + 1
  2713.             WEND
  2714.             SWAP CGSortLibArr(position&), item
  2715.             '* writes=writes+1
  2716.  
  2717.             'rotate the rest of the cycle
  2718.             WHILE position& <> cycleStart&
  2719.                 '* Find where to put the item
  2720.                 position& = cycleStart&
  2721.                 IF order& = 1 THEN
  2722.                     FOR i& = cycleStart& + 1 TO UBOUND(CGSortLibArr)
  2723.                         IF CGSortLibArr(i&) < item THEN position& = position& + 1
  2724.                     NEXT
  2725.                 ELSE
  2726.                     FOR i& = cycleStart& + 1 TO UBOUND(CGSortLibArr)
  2727.                         IF CGSortLibArr(i&) > item THEN position& = position& + 1
  2728.                     NEXT
  2729.                 END IF
  2730.                 ' Put the item there or right after any duplicates
  2731.                 WHILE item = CGSortLibArr(position&)
  2732.                     position& = position& + 1
  2733.                 WEND
  2734.                 SWAP CGSortLibArr(position&), item
  2735.                 '* writes=writes+1
  2736.             WEND
  2737.         END IF
  2738.     NEXT
  2739.  
  2740. '**********************
  2741. '* this is dl shell's sort but modified for faster running time than standard shellsort.
  2742. '**********************
  2743. SUB shellSortMetzner (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2744.     DIM b AS DOUBLE
  2745.     SELECT CASE order&
  2746.         CASE 1
  2747.             m& = Metzner&(start&, finish&)
  2748.             WHILE m& > 0
  2749.                 FOR j& = start& TO finish& - m&
  2750.                     l& = j& + m&
  2751.                     b = CGSortLibArr(l&)
  2752.                     FOR i& = j& TO start& STEP -m&
  2753.                         IF CGSortLibArr(i&) > b THEN
  2754.                             SWAP CGSortLibArr(i& + m&), CGSortLibArr(i&)
  2755.                             l& = i&
  2756.                         ELSE
  2757.                             i& = start&
  2758.                         END IF
  2759.                     NEXT
  2760.                     CGSortLibArr(l&) = b
  2761.                 NEXT
  2762.                 m& = (m& - 1) \ 3
  2763.             WEND
  2764.         CASE ELSE
  2765.             m& = Metzner&(start&, finish&)
  2766.             WHILE m& > 0
  2767.                 FOR j& = start& TO finish& - m&
  2768.                     l& = j& + m&
  2769.                     b = CGSortLibArr(l&)
  2770.                     FOR i& = j& TO start& STEP -m&
  2771.                         IF CGSortLibArr(i&) < b THEN
  2772.                             SWAP CGSortLibArr(i& + m&), CGSortLibArr(i&)
  2773.                             l& = i&
  2774.                         ELSE
  2775.                             i& = start&
  2776.                         END IF
  2777.                     NEXT
  2778.                     CGSortLibArr(l&) = b
  2779.                 NEXT
  2780.                 m& = (m& - 1) \ 3
  2781.             WEND
  2782.     END SELECT
  2783.  
  2784. FUNCTION Metzner& (a&, b&)
  2785.     x& = (b& - a& + 1) \ 3
  2786.     s& = 0
  2787.     DO
  2788.         IF x& < 1 THEN
  2789.             EXIT DO
  2790.         ELSE
  2791.             s& = 3 * s& + 1
  2792.             x& = (x& - 1) \ 3
  2793.         END IF
  2794.     LOOP
  2795.     Metzner& = s&
  2796.  
  2797. '*********************************
  2798. '* generates the Primes() table used by PrimeGapSort()
  2799. '* PrimeGapsSort2 uses wheel factoring to find primes.
  2800. '* I guess I could have used a Sieve of Eratosthenes too
  2801. '* But trial division is fast enough.
  2802. '*********************************
  2803. SUB PrimeGen (Primes() AS LONG, MaximumN&, NPrimes&)
  2804.     STATIC NeedPrimes%
  2805.     IF NeedPrimes% OR Primes(UBOUND(primes)) ^ 2 < MaximumN& THEN
  2806.         DIM addtoskip5(0 TO 3) AS LONG
  2807.         '* used correctly, this array will eliminate all integers of the form 10k and 10k+5 when added in sequence,
  2808.         '* resulting in in integers ending in 1,3,7 or 9, saving 20% compute time versus blindly adding 2 each time.
  2809.         addtoskip5(0) = 2
  2810.         addtoskip5(1) = 4
  2811.         addtoskip5(2) = 2
  2812.         addtoskip5(3) = 2
  2813.         Primes(0) = 2
  2814.         s& = 1
  2815.         r& = 2
  2816.         p& = 0
  2817.         NPrimes& = 1
  2818.         DO
  2819.             s& = s& + addtoskip5(p&)
  2820.             p& = (p& + 1) MOD 4
  2821.             div& = 3
  2822.             r& = 1
  2823.             DO
  2824.                 IF (s& / div&) < div& THEN
  2825.                     '* this is a prime
  2826.                     IF NPrimes& > UBOUND(Primes) THEN
  2827.                         REDIM _PRESERVE Primes(0 TO NPrimes&)
  2828.                     END IF
  2829.                     Primes(NPrimes&) = s&
  2830.                     NPrimes& = NPrimes& + 1
  2831.                     EXIT DO
  2832.                 ELSE
  2833.                     IF s& MOD div& THEN
  2834.                         div& = div& + addtoskip5(r&)
  2835.                         r& = (r& + 1) MOD 4
  2836.                     ELSE
  2837.                         EXIT DO
  2838.                     END IF
  2839.                 END IF
  2840.             LOOP
  2841.         LOOP UNTIL NPrimes& > UBOUND(Primes) OR s& > MaximumN&
  2842.         ERASE addtoskip5
  2843.         REDIM _PRESERVE Primes(0 TO NPrimes& - 1) AS LONG
  2844.         NeedPrimes% = 0
  2845.     END IF
  2846.  
  2847. '************************
  2848. '* the original invention by CodeGuy.
  2849. '* competitive time to MergeSort
  2850. '************************
  2851. SUB PrimeGapSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2852.     REDIM Primes(0 TO (finish& - start& + 1) / LOG((finish& - start& + 1))) AS LONG
  2853.     PrimeGen Primes(), finish& - start& + 1, Nprimes&
  2854.     IF order& = 1 THEN
  2855.         Gap& = finish& - start&
  2856.         b& = Nprimes&
  2857.         DO
  2858.             t& = INT(727 * (Gap& / 1000))
  2859.             a& = LBOUND(primes)
  2860.             DO
  2861.                 c& = a& + (b& - a&) \ 2
  2862.                 IF Primes(c&) > t& THEN
  2863.                     b& = c& - 1
  2864.                 ELSE
  2865.                     a& = c&
  2866.                 END IF
  2867.             LOOP WHILE b& > a& + 1
  2868.             b& = c& - 1
  2869.             Gap& = Primes(c&)
  2870.             FOR s& = start& TO finish& - Gap&
  2871.                 IF CGSortLibArr(s&) > CGSortLibArr(s& + Gap&) THEN
  2872.                     SWAP CGSortLibArr(s&), CGSortLibArr(s& + Gap&)
  2873.                 END IF
  2874.             NEXT
  2875.         LOOP WHILE c& > 0
  2876.     ELSE
  2877.         Gap& = finish& - start&
  2878.         b& = Nprimes&
  2879.         DO
  2880.             t& = INT(727 * (Gap& / 1000))
  2881.             a& = LBOUND(primes)
  2882.             DO
  2883.                 c& = a& + (b& - a&) \ 2
  2884.                 IF Primes(c&) > t& THEN
  2885.                     b& = c& - 1
  2886.                 ELSE
  2887.                     a& = c&
  2888.                 END IF
  2889.             LOOP WHILE b& > a& + 1
  2890.             b& = c& - 1
  2891.             Gap& = Primes(c&)
  2892.             FOR s& = start& TO finish& - Gap&
  2893.                 IF CGSortLibArr(s&) < CGSortLibArr(s& + Gap&) THEN
  2894.                     SWAP CGSortLibArr(s&), CGSortLibArr(s& + Gap&)
  2895.                 END IF
  2896.             NEXT
  2897.         LOOP WHILE c& > 0
  2898.     END IF
  2899.     ERASE Primes
  2900.     InsertionSort CGSortLibArr(), start&, finish&, order&
  2901.  
  2902. '*****************
  2903. '* as long as a stable subsorting algorithm is used, PostSort remains stable.
  2904. '* Surprisingly as NumPostBins& increases, the speed increases.
  2905. '*****************
  2906. SUB PostSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  2907.     '* surprisngly, PostSort in this variation performs MORE slowly with increasing NumPostBins&.
  2908.     '* not certain why, but that is the result.
  2909.     DIM PSMMrec AS MinMaxRec
  2910.     GetMinMaxArray CGSortLibArr(), start&, finish&, PSMMrec
  2911.     IF CGSortLibArr(PSMMrec.min) = CGSortLibArr(PSMMrec.max) THEN EXIT SUB
  2912.     NumPostBins& = 7
  2913.     ps& = 2 * INT((finish& - start& + 1) / (NumPostBins& + 1))
  2914.     REDIM PostCGSortLibArr(0 TO NumPostBins&, 0 TO ps&) AS DOUBLE
  2915.     REDIM Counts(0 TO NumPostBins&) AS LONG
  2916.     Range# = CGSortLibArr(PSMMrec.max) - CGSortLibArr(PSMMrec.min)
  2917.     FOR s& = start& TO finish&
  2918.         Bin& = NthPlace&(CGSortLibArr(), PSMMrec, 0, NumPostBins&, order&, s&)
  2919.         IF Counts(Bin&) > UBOUND(PostCGSortLibArr, 2) THEN
  2920.             REDIM _PRESERVE PostCGSortLibArr(0 TO NumPostBins&, 0 TO Counts(Bin&)) AS DOUBLE
  2921.         END IF
  2922.         PostCGSortLibArr(Bin&, Counts(Bin&)) = CGSortLibArr(s&)
  2923.         Counts(Bin&) = Counts(Bin&) + 1
  2924.     NEXT
  2925.     TotalInserted& = start&
  2926.     FOR a& = 0 TO NumPostBins&
  2927.         IF Counts(a&) > 0 THEN
  2928.             lastinsert& = Totalnserted&
  2929.             FOR q& = 0 TO Counts(a&) - 1
  2930.                 CGSortLibArr(TotalInserted&) = PostCGSortLibArr(a&, q&)
  2931.                 TotalInserted& = TotalInserted& + 1
  2932.             NEXT
  2933.             MergeSortEmerge CGSortLibArr(), lastinsert&, TotalInserted& - 1, order&
  2934.         END IF
  2935.     NEXT
  2936.     ERASE PostCGSortLibArr
  2937.     ERASE Counts
  2938.  
  2939. '******************************************
  2940. '* I make no claims this is the fastest overall sort. In some cases, HashLisSort EASILY wins.
  2941. '* flashSort struggles with high repetition. HashListSort does not and actually performs better
  2942. '* when this is the case.
  2943. '* Yes, this is MY invention, by CodeGuy. Faster than FlashSort and relatively simple.
  2944. '* It involves an array roughly 25% bigger than the original array,
  2945. '* Yes, you read that Correctly, faster than FlashSort, even with a final InsertionSort.
  2946. '* Can also be used in place of CountingSort as it keeps track of repetitions (counts > 1).
  2947. '* 09 AUG 2017. 8388608 DOUBLE-precision elements sorted in about 10.95s (actually, a bit less),
  2948. '* versus 11.80s for FlashSort. 25% faster than FlashSort at N=16777216.
  2949. '* designed for arrays with high repetition (integer, or not) with minor, easy changes
  2950. '* to data types). HashListSort also outperforms FlashSort and DualPivotQuicksort
  2951. '* in this case, beating FlashSort by an (10-15)% margin, sometimes even higher.
  2952. '******************************************
  2953. SUB HashListSort (CGSortLibArr() AS DOUBLE, Start AS LONG, Finish AS LONG, order&)
  2954.     IF Finish - Start > 15 THEN
  2955.         DIM Mrec AS MinMaxRec
  2956.         GetMinMaxArray CGSortLibArr(), Start, Finish, Mrec
  2957.         IF CGSortLibArr(Mrec.min) = CGSortLibArr(Mrec.max) THEN
  2958.             EXIT SUB
  2959.         END IF
  2960.         DIM HLS_NInserted AS LONG
  2961.         DIM HLS_F AS LONG
  2962.         DIM HLS_S AS LONG
  2963.         DIM HLSDelta AS DOUBLE
  2964.         DIM MinValueInArray AS DOUBLE
  2965.         DIM HLSHashProbe AS LONG
  2966.         HLSDelta = CGSortLibArr(Mrec.max) - CGSortLibArr(Mrec.min)
  2967.         MinValueInArray = CGSortLibArr(Mrec.min)
  2968.         HLSHashProbe = primeNumber&(2 * INT(1.25# * (Finish - Start) / 2) - 1)
  2969.         REDIM HashTable(0 TO HLSHashProbe) AS DOUBLE
  2970.         REDIM Count(0 TO HLSHashProbe) AS LONG
  2971.         FOR HLS_S = Start TO Finish
  2972.             HLS_F = INT(HLSHashProbe * (CGSortLibArr(HLS_S) - MinValueInArray) / HLSDelta)
  2973.             DO
  2974.                 IF HLS_F > HLSHashProbe THEN
  2975.                     HLS_F = HLS_F - HLSHashProbe
  2976.                 END IF
  2977.                 IF HLS_F < 0 THEN
  2978.                     HLS_F = HLS_F + HLSHashProbe
  2979.                 END IF
  2980.                 IF HashTable(HLS_F) = CGSortLibArr(HLS_S) THEN
  2981.                     Count(HLS_F) = Count(HLS_F) + 1
  2982.                     EXIT DO
  2983.                 ELSE
  2984.                     IF Count(HLS_F) = 0 THEN
  2985.                         HashTable(HLS_F) = CGSortLibArr(HLS_S)
  2986.                         Count(HLS_F) = 1
  2987.                         EXIT DO
  2988.                     END IF
  2989.                 END IF
  2990.                 HLS_F = HLS_F + 1
  2991.             LOOP
  2992.         NEXT
  2993.         HLS_NInserted = Start
  2994.         IF order& = 1 THEN
  2995.             FOR HLS_S = 0 TO HLSHashProbe
  2996.                 WHILE Count(HLS_S) > 0
  2997.                     CGSortLibArr(HLS_NInserted) = HashTable(HLS_S)
  2998.                     HLS_NInserted = HLS_NInserted + 1
  2999.                     Count(HLS_S) = Count(HLS_S) - 1
  3000.                 WEND
  3001.             NEXT
  3002.         ELSE
  3003.             FOR HLS_S = HLSHashProbe TO 0 STEP -1
  3004.                 WHILE Count(HLS_S) > 0
  3005.                     CGSortLibArr(HLS_NInserted) = HashTable(HLS_S)
  3006.                     HLS_NInserted = HLS_NInserted + 1
  3007.                     Count(HLS_S) = Count(HLS_S) - 1
  3008.                 WEND
  3009.             NEXT
  3010.         END IF
  3011.         ERASE Count, HashTable
  3012.     END IF
  3013.  
  3014.     '* use when you KNOW the data is narrow range
  3015.     '* BubbleSortModified CGSortLibArr(), Start, Finish, order&
  3016.  
  3017.     '* otherwise, this one is plenty fast for general purpose.
  3018.     'InsertionSortBinary CGSortLibArr(), Start, Finish, order&
  3019.  
  3020.     '* InsertionSort wins.
  3021.     InsertionSort CGSortLibArr(), Start, Finish, order&
  3022.  
  3023. '*****************
  3024. '* It is rumored RadixSort is fast. In some cases, yes. BUT it is stable and for integer-domain numbers, it is quite suitable. It requires auxiliary
  3025. '* storage, so it is not an in-place algorithm.
  3026. '*****************
  3027. SUB RadixSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  3028.     ArrayIsInteger CGSortLibArr(), start&, finish&, errindex&, errcon&
  3029.     IF errcon& THEN
  3030.         '* use another stable sort and sort anyway
  3031.         MergeSortEmerge CGSortLibArr(), start&, finish&, order&
  3032.     ELSE
  3033.         DIM RSMMrec AS MinMaxRec
  3034.         GetMinMaxArray CGSortLibArr(), start&, finish&, RSMMrec
  3035.         IF CGSortLibArr(RSMMrec.min) = CGSortLibArr(RSMMrec.max) THEN EXIT SUB '* no div0 bombs
  3036.         delta# = CGSortLibArr(RSMMrec.max) - CGSortLibArr(RSMMrec.min)
  3037.         DIM pow2 AS _UNSIGNED _INTEGER64
  3038.         DIM NtmpN AS _UNSIGNED _INTEGER64
  3039.         DIM Int64MaxShift AS _INTEGER64: Int64MaxShift = 2 ^ 64
  3040.         REDIM ct&(0 TO 1)
  3041.         REDIM RadixCGSortLibArr(0 TO 1, finish& - start&) AS DOUBLE
  3042.         SELECT CASE order&
  3043.             CASE 1
  3044.                 pow2 = Int64MaxShift
  3045.                 bits& = LEN(Int64MaxShift) * 8
  3046.                 DO UNTIL bits& < 0
  3047.                     FOR i& = start& TO finish&
  3048.                         NtmpN = Int64MaxShift * (CGSortLibArr(i&) - CGSortLibArr(RSMMrec.min)) / (delta#)
  3049.                         IF NtmpN AND pow2 THEN
  3050.                             tmpradix% = 1
  3051.                         ELSE
  3052.                             tmpradix% = 0
  3053.                         END IF
  3054.                         RadixCGSortLibArr(tmpradix%, ct&(tmpradix%)) = CGSortLibArr(i&)
  3055.                         ct&(tmpradix%) = ct&(tmpradix%) + 1
  3056.                     NEXT
  3057.                     c& = start&
  3058.                     FOR i& = 0 TO 1
  3059.                         FOR j& = 0 TO ct&(i&) - 1
  3060.                             CGSortLibArr(c&) = RadixCGSortLibArr(i&, j&)
  3061.                             c& = c& + 1
  3062.                         NEXT
  3063.                         ct&(i&) = 0
  3064.                     NEXT
  3065.                     pow2 = pow2 / 2
  3066.                     bits& = bits& - 1
  3067.                 LOOP
  3068.             CASE ELSE
  3069.                 pow2 = 1
  3070.                 FOR bits& = 0 TO 63
  3071.                     FOR i& = start& TO finish&
  3072.                         NtmpN = Int64MaxShift * (CGSortLibArr(i&) - CGSortLibArr(RSMMrec.min)) / (delta#)
  3073.                         IF NtmpN AND pow2 THEN
  3074.                             tmpradix% = 1
  3075.                         ELSE
  3076.                             tmpradix% = 0
  3077.                         END IF
  3078.                         RadixCGSortLibArr(tmpradix%, ct&(tmpradix%)) = CGSortLibArr(i&)
  3079.                         ct&(tmpradix%) = ct&(tmpradix%) + 1
  3080.                     NEXT
  3081.                     c& = start&
  3082.                     FOR i& = 0 TO 1
  3083.                         FOR j& = 0 TO ct&(i&) - 1
  3084.                             CGSortLibArr(c&) = RadixCGSortLibArr(i&, j&)
  3085.                             c& = c& + 1
  3086.                         NEXT
  3087.                         ct&(i&) = 0
  3088.                     NEXT
  3089.                     pow2 = pow2 * 2
  3090.                 NEXT
  3091.         END SELECT
  3092.         ERASE RadixCGSortLibArr, ct&
  3093.     END IF
  3094.  
  3095. '*****************
  3096. '* Used by RadixSort, which requires integer-domain arrays to function properly
  3097. '*****************
  3098. SUB ArrayIsInteger (CGSortLibArr() AS DOUBLE, start&, finish&, errorindex&, IsInt&)
  3099.     IsInt& = 1
  3100.     errorindex& = start&
  3101.     FOR IsIntegerS& = start& TO finish&
  3102.         IF CGSortLibArr(IsIntegerS&) MOD 1 THEN
  3103.             errorindex& = IsIntegerS&
  3104.             IsInt& = 0
  3105.             EXIT FUNCTION
  3106.         END IF
  3107.     NEXT
  3108.  
  3109. '*****************
  3110. SUB BatcherOddEvenMergeSort (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)
  3111.     IF (Finish& > 1) THEN
  3112.         m& = (Finish& + (Finish& MOD 2)) \ 2
  3113.         BatcherOddEvenMergeSort CGSortLibArr(), Start&, m&, order&
  3114.         BatcherOddEvenMergeSort CGSortLibArr(), Start& + m&, m&, order&
  3115.         BatcheroddEvenMerge CGSortLibArr(), Start&, Finish&, 1, order&
  3116.     END IF
  3117.  
  3118. SUB BatcheroddEvenMerge (CGSortLibArr() AS DOUBLE, Start&, Finish&, r&, order&)
  3119.     m& = r& * 2
  3120.     IF (m& < Finish&) AND m& > 0 THEN
  3121.         BatcheroddEvenMerge CGSortLibArr(), Start&, Finish&, m&, order&
  3122.         BatcheroddEvenMerge CGSortLibArr(), Start& + r&, Finish&, m&, order&
  3123.         i& = Start& + r&
  3124.         DO
  3125.             IF i& + m& > Start& + Finish& THEN
  3126.                 EXIT DO
  3127.             ELSE
  3128.                 IF order& = 1 THEN
  3129.                     IF CGSortLibArr(i&) > CGSortLibArr(i& + r&) THEN
  3130.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + r&)
  3131.                     END IF
  3132.                 ELSE
  3133.                     IF CGSortLibArr(i&) < CGSortLibArr(i& + r&) THEN
  3134.                         SWAP CGSortLibArr(i&), CGSortLibArr(i& + r&)
  3135.                     END IF
  3136.                 END IF
  3137.                 i& = i& + m&
  3138.             END IF
  3139.         LOOP
  3140.     ELSE
  3141.         IF order& = 1 THEN
  3142.             IF CGSortLibArr(Start&) > CGSortLibArr(Start& + r&) THEN
  3143.                 SWAP CGSortLibArr(Start&), CGSortLibArr(Start& + r&)
  3144.             END IF
  3145.         ELSE
  3146.             IF CGSortLibArr(Start&) < CGSortLibArr(Start& + r&) THEN
  3147.                 SWAP CGSortLibArr(Start&), CGSortLibArr(Start& + r&)
  3148.             END IF
  3149.         END IF
  3150.     END IF
  3151.  
  3152. SUB SinglePassShellSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  3153.     Gap& = (finish& - start&)
  3154.     DO
  3155.         SELECT CASE order&
  3156.             CASE 1
  3157.                 FOR c& = start& TO finish& - Gap&
  3158.                     IF CGSortLibArr(c&) > CGSortLibArr(c& + Gap&) THEN
  3159.                         SWAP CGSortLibArr(c&), CGSortLibArr(c& + Gap&)
  3160.                     END IF
  3161.                 NEXT
  3162.             CASE ELSE
  3163.                 FOR c& = start& TO finish& - Gap&
  3164.                     IF CGSortLibArr(c&) < CGSortLibArr(c& + Gap&) THEN
  3165.                         SWAP CGSortLibArr(c&), CGSortLibArr(c& + Gap&)
  3166.                     END IF
  3167.                 NEXT
  3168.         END SELECT
  3169.         Gap& = INT(Gap& / 1.247#)
  3170.     LOOP UNTIL Gap& < 1
  3171.     InsertionSort CGSortLibArr(), start&, finish&, order&
  3172.  
  3173. '*********************
  3174. '* Another one of Kenneth Batcher's cool parallel sorting algorithms, also O(NLogN) classification complexity. I think the actual complexity
  3175. '* involves more Logs and such. Batcher Odd-Even MergeSort is also part of the parallel processing arsenal found on GPU-assisted parallel
  3176. '* processing algorithms. Donald Knuth speaks highly of it and correctly claims it can sort more items than there are on all the world's
  3177. '* computers. Corrected to use dir& = 1 for ascending
  3178. '*********************
  3179. SUB BitonicSort (CGSortLibArr() AS DOUBLE, lo&, n&, dir&)
  3180.     IF (n& > 1) THEN
  3181.         m& = n& \ 2
  3182.         IF dir& = -1 THEN
  3183.             BitonicSort CGSortLibArr(), lo&, m&, 1
  3184.         ELSE
  3185.             BitonicSort CGSortLibArr(), lo&, m&, -1
  3186.         END IF
  3187.         BitonicSort CGSortLibArr(), lo& + m&, n& - m&, dir&
  3188.         BitonicMerge CGSortLibArr(), lo&, n&, dir&
  3189.     END IF
  3190.  
  3191. SUB BitonicMerge (CGSortLibArr() AS DOUBLE, lo&, n&, dir&)
  3192.     IF (n& > 1) THEN
  3193.         m& = greatestPowerOfTwoLessThan&(n&)
  3194.         FOR i& = lo& TO lo& + n& - m&
  3195.             BitonicMergeCompare CGSortLibArr(), i&, i& + m&, dir&
  3196.         NEXT
  3197.         BitonicMerge CGSortLibArr(), lo&, m&, dir&
  3198.         BitonicMerge CGSortLibArr(), lo& + m&, n& - m&, dir&
  3199.     END IF
  3200.  
  3201. SUB BitonicMergeCompare (CGSortLibArr() AS DOUBLE, i&, j&, dir&)
  3202.     IF (dir& = SGN(CGSortLibArr(i&) - CGSortLibArr(j&))) THEN
  3203.         SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  3204.     END IF
  3205.  
  3206. FUNCTION greatestPowerOfTwoLessThan& (n&)
  3207.     k& = 1
  3208.     WHILE (k& < n&)
  3209.         k& = k& * 2
  3210.     WEND
  3211.     greatestPowerOfTwoLessThan& = k& / 2
  3212.  
  3213. '***********************
  3214. '* Kth order statistic for CGSortLibArr()
  3215. '* this algorithm also modifies the passed array
  3216. '**********************
  3217. SUB QuickSelectRecursive (CGSortLibArr() AS DOUBLE, start&, finish&, statistic&)
  3218.     DIM PivotIndex AS LONG
  3219.     PivotIndex = QSelectPartitionArray&(CGSortLibArr(), start&, finish&)
  3220.     SELECT CASE PivotIndex&
  3221.         CASE IS < statistic&
  3222.             QuickSelectRecursive CGSortLibArr(), PivotIndex&, finish&, statistic&
  3223.         CASE IS > statistic&
  3224.             QuickSelectRecursive CGSortLibArr(), start&, PivotIndex&, statistic&
  3225.         CASE ELSE
  3226.             EXIT SUB
  3227.     END SELECT
  3228.  
  3229. FUNCTION QSelectPartitionArray& (CGSortLibArr() AS DOUBLE, start&, finish&)
  3230.     '* this declaration of pivot MUST be the same type as CGSortLibArr()
  3231.     DIM pivot AS DOUBLE
  3232.     pivotIndex& = start& + RND * (finish& - start&)
  3233.     pivot = CGSortLibArr(pivotIndex&)
  3234.     '* and a familiar shuffle routine reminiscent of QuickSort
  3235.     SWAP CGSortLibArr(pivotIndex&), CGSortLibArr(finish&)
  3236.     pivotIndex& = start&
  3237.     FOR i& = start& TO finish&
  3238.         IF CGSortLibArr(i&) < pivot THEN
  3239.             SWAP CGSortLibArr(i&), CGSortLibArr(pivotIndex&)
  3240.             pivotIndex& = pivotIndex& + 1
  3241.         END IF
  3242.     NEXT
  3243.     SWAP CGSortLibArr(pivotIndex&), CGSortLibArr(finish&)
  3244.     QSelectPartitionArray& = pivotIndex&
  3245.  
  3246. SUB QuickselectIterative (CGSortLibArr() AS DOUBLE, start&, finish&, k&)
  3247.     LStart& = start&
  3248.     LFinish& = finish&
  3249.     DIM pivotindex AS LONG
  3250.     pivotindex = QSelectPartitionArray&(CGSortLibArr(), LStart&, LFinish&)
  3251.     WHILE (pivotindex <> k&)
  3252.         pivotindex& = QSelectPartitionArray&(CGSortLibArr(), LStart&, LFinish&)
  3253.         IF (pivotindex& < k&) THEN
  3254.             LStart& = pivotindex
  3255.         ELSEIF (pivotindex > kK) THEN
  3256.             LFinish& = pivotindex
  3257.         END IF
  3258.     WEND
  3259.  
  3260. '* adapted for use with qb64. This method is roughly 20 percent more efficient than the standard vector scan algorithm for min/max
  3261. '* Roughly 6.19s versus 7.93 for n=134217728 (0 counts as 1, of course)
  3262. '* This may be a nice addition for perhaps _CGArrayMax() in qb64. Of course, I am not so vain as to insist about the CG part.
  3263. '* simply meant as a faster tool for a common array problem to be solved. Also adaptable to string types.
  3264. '* returns indexes instead of TYPE structure (not used in this library, but pretty much same as GetminMaxCGSortLibArr().
  3265. SUB ArrayGetMinMax (CGSortLibArr() AS DOUBLE, start&, finish&, ArrayMinIndex&, ArrayMaxIndex&)
  3266.     n& = finish& - start&
  3267.  
  3268.     IF (n& MOD 2) THEN
  3269.         ArrayMinIndex& = start&
  3270.         ArrayMaxIndex& = start&
  3271.         i& = 1
  3272.     ELSE
  3273.         IF CGSortLibArr(start&) > CGSortLibArr(finish&) THEN
  3274.             ArrayMaxIndex& = start&
  3275.             ArrayMinIndex& = start& + 1
  3276.         ELSE
  3277.             ArrayMinIndex& = start&
  3278.             ArrayMaxIndex& = start& + 1
  3279.         END IF
  3280.         i& = 2
  3281.     END IF
  3282.  
  3283.     WHILE (i& < finish&)
  3284.         IF (CGSortLibArr(i&) > CGSortLibArr(i& + 1)) THEN
  3285.             IF CGSortLibArr(i&) > CGSortLibArr(ArrayMaxIndex&) THEN
  3286.                 ArrayMaxIndex& = i&
  3287.             END IF
  3288.             IF CGSortLibArr(i& + 1) < CGSortLibArr(ArrayMinIndex&) THEN
  3289.                 ArrayMinIndex& = i& + 1
  3290.             END IF
  3291.         ELSE
  3292.             IF CGSortLibArr(i& + 1) > CGSortLibArr(ArrayMaxIndex&) THEN
  3293.                 ArrayMaxIndex& = i& + 1
  3294.             END IF
  3295.             IF CGSortLibArr(i&) < CGSortLibArr(ArrayMinIndex&) THEN
  3296.                 ArrayMinIndex& = i&
  3297.             END IF
  3298.         END IF
  3299.         i& = i& + 2
  3300.     WEND
  3301.  
  3302. '******************
  3303. '* yields the pointer to an array element whose frequency of occurrence is greatest
  3304. '******************
  3305. FUNCTION Mode& (CGSortLibArr() AS DOUBLE, start&, finish&, frequency&)
  3306.     FlashSort CGSortLibArr(), start&, finish&, 1
  3307.     m& = 0
  3308.     frequency& = 0
  3309.     S& = start&
  3310.     DO
  3311.         R& = S&
  3312.         q& = R&
  3313.         DO
  3314.             IF R& < finish& THEN
  3315.                 S& = S& + 1
  3316.                 IF CGSortLibArr(R&) = CGSortLibArr(S&) THEN
  3317.                 ELSE
  3318.  
  3319.                     EXIT DO
  3320.                 END IF
  3321.             ELSE
  3322.                 EXIT DO
  3323.             END IF
  3324.         LOOP
  3325.         IF q& - R& > m& THEN
  3326.             m& = q& - R&
  3327.             modetemp& = R&
  3328.         END IF
  3329.     LOOP
  3330.     frequency& = m& + 1
  3331.     Mode& = modetemp&
  3332.  
  3333. FUNCTION ArrayMedian# (CGSortLibArr() AS DOUBLE, start&, finish&)
  3334.     FlashSort CGSortLibArr(), start&, finish&, 1
  3335.     IF (finish& - start&) MOD 2 THEN
  3336.         '* There's an even number of elements in this subset -- think about it
  3337.         '* then then median is calculated by the average of these 2 elements
  3338.         p0& = IndexCenter&(start&, finish&)
  3339.         p1& = p& + 1
  3340.         ArrayMedian# = (CGSortLibArr(p0&) + CGSortLibArr(p1&)) / 2
  3341.     ELSE
  3342.         '* there's an odd number of elements in this subset, so the ArrayMedian is the start+(finish-start-1)/2 element
  3343.         ArrayMedian# = CGSortLibArr(IndexCenter&(start&, finish&))
  3344.     END IF
  3345.  
  3346. FUNCTION IndexCenter& (start&, finish&)
  3347.     IF start& <> finish& THEN
  3348.         t& = (finish& - start&) MOD 2
  3349.         IF t& MOD 2 THEN
  3350.             '* if it's even, such as 1,5
  3351.             '* it will be calculated as start&+(finish&-start&)/2
  3352.             IndexCenter& = start& + (finish& - start&) / 2
  3353.         ELSE
  3354.             '* otherwise, it will be calulated as start&+(finish&-start&-1)/2
  3355.             IndexCenter& = start& + (finish& - start& - 1) / 2
  3356.         END IF
  3357.     ELSE
  3358.         IndexCenter& = start&
  3359.     END IF
  3360.  
  3361. SUB SnakeSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  3362.     '* these MUST be the same type as start& and finish&
  3363.     '***************************
  3364.     DIM i AS LONG
  3365.     DIM L_MinInt AS LONG
  3366.     DIM L_MaxInt AS LONG
  3367.     DIM L_Index AS LONG
  3368.     DIM L_Level AS LONG
  3369.     DIM L_OldLevel AS LONG
  3370.     DIM L_NewLevel AS LONG
  3371.     DIM L_Direction AS LONG
  3372.     '***************************
  3373.     '*
  3374.     DIM blnMirror AS INTEGER
  3375.     '* these MUST be the same type as the array elements being sorted
  3376.     '****************************
  3377.     DIM varSwap AS DOUBLE
  3378.     DIM ArrayAuxiliary AS DOUBLE
  3379.     '****************************
  3380.     L_MinInt = start&
  3381.     L_MaxInt = finish&
  3382.     REDIM L_Index((L_MaxInt - L_MinInt + 3) \ 2)
  3383.     L_Index(0) = L_MinInt
  3384.     i = L_MinInt
  3385.     ' Initial loop: locate cutoffs for each ordered section
  3386.     DO UNTIL i >= L_MaxInt
  3387.         SELECT CASE L_Direction
  3388.             CASE 1
  3389.                 DO UNTIL i = L_MaxInt
  3390.                     IF CGSortLibArr(i) > CGSortLibArr(i + 1) THEN EXIT DO
  3391.                     i = i + 1
  3392.                 LOOP
  3393.             CASE -1
  3394.                 DO UNTIL i = L_MaxInt
  3395.                     IF CGSortLibArr(i) < CGSortLibArr(i + 1) THEN EXIT DO
  3396.                     i = i + 1
  3397.                 LOOP
  3398.             CASE ELSE
  3399.                 DO UNTIL i = L_MaxInt
  3400.                     IF CGSortLibArr(i) <> CGSortLibArr(i + 1) THEN EXIT DO
  3401.                     i = i + 1
  3402.                 LOOP
  3403.                 IF i = L_MaxInt THEN L_Direction = 1
  3404.         END SELECT
  3405.         IF L_Direction = 0 THEN
  3406.             IF CGSortLibArr(i) > CGSortLibArr(i + 1) THEN
  3407.                 L_Direction = -1
  3408.             ELSE
  3409.                 L_Direction = 1
  3410.             END IF
  3411.         ELSE
  3412.             L_Level = L_Level + 1
  3413.             L_Index(L_Level) = i * L_Direction
  3414.             L_Direction = 0
  3415.         END IF
  3416.         i = i + 1
  3417.     LOOP
  3418.     IF ABS(L_Index(L_Level)) < L_MaxInt THEN
  3419.         IF L_Direction = 0 THEN L_Direction = 1
  3420.         L_Level = L_Level + 1
  3421.         L_Index(L_Level) = i * L_Direction
  3422.     END IF
  3423.     ' If the list is already sorted, exit
  3424.     IF L_Level <= 1 THEN
  3425.         ' If sorted descending, reverse before exiting
  3426.         IF L_Index(L_Level) < 0 THEN
  3427.             FOR i = 0 TO (L_MaxInt - L_MinInt) \ 2
  3428.                 SWAP CGSortLibArr(L_MinInt + i), CGSortLibArr(L_MaxInt - i)
  3429.                 '* varSwap = CGSortLibArr(L_MinInt + i)
  3430.                 '* CGSortLibArr(L_MinInt + i) = CGSortLibArr(L_MaxInt - i)
  3431.                 '* CGSortLibArr(L_MaxInt - i) = varSwap
  3432.             NEXT
  3433.         END IF
  3434.         EXIT SUB
  3435.     END IF
  3436.     '* Main loop - merge section pairs together until only one section left
  3437.     REDIM ArrayAuxiliary(L_MinInt TO L_MaxInt) AS DOUBLE '* must be same type as CGSortLibArr()
  3438.     DO UNTIL L_Level = 1
  3439.         L_OldLevel = L_Level
  3440.         FOR L_Level = 1 TO L_Level - 1 STEP 2
  3441.             IF blnMirror THEN
  3442.                 SnakeSortMerge ArrayAuxiliary(), L_Index(L_Level - 1), L_Index(L_Level), L_Index(L_Level + 1), CGSortLibArr(), order&
  3443.             ELSE
  3444.                 SnakeSortMerge CGSortLibArr(), L_Index(L_Level - 1), L_Index(L_Level), L_Index(L_Level + 1), ArrayAuxiliary(), order&
  3445.             END IF
  3446.             L_NewLevel = L_NewLevel + 1
  3447.             L_Index(L_NewLevel) = ABS(L_Index(L_Level + 1))
  3448.         NEXT
  3449.         IF L_OldLevel MOD 2 = 1 THEN
  3450.             IF blnMirror THEN
  3451.                 FOR i = L_Index(L_NewLevel) + 1 TO L_MaxInt
  3452.                     CGSortLibArr(i) = ArrayAuxiliary(i)
  3453.                 NEXT
  3454.             ELSE
  3455.                 FOR i = L_Index(L_NewLevel) + 1 TO L_MaxInt
  3456.                     ArrayAuxiliary(i) = CGSortLibArr(i)
  3457.                 NEXT
  3458.             END IF
  3459.             L_NewLevel = L_NewLevel + 1
  3460.             L_Index(L_NewLevel) = L_Index(L_OldLevel)
  3461.         END IF
  3462.         L_Level = L_NewLevel
  3463.         L_NewLevel = 0
  3464.         blnMirror = NOT blnMirror
  3465.     LOOP
  3466.  
  3467.     '* Copy ArrayAuxiliary to CGSortLibArr() if necessary
  3468.     IF blnMirror THEN
  3469.         IF order& = 1 THEN
  3470.             FOR i = L_MinInt TO L_MaxInt
  3471.                 CGSortLibArr(i) = ArrayAuxiliary(i)
  3472.             NEXT
  3473.         ELSE
  3474.             FOR i = L_MaxInt TO L_MinInt STEP -1
  3475.                 CGSortLibArr(i) = ArrayAuxiliary(i)
  3476.             NEXT
  3477.         END IF
  3478.         ERASE ArrayAuxiliary
  3479.         EXIT SUB
  3480.     ELSE
  3481.         IF order& = 1 THEN
  3482.             EXIT SUB
  3483.         ELSE
  3484.             WHILE L_MinInt < L_MaxInt
  3485.                 SWAP CGSortLibArr(L_MinInt), CGSortLibArr(L_MaxInt)
  3486.                 L_MinInt = L_MinInt + 1
  3487.                 L_MaxInt = L_MaxInt - 1
  3488.             WEND
  3489.         END IF
  3490.     END IF
  3491.  
  3492. SUB SnakeSortMerge (ArraySource() AS DOUBLE, pL_Left AS LONG, pL_Mid AS LONG, pL_Right AS LONG, ArrayAuxiliary() AS DOUBLE, order&)
  3493.     DIM L_LeftPtr AS LONG
  3494.     DIM L_LMin AS LONG
  3495.     DIM LMax AS LONG
  3496.     DIM LStep AS LONG
  3497.  
  3498.     DIM L_RightPtr AS LONG
  3499.     DIM L_RMin AS LONG
  3500.     DIM RMax AS LONG
  3501.     DIM RStep AS LONG
  3502.  
  3503.     DIM OutCount AS LONG: OutCount = 0 '* Do not assume OutCount is set to 0
  3504.     IF pL_Left <> 0 THEN OutCount = ABS(pL_Left) + 1
  3505.     IF pL_Mid > 0 THEN
  3506.         L_LMin = OutCount
  3507.         LMax = ABS(pL_Mid)
  3508.         LStep = 1
  3509.     ELSE
  3510.         L_LMin = ABS(pL_Mid)
  3511.         LMax = OutCount
  3512.         LStep = -1
  3513.     END IF
  3514.     IF pL_Right > 0 THEN
  3515.         L_RMin = ABS(pL_Mid) + 1
  3516.         RMax = ABS(pL_Right)
  3517.         RStep = 1
  3518.     ELSE
  3519.         L_RMin = ABS(pL_Right)
  3520.         RMax = ABS(pL_Mid) + 1
  3521.         RStep = -1
  3522.     END IF
  3523.     L_LeftPtr = L_LMin
  3524.     L_RightPtr = L_RMin
  3525.  
  3526.     DO
  3527.         IF L_LeftPtr < pL_Left OR L_LeftPtr >= LMax THEN
  3528.             EXIT DO
  3529.         END IF
  3530.         IF L_RightPtr > pL_Right OR L_RightPtr >= RMax THEN
  3531.             EXIT DO
  3532.         END IF
  3533.  
  3534.         IF ArraySource(L_LeftPtr) <= ArraySource(L_RightPtr) THEN
  3535.             ArrayAuxiliary(OutCount) = ArraySource(L_LeftPtr)
  3536.             IF L_LeftPtr = LMax THEN
  3537.                 FOR L_RightPtr = L_RightPtr TO RMax STEP RStep
  3538.                     OutCount = OutCount + 1
  3539.                     ArrayAuxiliary(OutCount) = ArraySource(L_RightPtr)
  3540.                 NEXT
  3541.                 EXIT DO
  3542.             END IF
  3543.             L_LeftPtr = L_LeftPtr + LStep
  3544.         ELSE
  3545.             ArrayAuxiliary(OutCount) = ArraySource(L_RightPtr)
  3546.             IF L_RightPtr = RMax THEN
  3547.                 FOR L_LeftPtr = L_LeftPtr TO LMax STEP LStep
  3548.                     OutCount = OutCount + 1
  3549.                     ArrayAuxiliary(OutCount) = ArraySource(L_LeftPtr)
  3550.                 NEXT
  3551.                 EXIT DO
  3552.             END IF
  3553.             L_RightPtr = L_RightPtr + RStep
  3554.         END IF
  3555.         OutCount = OutCount + 1
  3556.     LOOP
  3557.  
  3558. '******************************
  3559. '* from: http://www.vbforums.com/attachment.php?attachmentid=64242&d=1211306594
  3560. '* not really fast, but included because it works reasonably.
  3561. '******************************
  3562. SUB JoinSort (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  3563.     DIM i AS LONG
  3564.     DIM L_MinInt AS LONG
  3565.     DIM L_MaxInt AS LONG
  3566.     DIM j AS LONG
  3567.     DIM jFirst AS LONG
  3568.     DIM jLast AS LONG
  3569.     DIM JStep AS LONG
  3570.     DIM k AS LONG
  3571.     DIM kFirst AS LONG
  3572.     DIM kLast AS LONG
  3573.     DIM kStep AS LONG
  3574.     DIM O AS LONG
  3575.     DIM L_Swap AS LONG
  3576.     DIM L_Left AS LONG
  3577.     DIM L_Right AS LONG
  3578.     DIM ArrayAuxiliary AS DOUBLE
  3579.     DIM varSwap AS DOUBLE
  3580.  
  3581.     L_MinInt = start&
  3582.     L_MaxInt = finish&
  3583.     REDIM ArrayAuxiliary(L_MinInt TO L_MaxInt)
  3584.     DO
  3585.         FOR i = L_MinInt TO L_MaxInt
  3586.             jFirst = i
  3587.             JStep = 0
  3588.             FOR jLast = i TO L_MaxInt - 1
  3589.                 IF CGSortLibArr(jLast) < CGSortLibArr(jLast + 1) THEN
  3590.                     IF JStep = -1 THEN EXIT FOR ELSE JStep = 1
  3591.                 ELSEIF CGSortLibArr(jLast) > CGSortLibArr(jLast + 1) THEN
  3592.                     IF JStep = 1 THEN EXIT FOR ELSE JStep = -1
  3593.                 END IF
  3594.             NEXT
  3595.             L_Left = jFirst
  3596.             kFirst = jLast + 1
  3597.             IF jLast = L_MaxInt THEN
  3598.                 IF JStep = -1 THEN
  3599.                     FOR j = 0 TO (jLast - jFirst) \ 2
  3600.                         SWAP CGSortLibArr(jFirst + j), CGSortLibArr(jLast - j)
  3601.                         '* varSwap = CGSortLibArr(jFirst + j)
  3602.                         '* CGSortLibArr(jFirst + j) = CGSortLibArr(jLast - j)
  3603.                         '* CGSortLibArr(jLast - j) = varSwap
  3604.                     NEXT
  3605.                 END IF
  3606.                 L_Right = jLast
  3607.                 EXIT FOR
  3608.             END IF
  3609.             SELECT CASE JStep
  3610.                 CASE -1
  3611.                     L_Swap = jFirst
  3612.                     jFirst = jLast
  3613.                     jLast = L_Swap
  3614.                 CASE 0
  3615.                     JStep = 1
  3616.             END SELECT
  3617.             kStep = 0
  3618.             FOR kLast = kFirst TO L_MaxInt - 1
  3619.                 IF CGSortLibArr(kLast) < CGSortLibArr(kLast + 1) THEN
  3620.                     IF kStep = -1 THEN EXIT FOR ELSE kStep = 1
  3621.                 ELSEIF CGSortLibArr(kLast) > CGSortLibArr(kLast + 1) THEN
  3622.                     IF kStep = 1 THEN EXIT FOR ELSE kStep = -1
  3623.                 END IF
  3624.             NEXT
  3625.             L_Right = kLast
  3626.             SELECT CASE kStep
  3627.                 CASE -1
  3628.                     L_Swap = kFirst
  3629.                     kFirst = kLast
  3630.                     kLast = L_Swap
  3631.                 CASE 0
  3632.                     kStep = 1
  3633.             END SELECT
  3634.             O = L_Left
  3635.             j = jFirst
  3636.             k = kFirst
  3637.             DO
  3638.                 IF CGSortLibArr(j) < CGSortLibArr(k) THEN
  3639.                     ArrayAuxiliary(O) = CGSortLibArr(j)
  3640.                     IF j = jLast THEN
  3641.                         FOR k = k TO kLast STEP kStep
  3642.                             O = O + 1
  3643.                             ArrayAuxiliary(O) = CGSortLibArr(k)
  3644.                         NEXT
  3645.                         EXIT DO
  3646.                     END IF
  3647.                     j = j + JStep
  3648.                 ELSE
  3649.                     ArrayAuxiliary(O) = CGSortLibArr(k)
  3650.                     IF k = kLast THEN
  3651.                         FOR j = j TO jLast STEP JStep
  3652.                             O = O + 1
  3653.                             ArrayAuxiliary(O) = CGSortLibArr(j)
  3654.                         NEXT
  3655.                         EXIT DO
  3656.                     END IF
  3657.                     k = k + kStep
  3658.                 END IF
  3659.                 O = O + 1
  3660.             LOOP
  3661.             FOR O = L_Left TO L_Right
  3662.                 CGSortLibArr(O) = ArrayAuxiliary(O)
  3663.             NEXT
  3664.             i = L_Right
  3665.         NEXT
  3666.     LOOP UNTIL L_Left = L_MinInt AND L_Right = L_MaxInt
  3667.     ERASE ArrayAuxiliary
  3668.     IF order& = -1 THEN
  3669.         L_MinInt = start&
  3670.         L_MaxInt = finish&
  3671.         WHILE L_MinInt < L_MaxInt
  3672.             SWAP CGSortLibArr(L_MinInt), CGSortLibArr(L_MaxInt)
  3673.             L_MinInt = L_MinInt + 1
  3674.             L_MaxInt = L_MaxInt - 1
  3675.         WEND
  3676.     END IF
  3677.  
  3678. SUB QSortRecursiveSimplified (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  3679.     IF (start& >= finish&) THEN EXIT SUB
  3680.     DIM PartitionVal AS DOUBLE
  3681.     DIM QSRi AS LONG
  3682.     DIM QSRj AS LONG
  3683.     QSRi = start&
  3684.     QSRj = finish&
  3685.     PartitionVal = CGSortLibArr(start& + INT(RND * (finish& - start&)))
  3686.     SELECT CASE order&
  3687.         CASE 1
  3688.             DO
  3689.                 WHILE (CGSortLibArr(QSRi) < PartitionVal)
  3690.                     QSRi = QSRi + 1
  3691.                 WEND
  3692.  
  3693.                 WHILE (CGSortLibArr(QSRj) > PartitionVal)
  3694.                     QSRj = QSRj - 1
  3695.                 WEND
  3696.  
  3697.                 IF QSRi <= QSRj THEN
  3698.                     SWAP CGSortLibArr(QSRi), CGSortLibArr(QSRj)
  3699.                     QSRi = QSRi + 1
  3700.                     QSRj = QSRj - 1
  3701.                 END IF
  3702.             LOOP UNTIL QSRi > QSRj
  3703.         CASE ELSE
  3704.             DO
  3705.                 WHILE (CGSortLibArr(QSRi) > PartitionVal)
  3706.                     QSRi = QSRi + 1
  3707.                 WEND
  3708.  
  3709.                 WHILE (CGSortLibArr(QSRj) < PartitionVal)
  3710.                     QSRj = QSRj - 1
  3711.                 WEND
  3712.  
  3713.                 IF QSRi <= QSRj THEN
  3714.                     SWAP CGSortLibArr(QSRi), CGSortLibArr(QSRj)
  3715.                     QSRi = QSRi + 1
  3716.                     QSRj = QSRj - 1
  3717.                 END IF
  3718.             LOOP UNTIL QSRi > QSRj
  3719.     END SELECT
  3720.     QSortRecursiveSimplified CGSortLibArr(), start&, QSRj, order&
  3721.     QSortRecursiveSimplified CGSortLibArr(), QSRi, finish&, order&
  3722.  
  3723. '********************
  3724. '* Djikstra SmoothSort converted from VB 2018Feb20 by CodeGuy
  3725. '* There is no BYVAL, in QB64, so I did a workaround
  3726. '*************************
  3727. SUB SmoothSort_TypedArray (TypedCGSortLibArr() AS DataElement, order&)
  3728.     DIM lngOneBasedIndex AS LONG
  3729.     DIM lngNodeIndex AS LONG
  3730.     DIM lngLeftRightTreeAddress AS LONG
  3731.     DIM lngSubTreeSize AS LONG
  3732.     DIM lngLeftSubTreeSize AS LONG
  3733.  
  3734.     lngLeftRightTreeAddress = 1
  3735.     lngSubTreeSize = 1
  3736.     lngLeftSubTreeSize = 1
  3737.     lngOneBasedIndex = 1
  3738.     lngNodeIndex = 0
  3739.  
  3740.     DO WHILE lngOneBasedIndex <> UBOUND(TypedCGSortLibArr) + 1
  3741.         IF lngLeftRightTreeAddress MOD 8 = 3 THEN
  3742.             SmoothSift_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngSubTreeSize, lngLeftSubTreeSize
  3743.             lngLeftRightTreeAddress = (lngLeftRightTreeAddress + 1) \ 4
  3744.             SmoothUp_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3745.             SmoothUp_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3746.         ELSEIF lngLeftRightTreeAddress MOD 4 = 1 THEN 'This is always true if it gets here
  3747.             IF lngOneBasedIndex + lngLeftSubTreeSize < UBOUND(TypedCGSortLibArr) + 1 THEN
  3748.                 SmoothSift_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngSubTreeSize, lngLeftSubTreeSize
  3749.             ELSE
  3750.                 SmoothTrinkle_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngLeftRightTreeAddress, lngSubTreeSize, lngLeftSubTreeSize
  3751.             END IF
  3752.             DO
  3753.                 SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3754.                 lngLeftRightTreeAddress = lngLeftRightTreeAddress * 2
  3755.             LOOP WHILE lngSubTreeSize <> 1 'Continue until we reach the bottom of the tree
  3756.             lngLeftRightTreeAddress = lngLeftRightTreeAddress + 1
  3757.         END IF
  3758.         lngOneBasedIndex = lngOneBasedIndex + 1
  3759.         lngNodeIndex = lngNodeIndex + 1
  3760.     LOOP
  3761.  
  3762.     SmoothTrinkle_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngLeftRightTreeAddress, lngSubTreeSize, lngLeftSubTreeSize
  3763.     DO WHILE lngOneBasedIndex <> 1
  3764.         lngOneBasedIndex = lngOneBasedIndex - 1
  3765.         IF lngSubTreeSize = 1 THEN
  3766.             lngNodeIndex = lngNodeIndex - 1
  3767.             lngLeftRightTreeAddress = lngLeftRightTreeAddress - 1
  3768.             DO WHILE lngLeftRightTreeAddress MOD 2 = 0
  3769.                 lngLeftRightTreeAddress = lngLeftRightTreeAddress / 2
  3770.                 SmoothUp_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3771.             LOOP
  3772.         ELSEIF lngSubTreeSize >= 3 THEN 'It must fall in here, sub trees are either size 1,1,3,5,9,15 etc
  3773.             lngLeftRightTreeAddress = lngLeftRightTreeAddress - 1
  3774.             lngNodeIndex = lngNodeIndex + lngLeftSubTreeSize - lngSubTreeSize
  3775.             IF lngLeftRightTreeAddress <> 0 THEN
  3776.                 SmoothSemiTrinkle_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngLeftRightTreeAddress, lngSubTreeSize, lngLeftSubTreeSize
  3777.             END IF
  3778.             SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3779.             lngLeftRightTreeAddress = lngLeftRightTreeAddress * 2 + 1
  3780.             lngNodeIndex = lngNodeIndex + lngLeftSubTreeSize
  3781.             SmoothSemiTrinkle_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngLeftRightTreeAddress, lngSubTreeSize, lngLeftSubTreeSize
  3782.             SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3783.             lngLeftRightTreeAddress = lngLeftRightTreeAddress * 2 + 1
  3784.         END IF
  3785.     LOOP
  3786.     Restabilize_TypedArray TypedCGSortLibArr(), order&
  3787.  
  3788. SUB SmoothUp_TypedArray (lngSubTreeSize AS LONG, lngLeftSubTreeSize AS LONG)
  3789.     DIM sutemp AS LONG
  3790.     sutemp = lngSubTreeSize + lngLeftSubTreeSize + 1
  3791.     lngLeftSubTreeSize = lngSubTreeSize
  3792.     lngSubTreeSize = sutemp
  3793.  
  3794. SUB SmoothDown_TypedArray (lngSubTreeSize AS LONG, lngLeftSubTreeSize AS LONG)
  3795.     DIM sdtemp AS LONG
  3796.     sdtemp = lngSubTreeSize - lngLeftSubTreeSize - 1
  3797.     lngSubTreeSize = lngLeftSubTreeSize
  3798.     lngLeftSubTreeSize = sdtemp
  3799.  
  3800. SUB SmoothSift_TypedArray (TypedCGSortLibArr() AS DataElement, NodeIndex AS LONG, SubTreeSize AS LONG, LeftSubTreeSize AS LONG)
  3801.     DIM lngNodeIndex AS LONG: lngNodeIndex = NodeIndex
  3802.     DIM lngSubTreeSize AS LONG: lngSubTreeSize = SubTreeSize
  3803.     DIM lngLeftSubTreeSize AS LONG: lngLeftSubTreeSize = LeftSubTreeSize
  3804.     DIM lngChildIndex AS LONG
  3805.  
  3806.     DO WHILE lngSubTreeSize >= 3
  3807.         lngChildIndex = lngNodeIndex - lngSubTreeSize + lngLeftSubTreeSize
  3808.         IF TypedCGSortLibArr(lngChildIndex).thekey < TypedCGSortLibArr(lngNodeIndex - 1).thekey THEN
  3809.             lngChildIndex = lngNodeIndex - 1
  3810.             SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3811.         END IF
  3812.  
  3813.         IF TypedCGSortLibArr(lngNodeIndex).thekey >= TypedCGSortLibArr(lngChildIndex).thekey THEN
  3814.             lngSubTreeSize = 1
  3815.         ELSE
  3816.             Exchange_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngChildIndex
  3817.             lngNodeIndex = lngChildIndex
  3818.             SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3819.         END IF
  3820.     LOOP
  3821.  
  3822. SUB SmoothTrinkle_TypedArray (TypedCGSortLibArr() AS DataElement, NodeIndex AS LONG, LeftRightTreeAddress AS LONG, SubTreeSize AS LONG, LeftSubTreeSize AS LONG)
  3823.     DIM lngNodeIndex AS LONG: lngNodeIndex = NodeIndex
  3824.     DIM lngLeftRightTreeAddress AS LONG: lngLeftRightTreeAddress = LeftRightTreeAddress
  3825.     DIM lngSubTreeSize AS LONG: lngSubTreeSize = SubTreeSize
  3826.     DIM lngLeftSubTreeSize AS LONG: lngLeftSubTreeSize = LeftSubTreeSize
  3827.     DIM lngChildIndex AS LONG
  3828.     DIM lngPreviousCompleteTreeIndex AS LONG
  3829.     DO WHILE lngLeftRightTreeAddress > 0
  3830.         DO WHILE lngLeftRightTreeAddress MOD 2 = 0
  3831.             lngLeftRightTreeAddress = lngLeftRightTreeAddress \ 2
  3832.             SmoothUp_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3833.         LOOP
  3834.         lngPreviousCompleteTreeIndex = lngNodeIndex - lngSubTreeSize
  3835.         IF lngLeftRightTreeAddress = 1 THEN
  3836.             lngLeftRightTreeAddress = 0
  3837.         ELSEIF TypedCGSortLibArr(lngPreviousCompleteTreeIndex).thekey <= TypedCGSortLibArr(lngNodeIndex).thekey THEN
  3838.             lngLeftRightTreeAddress = 0
  3839.         ELSE
  3840.             lngLeftRightTreeAddress = lngLeftRightTreeAddress - 1
  3841.             IF lngSubTreeSize = 1 THEN
  3842.                 Exchange_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngPreviousCompleteTreeIndex
  3843.                 lngNodeIndex = lngPreviousCompleteTreeIndex
  3844.             ELSEIF lngSubTreeSize >= 3 THEN
  3845.                 lngChildIndex = lngNodeIndex - lngSubTreeSize + lngLeftSubTreeSize
  3846.                 IF TypedCGSortLibArr(lngChildIndex).thekey < TypedCGSortLibArr(lngNodeIndex - 1).thekey THEN
  3847.                     lngChildIndex = lngNodeIndex - 1
  3848.                     SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3849.                     lngLeftRightTreeAddress = lngLeftRightTreeAddress * 2
  3850.                 END IF
  3851.                 IF TypedCGSortLibArr(lngPreviousCompleteTreeIndex).thekey >= TypedCGSortLibArr(lngChildIndex).thekey THEN
  3852.                     Exchange_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngPreviousCompleteTreeIndex
  3853.                     lngNodeIndex = lngPreviousCompleteTreeIndex
  3854.                 ELSE
  3855.                     Exchange_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngChildIndex
  3856.                     lngNodeIndex = lngChildIndex
  3857.                     SmoothDown_TypedArray lngSubTreeSize, lngLeftSubTreeSize
  3858.                     lngLeftRightTreeAddress = 0
  3859.                 END IF
  3860.             END IF
  3861.         END IF
  3862.     LOOP
  3863.     SmoothSift_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngSubTreeSize, lngLeftSubTreeSize
  3864.  
  3865. SUB SmoothSemiTrinkle_TypedArray (TypedCGSortLibArr() AS DataElement, NodeIndex AS LONG, LeftRightTreeAddress AS LONG, SubTreeSize AS LONG, LeftSubTreeSize AS LONG)
  3866.     DIM lngNodeIndex AS LONG: lngNodeIndex = NodeIndex
  3867.     DIM lngLeftRightTreeAddress AS LONG: lngLeftRightTreeAddress = LeftRightTreeAddress
  3868.     DIM lngSubTreeSize AS LONG: lngSubTreeSize = SubTreeSize
  3869.     DIM lngLeftSubTreeSize AS LONG: lngLeftSubTreeSize = LeftSubTreeSize
  3870.     DIM lngIndexTopPreviousCompleteHeap AS LONG
  3871.     lngIndexTopPreviousCompleteHeap = lngNodeIndex - lngLeftSubTreeSize
  3872.     IF TypedCGSortLibArr(lngIndexTopPreviousCompleteHeap).thekey > TypedCGSortLibArr(lngNodeIndex).thekey THEN
  3873.         Exchange_TypedArray TypedCGSortLibArr(), lngNodeIndex, lngIndexTopPreviousCompleteHeap
  3874.         SmoothTrinkle_TypedArray TypedCGSortLibArr(), lngIndexTopPreviousCompleteHeap, lngLeftRightTreeAddress, lngSubTreeSize, lngLeftSubTreeSize
  3875.     END IF
  3876.  
  3877. SUB Exchange_TypedArray (TypedCGSortLibArr() AS DataElement, plng1 AS LONG, plng2 AS LONG)
  3878.     IF TypedCGSortLibArr(plng1).thekey <> TypedCGSortLibArr(plng2).thekey THEN
  3879.         SWAP TypedCGSortLibArr(plng1), TypedCGSortLibArr(plng2)
  3880.     ELSE
  3881.         IF TypedCGSortLibArr(plng1).originalorder > TypedCGSortLibArr(plng2).originalorder THEN
  3882.             SWAP TypedCGSortLibArr(plng1), TypedCGSortLibArr(plng2)
  3883.         END IF
  3884.     END IF
  3885.  
  3886. SUB Restabilize_TypedArray (TypedCGSortLibArr() AS DataElement, order&)
  3887.     IF order& = 1 THEN
  3888.     ELSE
  3889.         Rsa& = LBOUND(TypedCGSortLibArr)
  3890.         Rsb& = UBOUND(TypedCGSortLibArr)
  3891.         WHILE Rsa& < Rsb&
  3892.             IF TypedCGSortLibArr(Rsa&).thekey <> TypedCGSortLibArr(Rsb&).thekey THEN
  3893.                 SWAP TypedCGSortLibArr(Rsa&), TypedCGSortLibArr(Rsb&)
  3894.             END IF
  3895.             Rsa& = Rsa& + 1
  3896.             Rsb& = Rsb& - 1
  3897.         WEND
  3898.     END IF
  3899.     q& = LBOUND(TypedCGSortLibArr)
  3900.     DO
  3901.         r& = q& + 1
  3902.         DO
  3903.             IF r& > UBOUND(TypedCGSortLibArr) THEN
  3904.                 EXIT SUB
  3905.             ELSE
  3906.                 IF TypedCGSortLibArr(q&).thekey = TypedCGSortLibArr(r&).thekey THEN
  3907.                     r& = r& + 1
  3908.                 ELSE
  3909.                     EXIT DO
  3910.                 END IF
  3911.             END IF
  3912.         LOOP
  3913.         z& = r&
  3914.         IF r& - q& > 1 THEN
  3915.             DO
  3916.                 p& = r& - 1
  3917.                 IF p& > q& THEN
  3918.                     r& = p& - 1
  3919.                     IF TypedCGSortLibArr(r&).originalorder > TypedCGSortLibArr(p&).originalorder THEN
  3920.                         SWAP TypedCGSortLibArr(r&), TypedCGSortLibArr(p&)
  3921.                     ELSE
  3922.                         EXIT DO
  3923.                     END IF
  3924.                 ELSE
  3925.                     EXIT DO
  3926.                 END IF
  3927.                 r& = p&
  3928.             LOOP
  3929.         END IF
  3930.         q& = z&
  3931.     LOOP UNTIL q& > UBOUND(TypedCGSortLibArr)
  3932.  
  3933. '*********************************
  3934. '* TESTED -- WORKS
  3935. '* This is the iterative version of QuickSort, using a software stack, useful for OLD processors lacking hardware registers
  3936. '* supporting recursion. Operationally, it is very much the same as the recursive version except the "stack" is software-based.
  3937. '* Similar performance to recursive quicksort. Uses Median-of-Three partition method, randomly selected pivot between low and high.
  3938. '**********************************
  3939. SUB QuickSortIterativeMedianOf3 (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)
  3940.     DIM compare AS DOUBLE
  3941.     MinStack& = 2 * LOG(Finish& - Start& + 1) \ LOG(2) + 1
  3942.     DIM LStack&(MinStack&, 1)
  3943.     StackPtr& = 0
  3944.     LStack&(StackPtr&, 0) = Start&
  3945.     LStack&(StackPtr&, 1) = Finish&
  3946.     DO
  3947.         Low& = LStack&(StackPtr&, 0)
  3948.         Hi& = LStack&(StackPtr&, 1)
  3949.         DO
  3950.             i& = Low&
  3951.             j& = Hi&
  3952.             '* one more tactic to help defeat O(n^2) worst-case performance
  3953.             '* pick a RANDOM pivot. Use of fixed pivot 1/2 distance from Low&+(hi&-low&)\2  MAY result in infinite loop
  3954.             IF Hi& - Low& >= 2 THEN
  3955.                 MedianOfThree CGSortLibArr(), Low&, Low& + (Hi& - Low&) \ 2, High&, MedianIndex&
  3956.                 compare = CGSortLibArr(MedianIndex&)
  3957.             ELSE
  3958.                 compare = CGSortLibArr(Low& + (Hi& - Low&) \ 2)
  3959.             END IF
  3960.             SELECT CASE order&
  3961.                 CASE 1
  3962.                     DO
  3963.                         DO WHILE CGSortLibArr(i&) < compare
  3964.                             i& = i& + 1
  3965.                         LOOP
  3966.                         DO WHILE CGSortLibArr(j&) > compare
  3967.                             j& = j& - 1
  3968.                         LOOP
  3969.                         IF i& <= j& THEN
  3970.                             SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  3971.                             i& = i& + 1
  3972.                             j& = j& - 1
  3973.                         END IF
  3974.                     LOOP UNTIL i& > j&
  3975.                 CASE ELSE
  3976.                     DO
  3977.                         DO WHILE CGSortLibArr(i&) > compare
  3978.                             i& = i& + 1
  3979.                         LOOP
  3980.                         DO WHILE CGSortLibArr(j&) < compare
  3981.                             j& = j& - 1
  3982.                         LOOP
  3983.                         IF i& <= j& THEN
  3984.                             SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  3985.                             i& = i& + 1
  3986.                             j& = j& - 1
  3987.                         END IF
  3988.                     LOOP UNTIL i& > j&
  3989.             END SELECT
  3990.             IF j& - Low& < Hi& - i& THEN
  3991.                 IF i& < Hi& THEN
  3992.                     LStack&(StackPtr&, 0) = i&
  3993.                     LStack&(StackPtr&, 1) = Hi&
  3994.                     StackPtr& = StackPtr& + 1
  3995.                 END IF
  3996.                 Hi& = j&
  3997.             ELSE
  3998.                 IF Low& < j& THEN
  3999.                     LStack&(StackPtr&, 0) = Low&
  4000.                     LStack&(StackPtr&, 1) = j&
  4001.                     StackPtr& = StackPtr& + 1
  4002.                 END IF
  4003.                 Low& = i&
  4004.             END IF
  4005.         LOOP WHILE Low& < Hi&
  4006.         StackPtr& = StackPtr& - 1
  4007.     LOOP UNTIL StackPtr& < 0
  4008.  
  4009. '* For QuickSort using the median of three partitioning method. Used to defeat "QuickSort Killer" arrays.
  4010. SUB MedianOfThree (CGSortLibArr() AS DOUBLE, MotA AS LONG, MotB AS LONG, MotC AS LONG, MedianIndex AS LONG)
  4011.     IF CGSortLibArr(MotA) > CGSortLibArr(MotB) THEN
  4012.         IF CGSortLibArr(MotA) < CGSortLibArr(MotC) THEN
  4013.             MedianIndex = MotA
  4014.         ELSEIF CGSortLibArr(MotB) > CGSortLibArr(MotC) THEN
  4015.             MedianIndex = MotB
  4016.         ELSE
  4017.             MedianIndex = MotC
  4018.         END IF
  4019.     ELSE
  4020.         IF CGSortLibArr(MotA) > CGSortLibArr(MotC) THEN
  4021.             MedianIndex = MotA
  4022.         ELSEIF CGSortLibArr(MotB) < CGSortLibArr(MotC) THEN
  4023.             MedianIndex = MotB
  4024.         ELSE
  4025.             MedianIndex = MotC
  4026.         END IF
  4027.     END IF
  4028.  
  4029. '************************************************
  4030. '* This version of BubbleSort actually performs BETTER than InsertionSort for low entropy,
  4031. '* roughly twice as fast as standard BubbleSort.
  4032. '************************************************
  4033. SUB BubbleSortModified (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  4034.     DIM BubbleSortModified_a AS LONG
  4035.     DIM BubbleSortModified_b AS LONG
  4036.     DIM BubbleSortModified_x AS LONG
  4037.     DIM BubbleSortModified_SwapFirst AS LONG
  4038.     DIM BubbleSortModified_SwapLast AS LONG
  4039.     DIM BubbleSortModified_s AS INTEGER
  4040.     SELECT CASE order&
  4041.         CASE 1
  4042.             DO
  4043.                 BubbleSortModified_s = -1
  4044.                 BubbleSortModified_a = start&
  4045.                 BubbleSortModified_b = finish&
  4046.                 DO
  4047.                     BubbleSortModified_SwapFirst = BubbleSortModified_a
  4048.                     BubbleSortModified_SwapLast = BubbleSortModified_b - 1
  4049.                     FOR BubbleSortModified_x = BubbleSortModified_SwapFirst TO BubbleSortModified_SwapLast
  4050.                         IF CGSortLibArr(BubbleSortModified_x) > CGSortLibArr(BubbleSortModified_x + 1) THEN
  4051.                             SWAP CGSortLibArr(BubbleSortModified_x), CGSortLibArr(BubbleSortModified_x + 1)
  4052.                             IF BubbleSortModified_x < BubbleSortModified_b THEN
  4053.                                 BubbleSortModified_b = BubbleSortModified_x
  4054.                             ELSE
  4055.                                 BubbleSortModified_a = BubbleSortModified_x
  4056.                             END IF
  4057.                             BubbleSortModified_s = 0
  4058.                         END IF
  4059.                     NEXT
  4060.                     SWAP BubbleSortModified_a, BubbleSortModified_b
  4061.                 LOOP WHILE BubbleSortModified_a < BubbleSortModified_b
  4062.             LOOP UNTIL BubbleSortModified_s
  4063.         CASE ELSE
  4064.             DO
  4065.                 BubbleSortModified_s = -1
  4066.                 BubbleSortModified_a = start&
  4067.                 BubbleSortModified_b = finish&
  4068.                 DO
  4069.                     BubbleSortModified_SwapFirst = BubbleSortModified_a
  4070.                     BubbleSortModified_SwapLast = BubbleSortModified_b - 1
  4071.                     FOR BubbleSortModified_x = BubbleSortModified_SwapFirst TO BubbleSortModified_SwapLast
  4072.                         IF CGSortLibArr(BubbleSortModified_x) < CGSortLibArr(BubbleSortModified_x + 1) THEN
  4073.                             SWAP CGSortLibArr(BubbleSortModified_x), CGSortLibArr(BubbleSortModified_x + 1)
  4074.                             IF BubbleSortModified_x < BubbleSortModified_b THEN
  4075.                                 BubbleSortModified_b = BubbleSortModified_x
  4076.                             ELSE
  4077.                                 BubbleSortModified_a = BubbleSortModified_x
  4078.                             END IF
  4079.                             BubbleSortModified_s = 0
  4080.                         END IF
  4081.                     NEXT
  4082.                     SWAP BubbleSortModified_a, BubbleSortModified_b
  4083.                 LOOP WHILE BubbleSortModified_a < BubbleSortModified_b
  4084.             LOOP UNTIL BubbleSortModified_s
  4085.     END SELECT
  4086.  
  4087. '*****************************
  4088. '* MergeEfficient is essentially MergeSort, except it uses the EfficientMerge routine requiring only half the auxiliary.
  4089. '* Just here for comparison against the standard MergeSort. Yes, it is faster because there is less array copying.
  4090. '*******************************
  4091. SUB MergeSortEfficient (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4092.     IF start < finish THEN
  4093.         DIM MergeEfficientMiddle AS LONG
  4094.         MergeEfficientMiddle = start + (finish - start) \ 2
  4095.         MergeSortEfficient CGSortLibArr(), start, MergeEfficientMiddle, order&
  4096.         MergeSortEfficient CGSortLibArr(), MergeEfficientMiddle + 1, finish, order&
  4097.         EfficientMerge CGSortLibArr(), start, finish, order&
  4098.     END IF
  4099. '********************
  4100. '* approximately 4 times as fast as standard BubbleSort, making this algorithm less
  4101. '* computationally painful for larger unordered datasets.
  4102. '********************
  4103. SUB BubbleSortRecursiveEmerge (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4104.     IF start < finish THEN
  4105.         m& = start + (finish - start) \ 2
  4106.         BubbleSortModified CGSortLibArr(), start, m&, order&
  4107.         BubbleSortModified CGSortLibArr(), m& + 1, finish, order&
  4108.         EfficientMerge CGSortLibArr(), start, finish, order&
  4109.     END IF
  4110.  
  4111. '**************************
  4112. '* approximately twice as fast as the original version for unordered datasets.
  4113. '**************************
  4114. SUB BubbleSortModified_0 (a() AS DOUBLE, start&, finish&, order&)
  4115.     SELECT CASE order&
  4116.         CASE 1
  4117.             DO
  4118.                 s& = -1
  4119.                 a& = start&
  4120.                 b& = finish&
  4121.                 DO
  4122.                     first& = a&
  4123.                     last& = b& - 1
  4124.                     FOR x& = first& TO last&
  4125.                         IF a(x&) > a(x& + 1) THEN
  4126.                             SWAP a(x&), a(x& + 1)
  4127.                             IF x& < b& THEN
  4128.                                 b& = x&
  4129.                             ELSE
  4130.                                 a& = x&
  4131.                             END IF
  4132.                             s& = 0
  4133.                         END IF
  4134.                     NEXT
  4135.                     SWAP a&, b&
  4136.                 LOOP WHILE a& < b&
  4137.             LOOP UNTIL s&
  4138.         CASE ELSE
  4139.             DO
  4140.                 s& = -1
  4141.                 a& = start&
  4142.                 b& = finish&
  4143.                 DO
  4144.                     first& = a&
  4145.                     last& = b& - 1
  4146.                     FOR x& = first& TO last&
  4147.                         IF a(x&) < a(x& + 1) THEN
  4148.                             SWAP a(x&), a(x& + 1)
  4149.                             IF x& < b& THEN
  4150.                                 b& = x&
  4151.                             ELSE
  4152.                                 a& = x&
  4153.                             END IF
  4154.                             s& = 0
  4155.                         END IF
  4156.                     NEXT
  4157.                     SWAP a&, b&
  4158.                 LOOP WHILE a& < b&
  4159.             LOOP UNTIL s&
  4160.     END SELECT
  4161.  
  4162. '*******************************
  4163. '* KnuthShuffle, named for its progenitor, Donald Knuth, rearranges CGSortLibArr() in randomized order, swapping element
  4164. '* KnuthStart& and some element after it up to CGSortLibArr(finish&)
  4165. '*******************************
  4166. SUB KnuthShuffle (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG)
  4167.     DIM KnuthStart AS LONG: KnuthStart = start
  4168.     DIM Randomindexintoarray AS LONG
  4169.  
  4170.     DO WHILE (KnuthStart < finish)
  4171.         Randomindexintoarray = KnuthStart + INT(RND * (finish - KnuthStart - 1))
  4172.         SWAP CGSortLibArr(KnuthStart), CGSortLibArr(Randomindexintoarray)
  4173.         KnuthStart = KnuthStart + 1
  4174.     LOOP
  4175.  
  4176. FUNCTION ArraySequenceCheck& (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  4177.     oseq& = order&
  4178.     h& = start&
  4179.     i& = start&
  4180.     DO
  4181.         IF CGSortLibArr(i&) < CGSortLibArr(h&) THEN
  4182.             IF oseq& = 1 THEN
  4183.                 '* this is a sequence error
  4184.                 oseq& = 0
  4185.                 EXIT DO
  4186.             ELSE
  4187.                 oseq& = -1
  4188.                 h& = i&
  4189.             END IF
  4190.         ELSEIF CGSortLibArr(i&) > CGSortLibArr(h&) THEN
  4191.             IF oseq& = -1 THEN
  4192.                 '* this is also a sequence error
  4193.                 oseq& = 0
  4194.                 EXIT DO
  4195.             ELSE
  4196.                 oseq& = 1
  4197.                 h& = i&
  4198.             END IF
  4199.         END IF
  4200.         i& = i& + 1
  4201.     LOOP UNTIL i& > finish&
  4202.     ArraySequenceCheck& = (oseq& = order&)
  4203.  
  4204. FUNCTION NthPlace& (a() AS DOUBLE, NPMMrec AS MinMaxRec, start AS LONG, finish AS LONG, order&, npindex AS LONG)
  4205.     DIM NPx AS DOUBLE: NPx = (a(npindex) - a(NPMMrec.min))
  4206.     DIM NPy AS DOUBLE: NPy = (a(NPMMrec.max) - a(NPMMrec.min))
  4207.     Np& = INT((NPx * (finish - start)) \ NPy)
  4208.     IF order& = 1 THEN
  4209.         NthPlace& = start + Np&
  4210.     ELSE
  4211.         NthPlace& = finish - Np&
  4212.     END IF
  4213.  
  4214. 'SUB BFPRT (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, BFPRTMedian AS DOUBLE)
  4215. '    bfprtn& = 5
  4216. '    IF finish - start < bfprtn& - 1 THEN
  4217. '        SELECT CASE (finish - start) MOD bfprtn&
  4218. '            CASE 0, 2, 4
  4219. '                BFPRTMedian = CGSortLibArr(start + (finish - start) \ 2)
  4220. '            CASE 1
  4221. '                BFPRTMedian = (CGSortLibArr(start) + CGSortLibArr(finish)) / 2
  4222. '            CASE 3
  4223. '                BFPRTMedian = (CGSortLibArr(start + 1) + CGSortLibArr(finish - 1)) / 2
  4224. '        END SELECT
  4225. '        FOR c& = start TO finish
  4226. '            PRINT CGSortLibArr(c&); finish - start
  4227. '        NEXT
  4228. '    ELSE
  4229. '        REDIM BfprtArray(0 TO (finish - start) / bfprtn& + bfprtn&) AS DOUBLE
  4230. '        s& = LBOUND(CGSortLibArr)
  4231. '        BfprtCount& = 0
  4232. '        DO
  4233. '            IF s& - 1 > finish - bfprtn& THEN
  4234. '                InsertionSort CGSortLibArr(), s&, finish, 1
  4235. '                DO UNTIL s& > finish
  4236. '                    BfprtArray(BfprtCount&) = CGSortLibArr(s&)
  4237. '                    s& = s& + 1
  4238. '                LOOP
  4239. '                EXIT DO
  4240. '            ELSE
  4241. '                InsertionSort CGSortLibArr(), s&, s& + bfprtn& - 1, 1
  4242. '                BfprtArray(BfprtCount&) = CGSortLibArr(s& + (bfprtn& - 1) \ 2)
  4243. '                '* PRINT BfprtArray(BfprtCount&); BfprtCount&
  4244. '                BfprtCount& = BfprtCount& + 1
  4245. '                s& = s& + bfprtn&
  4246. '            END IF
  4247. '        LOOP
  4248. '        BFPRT BfprtArray(), 0, BfprtCount& - 1, BFPRTMedian
  4249. '    END IF
  4250. 'END SUB
  4251.  
  4252.  
  4253. '* Demo of 48828125 double-precision elements takes a shade over 10s on 2.16GHz machine, sometimes going low as 9.6s.
  4254. '* Blum, M.; Floyd, R. W.; Pratt, V. R.; Rivest, R. L.; Tarjan, R. E. (August 1973). "Time bounds for selection" (PDF).
  4255. '* Journal of Computer and System Sciences. 7 (4): 448–461. doi:10.1016/S0022-0000(73)80033-9.
  4256.  
  4257. '*******************************
  4258. ''* Coded 25 Mar 2018 By CodeGuy
  4259. '*******************************
  4260. SUB BFPRT (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, BFPRTMedian AS DOUBLE)
  4261.     DIM BFPRT_ScanIndexArray AS LONG
  4262.     DIM BFPRT_countIndex AS LONG
  4263.     DIM BFPRT_SubdivisionSize AS LONG: BFPRT_SubdivisionSize = 5
  4264.     IF finish - start < BFPRT_SubdivisionSize - 1 THEN
  4265.         SELECT CASE (finish - start) MOD BFPRT_SubdivisionSize
  4266.             CASE 0, 2, 4
  4267.                 BFPRTMedian = CGSortLibArr(start + (finish - start) \ 2)
  4268.             CASE 1
  4269.                 BFPRTMedian = (CGSortLibArr(start) + CGSortLibArr(finish)) / 2
  4270.             CASE 3
  4271.                 BFPRTMedian = (CGSortLibArr(start + 1) + CGSortLibArr(finish - 1)) / 2
  4272.         END SELECT
  4273.     ELSE
  4274.         REDIM BfprtArray(0 TO (finish - start) / BFPRT_SubdivisionSize + BFPRT_SubdivisionSize) AS DOUBLE
  4275.         BFPRT_ScanIndexArray = LBOUND(CGSortLibArr)
  4276.         BFPRT_CountArrayIndex = 0
  4277.         DO
  4278.             IF BFPRT_ScanIndexArray - 1 > finish - BFPRT_SubdivisionSize THEN
  4279.                 InsertionSort CGSortLibArr(), BFPRT_ScanIndexArray, finish, 1
  4280.                 DO UNTIL BFPRT_ScanIndexArray > finish
  4281.                     BfprtArray(BFPRT_CountArrayIndex) = CGSortLibArr(BFPRT_ScanIndexArray)
  4282.                     BFPRT_ScanIndexArray = BFPRT_ScanIndexArray + 1
  4283.                 LOOP
  4284.                 EXIT DO
  4285.             ELSE
  4286.                 InsertionSort CGSortLibArr(), BFPRT_ScanIndexArray, BFPRT_ScanIndexArray + BFPRT_SubdivisionSize - 1, 1
  4287.                 BfprtArray(BFPRT_CountArrayIndex) = CGSortLibArr(BFPRT_ScanIndexArray + (BFPRT_SubdivisionSize - 1) \ 2)
  4288.                 '* PRINT BfprtArray(BFPRT_CountArrayIndex); BFPRT_CountArrayIndex
  4289.                 BFPRT_CountArrayIndex = BFPRT_CountArrayIndex + 1
  4290.                 BFPRT_ScanIndexArray = BFPRT_ScanIndexArray + BFPRT_SubdivisionSize
  4291.             END IF
  4292.         LOOP
  4293.         BFPRT BfprtArray(), 0, BFPRT_CountArrayIndex - 1, BFPRTMedian
  4294.     END IF
  4295.  
  4296.  
  4297. '* used to defeat the "midnight bug."
  4298. FUNCTION DeltaTime! (time1!, time2!)
  4299.     IF time2! < time1! THEN
  4300.         DeltaTime! = (86400 - time1!) + time2!
  4301.     ELSE
  4302.         DeltaTime! = time2! - time1!
  4303.     END IF
  4304.  
  4305. SUB CGScaleArrayToInteger (CGSortLibArr() AS DOUBLE, start&, finish&, order&, CGSortLibArr_mmrec AS MinMaxRec, CGSortLibArr_ScaleMultiplier AS DOUBLE)
  4306.     DIM CGScaleArray_Range AS DOUBLE
  4307.     DIM CGScaleArray_ScaleTemp AS DOUBLE
  4308.     DIM CGScaleArray_PowerOf2 AS LONG
  4309.  
  4310.     DIM CGScaleArray_rank AS LONG
  4311.     DIM CGScaleArray_Index AS LONG
  4312.  
  4313.     GetMinMaxArray CGSortLibArr(), start&, finish&, CGSortLibArr_mmrec
  4314.     CGScaleArray_Range = CGSortLibArr(CGSortLibArr_mmrec.max) - CGSortLibArr(CGSortLibArr_mmrec.min)
  4315.     IF CGScaleArray_Range <> 0 THEN
  4316.         CGScaleArray_ScaleTemp = CGScaleArray_Range
  4317.     ELSE
  4318.         CGScaleArray_ScaleTemp = CGSortLibArr(CGSortLibArr_mmrec.min)
  4319.         CGScaleArray_Range = 1
  4320.     END IF
  4321.     CGScaleArray_PowerOf2 = 0
  4322.     DO UNTIL CGScaleArray_ScaleTemp = INT(CGScaleArray_ScaleTemp)
  4323.         CGScaleArray_ScaleTemp = CGScaleArray_ScaleTemp * 2
  4324.         CGScaleArray_PowerOf2 = CGScaleArray_PowerOf2 + 1
  4325.     LOOP
  4326.     CGSortLibArr_ScaleMultiplier = 2 ^ CGScaleArray_PowerOf2
  4327.  
  4328. SUB CGFrequencyCounts (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&, CGSortLibArr_mmrec AS MinMaxRec, CGSortLibArr_ScaleMultiplier AS DOUBLE)
  4329.     '* a short example of using this multiplier to convert the range respresented by
  4330.     '* CGSortLibArr(start&) -> CGSortLibArr(finish&)
  4331.     '* to a scaled integer: the lowest valued element will appear as 0.
  4332.     REDIM CGFrequencyCounts_Array(0 TO Finish& - Start&) AS LONG
  4333.     DIM CGFrequencyCounts_IteratorU AS LONG
  4334.     DIM CGFrequencyCounts_Index AS LONG
  4335.     DIM CGFrequencyCounts_rank AS LONG
  4336.     FOR CGFrequencyCounts_IteratorU = Start& TO Finish&
  4337.         CGFrequencyCounts_Index = NthPlace&(CGSortLibArr(), CGSortLibArr_mmrec, Start&, Finish&, order&, CGFrequencyCounts_IteratorU)
  4338.         'CGFrequencyCounts_Index = INT((CGSortLibArr(CGFrequencyCounts_IteratorU) - CGSortLibArr(CGSortLibArr_mmrec.min)) * (Finish& - Start&) / (CGSortLibArr(CGSortLibArr_mmrec.max) - CGSortLibArr(CGSortLibArr_mmrec.min)))
  4339.         CGFrequencyCounts_Array(CGFrequencyCounts_rank) = CGFrequencyCounts_Array(CGFrequencyCounts_rank) + 1
  4340.         '* this was for profiling purposes and short demo
  4341.         'IF CGFrequencyCounts_IteratorU MOD 37 = 0 THEN
  4342.         '    LOCATE (CGFrequencyCounts_IteratorU MOD DisplayRows) + 1, 1
  4343.         '    PRINT USING "i=#,###,###,###,###"; CGFrequencyCounts_Index;
  4344.         '    PRINT USING "r=#,###,###,###,###"; CGFrequencyCounts_rank;
  4345.         '    PRINT USING "s=#################"; CGFrequencyCounts_x * CGSortLibArr_ScaleMultiplier;
  4346.         '    PRINT USING "t(###,###,###,###)="; CGFrequencyCounts_IteratorU;
  4347.         '    PRINT USING "###################"; CGSortLibArr(CGFrequencyCounts_IteratorU) * CGSortLibArr_ScaleMultiplier;
  4348.         '    PRINT USING "c=#,###,####,###,###"; CGFrequencyCounts_Array(CGFrequencyCounts_rank);
  4349.         'END IF
  4350.     NEXT
  4351.     FOR stx& = LBOUND(CGFrequencyCounts_Array) TO UBOUND(CGFrequencyCounts_Array)
  4352.         IF CGFrequencyCounts_Array(stx&) > 0 THEN
  4353.             b# = CGSortLibArr(CGSortLibArr_mmrec.min) + (CGSortLibArr(CGSortLibArr_mmrec.max) - CGSortLibArr(CGSortLibArr_mmrec.min)) * (stx& / UBOUND(CGFrequencyCounts_Array))
  4354.             'PRINT "{"; b#; ","; CGFrequencyCounts_Array(stx&); "}";
  4355.         END IF
  4356.     NEXT
  4357.     ERASE CGFrequencyCounts_Array
  4358.  
  4359.  
  4360. SUB CGSetSortLibArray (CGSortLibArr() AS DOUBLE, start&, finish&, order&, minimum AS DOUBLE, maximum AS DOUBLE, makeint&)
  4361.     DIM CGSetSortLibArrDelta AS DOUBLE
  4362.     IF minimum > maximum THEN
  4363.         CGSetSortLibArrDelta = minimum - maximum
  4364.     ELSE
  4365.         CGSetSortLibArrDelta = maximum - minimum
  4366.     END IF
  4367.  
  4368.     CGSetSortLibArrDelta = CGSetSortLibArrDelta / (finish& - start& + 1)
  4369.     MontonicValue# = mimumum + RND * (maximum - minimum)
  4370.     FOR s& = start& TO finish&
  4371.         SELECT CASE order&
  4372.             CASE -1 '* descending
  4373.                 CGSortLibArr(s&) = maximum - (s& - start&) * CGSetSortLibArrDelta
  4374.             CASE 0 '*random
  4375.                 CGSortLibArr(s&) = minimum + RND * (maximum - minimum)
  4376.             CASE 1 '* ascending
  4377.                 CGSortLibArr(s&) = minimum + (s& - start&) * CGSetSortLibArrDelta
  4378.             CASE 2 '* bitonic
  4379.                 m& = start& + (finish& - start&) \ 2
  4380.                 IF s& > m& THEN
  4381.                     CGSortLibArr(s&) = maximum - (s& - m&) * CGSetSortLibArrDelta
  4382.                 ELSE
  4383.                     CGSortLibArr(s&) = minimum + (s& - start&) * CGSetSortLibArrDelta
  4384.                 END IF
  4385.             CASE 3 '* monotonic
  4386.                 CGSortLibArr(s&) = MontonicValue#
  4387.         END SELECT
  4388.     NEXT
  4389.  
  4390.     IF makeint& THEN
  4391.         ScaleArrayToInt CGSortLibArr(), start&, finish&
  4392.     END IF
  4393.  
  4394. SUB ScaleArrayToInt (CGSortLibArr() AS DOUBLE, start&, finish&)
  4395.     DIM satimmrec AS MinMaxRec
  4396.     GetMinMaxArray CGSortLibArr(), start&, finish&, satimmrec
  4397.     DIM sati_T AS DOUBLE
  4398.     DIM sati_m AS INTEGER
  4399.     IF CGSortLibArr(satimmrec.min) <> 0 THEN
  4400.         sati_T = CGSortLibArr(satimmrec.min)
  4401.     ELSE
  4402.         sati_T = CGSortLibArr(satimmrec.max)
  4403.     END IF
  4404.  
  4405.     IF sati_T <> 0 THEN
  4406.         sati_m = 1
  4407.         DO
  4408.             IF sati_T = INT(sati_T) THEN
  4409.                 EXIT DO
  4410.             ELSE
  4411.                 sati_m = sati_m * 10
  4412.                 sati_T = sati_T * 10
  4413.             END IF
  4414.         LOOP
  4415.         IF sati_m > 1 THEN
  4416.             FOR s& = start& TO finish&
  4417.                 CGSortLibArr(s&) = CGSortLibArr(s&) * sati_m
  4418.             NEXT
  4419.         END IF
  4420.     END IF
  4421.  
  4422. SUB BitInvert (C() AS DOUBLE, start&, finish&, method&)
  4423.     '* Method
  4424.     '* 0 simply bit-inverts bits in an element of CGSortLibArr(), one by one
  4425.     '* 1 Inverts the elements of CGSortLibArr()
  4426.     '* 2 bit-inverts elements of CGSortLibArr()
  4427.  
  4428.  
  4429. '// C++ program to perform TimSort.
  4430. '#include<bits/stdc++.h>
  4431. 'using namespace std;
  4432. 'const int RUN = 32;
  4433.  
  4434. '// this function sorts array from left index to
  4435. '// to right index which is of size atmost RUN
  4436. 'void insertionSort(int arr[], int left, int right)
  4437. '{
  4438. '    for (int i = left + 1; i <= right; i++)
  4439. '    {
  4440. '        int temp = arr[i];
  4441. '        int j = i - 1;
  4442. '        while (arr[j] > temp && j >= left)
  4443. '        {
  4444. '            arr[j+1] = arr[j];
  4445. '            j--;
  4446. '        }
  4447. '        arr[j+1] = temp;
  4448. '    }
  4449. '}
  4450.  
  4451. '// merge function merges the sorted runs
  4452. 'void merge(int arr[], int l, int m, int r)
  4453. '{
  4454. '    // original array is broken in two parts
  4455. '    // left and right array
  4456. '    int len1 = m - l + 1, len2 = r - m;
  4457. '    int left[len1], right[len2];
  4458. '    for (int i = 0; i < len1; i++)
  4459. '        left[i] = arr[l + i];
  4460. '    for (int i = 0; i < len2; i++)
  4461. '        right[i] = arr[m + 1 + i];
  4462.  
  4463. '    int i = 0;
  4464. '    int j = 0;
  4465. '    int k = l;
  4466.  
  4467. '    // after comparing, we merge those two array
  4468. '    // in larger sub array
  4469. '    while (i < len1 && j < len2)
  4470. '    {
  4471. '        if (left[i] <= right[j])
  4472. '        {
  4473. '            arr[k] = left[i];
  4474. '            i++;
  4475. '        }
  4476. '        else
  4477. '        {
  4478. '            arr[k] = right[j];
  4479. '            j++;
  4480. '        }
  4481. '        k++;
  4482. '    }
  4483.  
  4484. '    // copy remaining elements of left, if any
  4485. '    while (i < len1)
  4486. '    {
  4487. '        arr[k] = left[i];
  4488. '        k++;
  4489. '        i++;
  4490. '    }
  4491.  
  4492. '    // copy remaining element of right, if any
  4493. '    while (j < len2)
  4494. '    {
  4495. '        arr[k] = right[j];
  4496. '        k++;
  4497. '        j++;
  4498. '    }
  4499. '}
  4500.  
  4501. '// iterative Timsort function to sort the
  4502. '// array[0...n-1] (similar to merge sort)
  4503. 'void timSort(int arr[], int n)
  4504. '{
  4505. '    // Sort individual subarrays of size RUN
  4506. '    for (int i = 0; i < n; i+=RUN)
  4507. '        insertionSort(arr, i, min((i+31), (n-1)));
  4508.  
  4509. '    // start merging from size RUN (or 32). It will merge
  4510. '    // to form size 64, then 128, 256 and so on ....
  4511. '    for (int size = RUN; size < n; size = 2*size)
  4512. '    {
  4513. '        // pick starting point of left sub array. We
  4514. '        // are going to merge arr[left..left+size-1]
  4515. '        // and arr[left+size, left+2*size-1]
  4516. '        // After every merge, we increase left by 2*size
  4517. '        for (int left = 0; left < n; left += 2*size)
  4518. '        {
  4519. '            // find ending point of left sub array
  4520. '            // mid+1 is starting point of right sub array
  4521. '            int mid = left + size - 1;
  4522. '            int right = min((left + 2*size - 1), (n-1));
  4523.  
  4524. '            // merge sub array arr[left.....mid] &
  4525. '            // arr[mid+1....right]
  4526. '            merge(arr, left, mid, right);
  4527. '        }
  4528. '    }
  4529. '}
  4530.  
  4531. '// utility function to print the Array
  4532. 'void printArray(int arr[], int n)
  4533. '{
  4534. '    for (int i = 0; i < n; i++)
  4535. '        printf("%d  ", arr[i]);
  4536. '    printf("\n");
  4537. '}
  4538.  
  4539. '// Driver program to test above function
  4540. 'int main()
  4541. '{
  4542. '    int arr[] = {5, 21, 7, 23, 19};
  4543. '    int n = sizeof(arr)/sizeof(arr[0]);
  4544. '    printf("Given Array is\n");
  4545. '    printArray(arr, n);
  4546.  
  4547. '    timSort(arr, n);
  4548.  
  4549. '    printf("After Sorting Array is\n");
  4550. '    printArray(arr, n);
  4551. '    return 0;
  4552. '}
  4553. SUB UnionIntersectionLists (array_a() AS DOUBLE, array_a_start AS LONG, array_a_finish AS LONG, array_b() AS DOUBLE, array_b_start AS LONG, array_b_finish AS LONG, UIArray() AS DOUBLE, UIFunction%)
  4554.     SELECT CASE UIFunction%
  4555.         CASE 0 '* union"
  4556.         CASE ELSE '* intersection
  4557.     END SELECT
  4558.     REDIM UIArray(0 TO 0) AS DOUBLE
  4559.     DIM inserted_in_UI AS LONG
  4560.     DIM start_a AS LONG
  4561.     DIM Start_b AS LONG
  4562.     '* Give CodeGuy some props.
  4563.     primeGapSort2 array_a(), array_a_start, array_a_finish, 1
  4564.     primeGapSort2 array_b(), array_b_start, array_b_finish, 1
  4565.     inserted_in_UI = LBOUND(UIArray)
  4566.     start_a = array_a_start
  4567.     Start_b = array_b_start
  4568.     SELECT CASE UIFunction%
  4569.         CASE 0 '* union
  4570.             DO
  4571.                 IF start_a > array_a_finish THEN
  4572.                     WHILE Start_b <= array_b_finish
  4573.                         REDIM _PRESERVE UIArray(0 TO inserted_in_UI) AS DOUBLE
  4574.                         UIArray(inserted_in_UI) = array_b(Start_b)
  4575.                         inserted_in_UI = inserted_in_UI + 1
  4576.                         Start_b = Start_b + 1
  4577.                     WEND
  4578.                     EXIT DO
  4579.                 ELSEIF Start_b > array_b_finish THEN
  4580.                     WHILE start_a <= array_a_finish
  4581.                         REDIM _PRESERVE UIArray(0 TO inserted_in_UI) AS DOUBLE
  4582.                         UIArray(inserted_in_UI) = array_a(start_a)
  4583.                         inserted_in_UI = inserted_in_UI + 1
  4584.                         start_a = start_a + 1
  4585.                     WEND
  4586.                     EXIT DO
  4587.                 ELSE
  4588.                     REDIM _PRESERVE UIArray(0 TO inserted_in_UI) AS DOUBLE
  4589.                     IF array_b(Start_b) < array_a(start_a) THEN
  4590.                         UIArray(inserted_in_UI) = array_b(Start_b)
  4591.                         Start_b = Start_b + 1
  4592.                     ELSE
  4593.                         UIArray(inserted_in_UI) = array_a(start_a)
  4594.                         start_a = start_a + 1
  4595.                     END IF
  4596.                     inserted_in_UI = inserted_in_UI + 1
  4597.                 END IF
  4598.             LOOP
  4599.         CASE 1 '* intersection
  4600.             '* binary search is fine for this operation too
  4601.             '* intersection finds elements common to array_a() and array_b()
  4602.             '* elements common to both arrays are inserted into UIArray()
  4603.             DO
  4604.                 IF start_a > array_a_finish THEN
  4605.                     EXIT DO
  4606.                 END IF
  4607.                 IF Start_b > array_b_finish THEN
  4608.                     EXIT DO
  4609.                 END IF
  4610.                 IF array_a(start_a) = array_b(Start_b) THEN
  4611.                     REDIM _PRESERVE UIArray(0 TO inserted_in_UI)
  4612.                     UIArray(inserted_in_UI) = array_a(start_a)
  4613.                     inserted_in_UI = inserted_in_UI + 1
  4614.                     start_a = start_a + 1
  4615.                     Start_b = Start_b + 1
  4616.                 ELSEIF array_a(start_a) > array_b(Start_b) THEN
  4617.                     Start_b = Start_b + 1
  4618.                 ELSE
  4619.                     start_a = start_a + 1
  4620.                 END IF
  4621.             LOOP
  4622.     END SELECT
  4623.  
  4624. '**************************************
  4625. '* anyone claiming you need c/c++ to implement trees is telling you CRAP
  4626. '* This is a bit more complex than the standard non-copying version, but it is still
  4627. '* respectably fast. General complexity for TreeSort() is O(NLogN), EXCEPT when
  4628. '* presented with elements already sorted. One way to avoid this is to KnuthShuffle
  4629. '* the input first. Skipped in this implementation, but there is no reason you
  4630. '* can't do it prior to TreeSort(). Code modified/added from my repository. This
  4631. '* version allows multiple same-value nodes
  4632. '* Modified/added 26 March 2018.
  4633. '**************************************
  4634. SUB TreeSortUsingBST (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  4635.     DIM TSAmmrec AS MinMaxRec
  4636.     GetMinMaxArray CGSortLibArr(), start&, finish&, TSAmmrec
  4637.     delta# = CGSortLibArr(TSAmmrec.max) - CGSortLibArr(TSAmmrec.min)
  4638.     IF delta# = 0 THEN 'already sorted because they're all equal
  4639.         EXIT SUB
  4640.     END IF
  4641.     NilValue& = LBOUND(CGSortLibArr) - 1
  4642.     TYPE TreeNode
  4643.         value AS DOUBLE
  4644.         left AS LONG
  4645.         right AS LONG
  4646.     END TYPE
  4647.     DIM tree(start& + 1 TO finish& + 1) AS TreeNode
  4648.     FOR x& = start& + 1 TO finish& + 1
  4649.         tree(x&).value = 0
  4650.         tree(x&).left = NilValue&
  4651.         tree(x&).right = NilValue&
  4652.     NEXT
  4653.     tree(1).value = CGSortLibArr(1 - 1)
  4654.     free& = 2
  4655.     IF order& = 1 THEN
  4656.         FOR x& = 2 TO finish&
  4657.             pointer& = 1
  4658.             DO
  4659.                 IF CGSortLibArr(x& - 1) < tree(pointer&).value THEN
  4660.                     IF tree(pointer&).left = NilValue& THEN
  4661.                         tree(pointer&).left = free&
  4662.                         tree(free&).value = CGSortLibArr(x& - 1)
  4663.                         free& = free& + 1
  4664.                         EXIT DO
  4665.                     ELSE
  4666.                         pointer& = tree(pointer&).left
  4667.                     END IF
  4668.                 ELSE
  4669.                     IF tree(pointer&).right = NilValue& THEN
  4670.                         tree(pointer&).right = free&
  4671.                         tree(free&).value = CGSortLibArr(x& - 1)
  4672.                         free& = free& + 1
  4673.                         EXIT DO
  4674.                     ELSE
  4675.                         pointer& = tree(pointer&).right
  4676.                     END IF
  4677.                 END IF
  4678.             LOOP
  4679.         NEXT x&
  4680.     ELSE
  4681.         FOR x& = 2 TO finish&
  4682.             pointer& = 1
  4683.             DO
  4684.                 IF CGSortLibArr(x& - 1) > tree(pointer&).value THEN
  4685.                     IF tree(pointer&).left = NilValue& THEN
  4686.                         tree(pointer&).left = free&
  4687.                         tree(free&).value = CGSortLibArr(x& - 1)
  4688.                         free& = free& + 1
  4689.                         EXIT DO
  4690.                     ELSE
  4691.                         pointer& = tree(pointer&).left
  4692.                     END IF
  4693.                 ELSE
  4694.                     IF tree(pointer&).right = NilValue& THEN
  4695.                         tree(pointer&).right = free&
  4696.                         tree(free&).value = CGSortLibArr(x& - 1)
  4697.                         free& = free& + 1
  4698.                         EXIT DO
  4699.                     ELSE
  4700.                         pointer& = tree(pointer&).right
  4701.                     END IF
  4702.                 END IF
  4703.             LOOP
  4704.         NEXT x&
  4705.     END IF
  4706.     depth& = start& + 1
  4707.     Traverse_tree CGSortLibArr(), start& + 1, depth&, tree(), NilValue&
  4708.     ERASE tree
  4709.  
  4710. SUB Traverse_tree (CGSortLibArr() AS DOUBLE, NextPtr&, depth&, tree() AS TreeNode, NilValue&)
  4711.     IF tree(NextPtr&).left <> NilValue& THEN
  4712.         Traverse_tree CGSortLibArr(), tree(NextPtr&).left, depth&, tree(), NilValue&
  4713.     END IF
  4714.     CGSortLibArr(depth& - 1) = tree(NextPtr&).value
  4715.     depth& = depth& + 1
  4716.     IF tree(NextPtr&).right <> NilValue& THEN Traverse_tree CGSortLibArr(), tree(NextPtr&).right, depth&, tree(), NilValue&
  4717.  
  4718.  
  4719. '* normopt& = 0 normalizes range (0,,1) inclusive
  4720. '* normopt& = 1 returns minimum NVT_ScaleMultiplier that multiplied by each element returns a whole (non-decimal) number
  4721. SUB NormalizeVectorTo (CGSortLibArr() AS DOUBLE, start&, finish&, NormOpt&, NVT_ScaleMultiplier AS DOUBLE)
  4722.     DIM NormalizeVectorTo_minmax AS MinMaxRec
  4723.     DIM NVT_Minimum AS DOUBLE
  4724.     NVT_ScaleMultiplier = 1
  4725.     GetMinMaxArray CGSortLibArr(), start&, finish&, NormalizeVectorTo_minmax: NVT_Minimum = CGSortLibArr(NormalizeVectorTo_minmax.min)
  4726.     IF CGSortLibArr(NormalizeVectorTo_minmax.min) < CGSortLibArr(NormalizeVectorTo_minmax.max) THEN
  4727.         IF NormOpt& = 1 THEN
  4728.             NVT_ScaleMultiplier = CGSortLibArr(NormalizeVectorTo_minmax.max) - CGSortLibArr(NormalizeVectorTo_minmax.min)
  4729.             FOR s& = start& TO finish&
  4730.                 CGSortLibArr(s&) = (CGSortLibArr(s&) - NVT_Minimum) / NVT_ScaleMultiplier#
  4731.             NEXT
  4732.         ELSE
  4733.             '*************************
  4734.             IF CGSortLibArr(NormalizeVectorTo_minmax.min) <> 0 THEN
  4735.                 NVT_ScaleMultiplier = CGSortLibArr(NormalizeVectorTo_minmax.min)
  4736.             ELSE
  4737.                 IF CGSortLibArr(NormalizeVectorTo_minmax.max) <> 0 THEN
  4738.                     NVT_ScaleMultiplier = CGSortLibArr(NormalizeVectorTo_minmax.max)
  4739.                 ELSE
  4740.                     EXIT SUB
  4741.                 END IF
  4742.             END IF
  4743.             DO UNTIL NVT_ScaleMultiplier = INT(NVT_ScaleMultiplier)
  4744.                 NVT_ScaleMultiplier = NVT_ScaleMultiplier * 2
  4745.             LOOP
  4746.         END IF
  4747.     ELSE
  4748.         '* monotonic
  4749.         IF CGSortLibArr(start&) <> 0 THEN
  4750.             FOR s& = start& TO finish&
  4751.                 CGSortLibArr(s&) = 1
  4752.             NEXT
  4753.             NVT_ScaleMultiplier = CGSortLibArr(start&)
  4754.         END IF
  4755.     END IF
  4756.  
  4757. '* assumes CGBI_element_d >=0
  4758. '* returns CGBI_element_d as bit-inverted version of CGBI_element_d
  4759. SUB CGBitInvert_element (CGBI_element_d AS _UNSIGNED _INTEGER64)
  4760.     STATIC CGBI_ShiftTable%
  4761.     STATIC ui64_shift(0 TO 63) AS _UNSIGNED _INTEGER64
  4762.     IF CGBI_ShiftTable% = 0 THEN
  4763.         DIM CGBIO_s AS INTEGER
  4764.         ui64_shift(0) = 1
  4765.         FOR cgbi_s = 1 TO LEN(ui64_shift(0)) * 8 - 1
  4766.             ui64_shift(cgbi_s) = ui64_shift(cgbi_s - 1) * 2
  4767.         NEXT
  4768.         CGBI_ShiftTable% = -1
  4769.     END IF
  4770.     DIM CGBI_element_r AS DOUBLE: CGBI_element_r = 0
  4771.     DIM CGBI_element_s AS INTEGER
  4772.     DIM CGBI_BitsInElement AS INTEGER
  4773.     CGBI_BitsInElement = LEN(CGBI_element_d) * 8 - 1
  4774.     DO
  4775.         IF CGBI_element_d < 0 THEN
  4776.             IF CGBI_element_d > ui64_shift(CGBI_element_s) THEN
  4777.                 CGBI_element_s = CGBI_element_s - 1
  4778.             ELSE
  4779.                 CGBI_element_d = CGBI_element_d + ui64_shift(CGBI_element_s)
  4780.                 CGBI_element_r = CGBI_element_r + ui64_shift(CGBI_element_s)
  4781.             END IF
  4782.         ELSEIF CGBI_element_d > 0 THEN
  4783.             IF CGBI_element_d < ui64_shift(cgb_element_s) THEN
  4784.                 CGBI_element_s = CGBI_element_s - 1
  4785.             ELSE
  4786.                 CGBI_element_d = CGBI_element_d - ui64_shift(CGBI_BitsInElement - CGBI_element_s)
  4787.                 CGBI_element_r = CGBI_element_r + uinsi64_shift(CGBI_BitsInElement - CGBI_element_s)
  4788.             END IF
  4789.         ELSE
  4790.             EXIT DO
  4791.         END IF
  4792.     LOOP
  4793.     CGBI_element_d = CGBI_element_r
  4794.  
  4795.     DIM x AS DOUBLE: x = d
  4796.     p% = 0
  4797.     DO
  4798.         IF x = INT(x) THEN
  4799.             EXIT DO
  4800.         ELSE
  4801.             p% = p% + 1
  4802.             x = x * 10
  4803.         END IF
  4804.     LOOP
  4805.     CGBitInvert_element x
  4806.     PRINT x
  4807.     _DELAY 10
  4808.  
  4809.  
  4810. '*********************
  4811. '* Timsort, slightly modified and highly simplified
  4812. '* O(NLogN) complexity and at 8388608 elements in 24s make this a good, fast stable sort.
  4813. '*********************
  4814.  
  4815. SUB TimSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4816.     DIM minrun AS LONG: minrun = 32
  4817.     IF finish - start < minrun THEN
  4818.         InsertionSortBinary CGSortLibArr(), start, finish, order&
  4819.     ELSE
  4820.         DIM TimSort_Local_size AS LONG: TimSort_Local_size = minrun
  4821.         DIM TimSort_Local_i AS LONG
  4822.         DIM TimSort_local_left AS LONG
  4823.         DIM TimSort_local_mid AS LONG
  4824.         DIM TimSort_local_right AS LONG
  4825.         FOR TimSort_Local_i = start TO finish - minrun + 1 STEP minrun
  4826.             InsertionSortBinary CGSortLibArr(), TimSort_Local_i, TimSort_Local_i + minrun - 1, order&
  4827.         NEXT
  4828.         IF TimSort_Local_i < finish THEN
  4829.             InsertionSortBinary CGSortLibArr(), TimSort_Local_i, finish, order&
  4830.         END IF
  4831.         DO
  4832.             TimSort_local_left = start
  4833.             TimSort_local_mid = TimSort_local_left + TimSort_Local_size - 1
  4834.             DO
  4835.                 IF TimSort_local_mid + TimSort_Local_size > finish - TimSort_Local_size THEN
  4836.                     Tim_merge CGSortLibArr(), TimSort_local_left, TimSort_local_mid, finish, order&
  4837.                     EXIT DO
  4838.                 ELSE
  4839.                     TimSort_local_right = TimSort_local_mid + TimSort_Local_size
  4840.                     Tim_merge CGSortLibArr(), TimSort_local_left, TimSort_local_mid, TimSort_local_right, order&
  4841.                 END IF
  4842.                 TimSort_local_left = TimSort_local_left + 2 * TimSort_Local_size
  4843.                 TimSort_local_mid = TimSort_local_left + TimSort_Local_size - 1
  4844.             LOOP
  4845.             TimSort_Local_size = TimSort_Local_size * 2
  4846.         LOOP UNTIL start + TimSort_Local_size > finish
  4847.     END IF
  4848.  
  4849. SUB Tim_merge (CGSortLibArr() AS DOUBLE, left AS LONG, middle AS LONG, right AS LONG, order&)
  4850.     DIM Tim_Merge_LenLeft AS LONG
  4851.     DIM Tim_Merge_LenRight AS LONG
  4852.     DIM Tim_Merge_i AS LONG
  4853.     DIM Tim_Merge_J AS LONG
  4854.     DIM Tim_Merge_k AS LONG
  4855.     Tim_Merge_LenLeft = middle - left + 1
  4856.     Tim_Merge_LenRight = right - middle
  4857.     DIM array_left(0 TO Tim_Merge_LenLeft - 1) AS DOUBLE
  4858.     DIM array_right(0 TO Tim_Merge_LenRight - 1) AS DOUBLE
  4859.  
  4860.     '* load up left side (lower half in left) (start ... middle)
  4861.     FOR Tim_Merge_i = 0 TO Tim_Merge_LenLeft - 1
  4862.         array_left(Tim_Merge_i) = CGSortLibArr(left + Tim_Merge_i)
  4863.     NEXT
  4864.  
  4865.     '* load up right side (upper half in left) (middle + 1 ... finish)
  4866.     FOR Tim_Merge_i = 0 TO Tim_Merge_LenRight - 1
  4867.         array_right(Tim_Merge_i) = CGSortLibArr(middle + Tim_Merge_i + 1)
  4868.     NEXT
  4869.  
  4870.     Tim_Merge_i = 0
  4871.     Tim_Merge_J = 0
  4872.     Tim_Merge_k = left
  4873.     IF order& = 1 THEN
  4874.         WHILE (Tim_Merge_i < Tim_Merge_LenLeft AND Tim_Merge_J < Tim_Merge_LenRight)
  4875.             IF (array_left(Tim_Merge_i) <= array_right(Tim_Merge_J)) THEN
  4876.                 CGSortLibArr(Tim_Merge_k) = array_left(Tim_Merge_i)
  4877.                 Tim_Merge_i = Tim_Merge_i + 1
  4878.             ELSE
  4879.                 CGSortLibArr(Tim_Merge_k) = array_right(Tim_Merge_J)
  4880.                 Tim_Merge_J = Tim_Merge_J + 1
  4881.             END IF
  4882.             Tim_Merge_k = Tim_Merge_k + 1
  4883.         WEND
  4884.     ELSE
  4885.         WHILE (Tim_Merge_i < Tim_Merge_LenLeft AND Tim_Merge_J < Tim_Merge_LenRight)
  4886.             IF (array_left(Tim_Merge_i) >= array_right(Tim_Merge_J)) THEN
  4887.                 CGSortLibArr(Tim_Merge_k) = array_left(Tim_Merge_i)
  4888.                 Tim_Merge_i = Tim_Merge_i + 1
  4889.             ELSE
  4890.                 CGSortLibArr(Tim_Merge_k) = array_right(Tim_Merge_J)
  4891.                 Tim_Merge_J = Tim_Merge_J + 1
  4892.             END IF
  4893.             Tim_Merge_k = Tim_Merge_k + 1
  4894.         WEND
  4895.     END IF
  4896.  
  4897.     WHILE (Tim_Merge_i < Tim_Merge_LenLeft)
  4898.         CGSortLibArr(Tim_Merge_k) = array_left(Tim_Merge_i)
  4899.         Tim_Merge_k = Tim_Merge_k + 1
  4900.         Tim_Merge_i = Tim_Merge_i + 1
  4901.     WEND
  4902.  
  4903.     WHILE (Tim_Merge_J < Tim_Merge_LenRight)
  4904.         CGSortLibArr(Tim_Merge_k) = array_right(Tim_Merge_J)
  4905.         Tim_Merge_k = Tim_Merge_k + 1
  4906.         Tim_Merge_J = Tim_Merge_J + 1
  4907.     WEND
  4908.     ERASE array_left
  4909.     ERASE array_right
  4910.  
  4911. 'SUB GnomeSort (array() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4912. '    '* LOCATE 40, 1: PRINT USING "#####.################"; TIMER(.001);
  4913. '    DIM GnomeSort_I AS LONG
  4914. '    SELECT CASE order&
  4915. '        CASE 1
  4916. '            GnomeSort_I = start + 1
  4917. '            DO UNTIL GnomeSort_I > finish
  4918. '                IF (array(GnomeSort_I - 1) <= array(GnomeSort_I)) THEN
  4919. '                    GnomeSort_I = GnomeSort_I + 1
  4920. '                ELSE
  4921. '                    SWAP array(GnomeSort_I), array(GnomeSort_I - 1)
  4922. '                    GnomeSort_I = GnomeSort_I - 1
  4923. '                    IF (GnomeSort_I < start + 1) THEN
  4924. '                        GnomeSort_I = start + 1
  4925. '                    END IF
  4926. '                END IF
  4927. '            LOOP
  4928. '        CASE ELSE
  4929. '            GnomeSort_I = start + 1
  4930. '            DO UNTIL GnomeSort_I > finish
  4931. '                IF (array(GnomeSort_I - 1) >= array(GnomeSort_I)) THEN
  4932. '                    GnomeSort_I = GnomeSort_I + 1
  4933. '                ELSE
  4934. '                    SWAP array(GnomeSort_I), array(GnomeSort_I - 1)
  4935. '                    GnomeSort_I = GnomeSort_I - 1
  4936. '                    IF (GnomeSort_I < start + 1) THEN
  4937. '                        GnomeSort_I = start + 1
  4938. '                    END IF
  4939. '                END IF
  4940. '            LOOP
  4941. '    END SELECT
  4942. '    '* LOCATE 40, 1: PRINT USING "#####.################"; TIMER(.001);
  4943. 'END SUB
  4944.  
  4945. SUB GnomeSort (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4946.     DIM Gnome_i AS LONG
  4947.     DIM Gnome_j AS LONG
  4948.     SELECT CASE order&
  4949.         CASE 1
  4950.             Gnome_i = start + 1
  4951.             Gnome_j = Gnome_i + 1
  4952.             WHILE (Gnome_i < finish - start)
  4953.                 IF CGSortLibArray(Gnome_i - 1) <= CGSortLibArray(Gnome_i) THEN
  4954.                     Gnome_i = Gnome_j
  4955.                     Gnome_j = Gnome_j + 1
  4956.                 ELSE
  4957.                     SWAP CGSortLibArray(Gnome_i - 1), CGSortLibArray(Gnome_i)
  4958.                     Gnome_i = Gnome_i - 1
  4959.                     IF Gnome_i < start + 1 THEN
  4960.                         Gnome_i = Gnome_j
  4961.                         Gnome_j = Gnome_j + 1
  4962.                     END IF
  4963.                 END IF
  4964.             WEND
  4965.         CASE ELSE
  4966.             Gnome_i = start + 1
  4967.             Gnome_j = Gnome_i + 1
  4968.             WHILE (Gnome_i < finish - start)
  4969.                 IF CGSortLibArray(Gnome_i - 1) >= CGSortLibArray(Gnome_i) THEN
  4970.                     Gnome_i = Gnome_j
  4971.                     Gnome_j = Gnome_j + 1
  4972.                 ELSE
  4973.                     SWAP CGSortLibArray(Gnome_i - 1), CGSortLibArray(Gnome_i)
  4974.                     Gnome_i = Gnome_i - 1
  4975.                     IF Gnome_i < start + 1 THEN
  4976.                         Gnome_i = Gnome_j
  4977.                         Gnome_j = Gnome_j + 1
  4978.                     END IF
  4979.                 END IF
  4980.             WEND
  4981.     END SELECT
  4982.  
  4983. 'SUB CountingSort (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  4984. '    DIM CSmmrec AS MinMaxRec
  4985. '    GetMinMaxArray CGSortLibArray(), start, finish, CSmmrec
  4986. '    DIM pow2_shift AS DOUBLE: pow2_shift = 1
  4987. '    DIM XInsert AS LONG: XInsert = 0
  4988. '    IF CGSortLibArray(CSmmrec.min) <> CGSortLibArray(CSmmrec.max) THEN
  4989. '        ArrayIsInteger CGSortLibArray(), start, finish, ErrIndex&, IsIntegers&
  4990. '        IF IsIntegers& THEN
  4991. '            '* no scaling needed
  4992. '            IF start = LBOUND(CGSortLibArray) THEN
  4993. '                IF finish = UBOUND(CGSortLibArray) THEN
  4994. '                    redimc& = -1
  4995. '                ELSE
  4996. '                    redimc& = 0
  4997. '                END IF
  4998. '            ELSE
  4999. '                redimc& = 0
  5000. '            END IF
  5001.  
  5002. '            IF redimc& THEN
  5003. '                REDIM Counts(0 TO CGSortLibArray(CSmmrec.max) - CGSortLibArray(CSmmrec.min)) AS LONG
  5004. '                FOR scanarrayp& = start TO finish
  5005. '                    Counts(CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min)) = Counts(CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min)) + 1
  5006. '                NEXT
  5007. '                IF order& = 1 THEN
  5008. '                    XInsert = start
  5009. '                    FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5010. '                        IF Counts(scanarrayp&) > 0 THEN
  5011. '                            FOR u& = 0 TO Counts(scanarrayp&) - 1
  5012. '                                CGSortLibArray(XInsert) = scanarrayp& + CGSortLibArray(CSmmrec.min)
  5013. '                                XInsert = XInsert + 1
  5014. '                            NEXT
  5015. '                        END IF
  5016.  
  5017. '                    NEXT
  5018. '                ELSE
  5019. '                    XInsert = finish
  5020. '                    FOR scanarrayp& = UBOUND(counts) TO LBOUND(counts) STEP -1
  5021. '                        IF Counts(scanarrayp&) > 0 THEN
  5022. '                            FOR u& = Counts(scanarrayp&) - 1 TO 0 STEP -1
  5023. '                                CGSortLibArray(XInsert) = scanarrayp& + CGSortLibArray(CSmmrec.min)
  5024. '                                XInsert = XInsert - 1
  5025. '                            NEXT
  5026. '                        END IF
  5027. '                    NEXT
  5028. '                END IF
  5029. '                FOR x& = start TO finish
  5030. '                    PRINT CGSortLibArray(x&);
  5031. '                NEXT
  5032. '            ELSE
  5033. '                FOR scanarrayp& = start TO finish
  5034. '                    Counts(CGSortLibArray(scanarrayp&) - MinArray) = Counts(CGSortLibArray(scanarrayp&) - MinArray) + 1
  5035. '                NEXT
  5036. '                FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5037. '                    FOR u& = 0 TO Counts(scanarrayp&) - 1
  5038. '                        CGSortLibArray(XInsert) = scanarrayp& + MinArray
  5039. '                        XInsert = XInsert + 1
  5040. '                    NEXT
  5041. '                    '* REDIM _PRESERVE Counts(scanarrayp& TO finish) AS LONG
  5042. '                NEXT
  5043. '            END IF
  5044. '            '* clear the Counts() array
  5045. '            ERASE Counts
  5046. '        ELSE
  5047. '            DIM t AS DOUBLE
  5048. '            IF CGSortLibArray(CSmmrec.min) <> 0 THEN
  5049. '                t = CGSortLibArray(CSmmrec.min)
  5050. '            ELSE
  5051. '                t = CGSortLibArray(CSmmrec.max)
  5052. '            END IF
  5053. '            POW2_SHIFT& = 1
  5054. '            DO UNTIL t = INT(t)
  5055. '                t = t * 2
  5056. '                pow2_shift = pow2_shift * 2
  5057. '            LOOP
  5058. '            REDIM Counts(0 TO pow2_shift * (CGSortLibArray(CSmmrec.max) - CGSortLibArray(CSmmrec.min)))
  5059. '            FOR scanarrayp& = start TO finish
  5060. '                x& = pow2_shift * (CGSortLibArray(scanarray&) - MinArray)
  5061. '                Counts(x&) = Counts(x&) + 1
  5062. '            NEXT
  5063. '            IF order& = 1 THEN
  5064. '                FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5065. '                    FOR u& = 0 TO Counts(scanarrayp&) - 1
  5066. '                        CGSortLibArray(XInsert) = scanarrayp& + MinArray
  5067. '                        XInsert = XInsert + 1
  5068. '                    NEXT
  5069. '                    '* REDIM _PRESERVE Counts(scanarrayp& TO finish) AS LONG
  5070. '                NEXT
  5071. '            ELSE
  5072. '                FOR scanarrayp& = UBOUND(counts) TO LBOUND(counts) STEP -1
  5073. '                    FOR u& = Counts(scanarrayp&) - 1 TO 0 STEP -1
  5074. '                        CGSortLibArray(XInsert) = scanarrayp& + MinArray
  5075. '                        XInsert = XInsert - 1
  5076. '                    NEXT
  5077. '                    '* REDIM _PRESERVE Counts(scanarrayp& TO finish) AS LONG
  5078. '                NEXT
  5079. '            END IF
  5080. '            ERASE Counts
  5081. '        END IF
  5082. '    END IF
  5083. 'END SUB
  5084.  
  5085. 'SUB CountingSortInteger (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5086. '    DIM CSmmrec AS MinMaxRec
  5087. '    GetMinMaxArray CGSortLibArray(), start, finish, CSmmrec
  5088. '    DIM XInsert AS LONG: XInsert = 0
  5089. '    IF CGSortLibArray(CSmmrec.min) <> CGSortLibArray(CSmmrec.max) THEN
  5090. '        REDIM Counts(CGSortLibArray(CSmmrec.min) TO CGSortLibArray(CSmmrec.max)) AS LONG
  5091. '        FOR scanarrayp& = start TO finish
  5092. '            Counts(CLNG(CGSortLibArray(scanarrayp&))) = Counts(CLNG(CGSortLibArray(scanarrayp&))) + 1
  5093. '        NEXT
  5094. '        IF order& = 1 THEN
  5095. '            XInsert = start
  5096. '            FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5097. '                FOR u& = 0 TO Counts(scanarrayp&) - 1
  5098. '                    CGSortLibArray(XInsert) = scanarrayp&
  5099. '                    XInsert = XInsert + 1
  5100. '                NEXT
  5101. '            NEXT
  5102. '        ELSE
  5103. '            XInsert = start
  5104. '            FOR scanarrayp& = UBOUND(counts) TO LBOUND(counts) STEP -1
  5105. '                FOR u& = 0 TO Counts(scanarrayp&) - 1
  5106. '                    CGSortLibArray(XInsert) = scanarrayp&
  5107. '                    XInsert = XInsert + 1
  5108. '                NEXT
  5109. '            NEXT
  5110. '        END IF
  5111. '        '* clear the Counts() array
  5112. '        ERASE Counts
  5113. '    END IF
  5114. 'END SUB
  5115.  
  5116. SUB CountingSortInteger (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5117.     ArrayIsInteger CGSortLibArray(), start, finish, FirstNonIntegerElement&, errcon&
  5118.     IF errcon& THEN
  5119.         CountingSortNonInteger CGSortLibArray(), start, finish, order&
  5120.     ELSE
  5121.         DIM CSmmrec AS MinMaxRec
  5122.         CSmmrec.min = start
  5123.         CSmmrec.max = finish
  5124.         FOR scanarrayp& = start TO finish
  5125.             IF CGSortLibArray(scanarrayp&) < CGSortLibArray(CSmmrec.min) THEN CSmmrec.min = scanarrayp&
  5126.             IF CGSortLibArray(scanarrayp&) > CGSortLibArray(CSmmrec.max) THEN CSmmrec.max = scanarrayp&
  5127.         NEXT
  5128.         DIM XInsert AS LONG: XInsert = 0
  5129.         IF CGSortLibArray(CSmmrec.min) <> CGSortLibArray(CSmmrec.max) THEN
  5130.             '* PRINT CGSortLibArray(CSmmrec.min); CGSortLibArray(CSmmrec.max)
  5131.             '* no scaling needed
  5132.             DIM cgslam AS DOUBLE: cgslam = CGSortLibArray(CSmmrec.min)
  5133.             REDIM Counts(0 TO CGSortLibArray(CSmmrec.max) - CGSortLibArray(CSmmrec.min)) AS LONG
  5134.             FOR scanarrayp& = start TO finish
  5135.                 Counts(CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min)) = Counts(CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min)) + 1
  5136.             NEXT
  5137.             IF order& = 1 THEN
  5138.                 XInsert = start
  5139.                 FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5140.                     FOR u& = 0 TO Counts(scanarrayp&) - 1
  5141.                         CGSortLibArray(XInsert) = scanarrayp& + cgslam
  5142.                         XInsert = XInsert + 1
  5143.                     NEXT
  5144.                 NEXT
  5145.             ELSE
  5146.                 XInsert = start
  5147.                 FOR scanarrayp& = UBOUND(counts) TO LBOUND(counts) STEP -1
  5148.                     FOR u& = 0 TO Counts(scanarrayp&) - 1
  5149.                         CGSortLibArray(XInsert) = scanarrayp& + cgslam
  5150.                         XInsert = XInsert + 1
  5151.                     NEXT
  5152.                 NEXT
  5153.             END IF
  5154.             '* clear the Counts() array
  5155.             ERASE Counts
  5156.         END IF
  5157.     END IF
  5158.  
  5159.  
  5160. '************************************
  5161. '* CountingSort() extended to non-integer
  5162. '* complexity class: O(N) -- Typical throughput: 600,000 double-precision/GHzS
  5163. '************************************
  5164. SUB CountingSortNonInteger (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5165.     DIM CSmmrec AS MinMaxRec
  5166.     GetMinMaxArray CGSortLibArray(), start, finish, CSmmrec
  5167.     DIM XInsert AS LONG: XInsert = 0
  5168.     IF CGSortLibArray(CSmmrec.min) <> CGSortLibArray(CSmmrec.max) THEN
  5169.         FindNonZeroElement CGSortLibArray(), start, finish, ascale#
  5170.         IF ascale# * (CGSortLibArray(CSmmrec.max) - CGSortLibArray(CSmmrec.min)) > (2 ^ 32) / (LEN(CGSortLibArray(start))) THEN
  5171.             MergeSortEmerge CGSortLibArray(), start, finish, order&
  5172.         ELSE
  5173.             DIM cgslam AS DOUBLE: cgslam = CGSortLibArray(CSmmrec.min)
  5174.             cgslam = CGSortLibArray(CSmmrec.min)
  5175.             REDIM Counts(0 TO ascale# * (CGSortLibArray(CSmmrec.max) - CGSortLibArray(CSmmrec.min))) AS LONG
  5176.             FOR scanarrayp& = start TO finish
  5177.                 Counts(ascale# * (CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min))) = Counts(ascale# * (CGSortLibArray(scanarrayp&) - CGSortLibArray(CSmmrec.min))) + 1
  5178.             NEXT
  5179.             IF order& = 1 THEN
  5180.                 XInsert = start
  5181.                 FOR scanarrayp& = LBOUND(counts) TO UBOUND(counts)
  5182.                     FOR u& = 0 TO Counts(scanarrayp&) - 1
  5183.                         CGSortLibArray(XInsert) = scanarrayp& + cgslam
  5184.                         XInsert = XInsert + 1
  5185.                     NEXT
  5186.                 NEXT
  5187.             ELSE
  5188.                 XInsert = start
  5189.                 FOR scanarrayp& = UBOUND(counts) TO LBOUND(counts) STEP -1
  5190.                     FOR u& = 0 TO Counts(scanarrayp&) - 1
  5191.                         CGSortLibArray(XInsert) = scanarrayp& + cgslam
  5192.                         XInsert = XInsert + 1
  5193.                     NEXT
  5194.                 NEXT
  5195.             END IF
  5196.             '* clear the Counts() array
  5197.             ERASE Counts
  5198.         END IF
  5199.     END IF
  5200.  
  5201. '***********************************
  5202. '* finds the lowest power of 2 when multiplied by each array element yields an integer result
  5203. '***********************************
  5204. SUB FindNonZeroElement (Cg() AS DOUBLE, start AS LONG, finish AS LONG, FindNZOScale AS DOUBLE)
  5205.     FindNZOScale = 1
  5206.     DIM find_nzo AS LONG
  5207.     DIM LowestNonZeroElement AS LONG: LowestNonZeroElement = start - 1
  5208.     DIM highestNonZeroElement AS LONG: highestNonZeroElement = start - 1
  5209.     DIM curhigp AS DOUBLE: curhighp = 1
  5210.     FOR find_nzo = start TO finish
  5211.         IF Cg(find_nzo) <> 0 THEN
  5212.             IF LowestNonZeroElement > start - 1 THEN
  5213.                 IF Cg(find_nzo) < Cg(LowestNonZeroElement) THEN
  5214.                     LowestNonZeroElement = find_nzo
  5215.                 END IF
  5216.                 IF Cg(find_nzo) > Cg(LowestNonZeroElement) THEN
  5217.                     highestNonZeroElement = find_nzo
  5218.                 END IF
  5219.             ELSE
  5220.                 LowestNonZeroElement = find_nzo
  5221.                 highestNonZeroElement = find_nzo
  5222.             END IF
  5223.             curhighp = 1
  5224.             DO UNTIL curhighp * Cg(find_nzo) = INT(curhighp * Cg(find_nzo))
  5225.                 curhighp = curhighp * 2
  5226.             LOOP
  5227.             IF curhighp > FindNZOScale THEN
  5228.                 FindNZOScale = curhighp
  5229.             END IF
  5230.         END IF
  5231.     NEXT
  5232.  
  5233. '* from this:
  5234. '#define BEAD(i, j) beads[i * max + j]
  5235.  
  5236. '// function to perform the above algorithm
  5237.  
  5238. 'void beadSort(int *a, int len)
  5239.  
  5240. '{
  5241.  
  5242. '        // Find the maximum element
  5243.  
  5244. '        int max = a[start];
  5245.  
  5246. '        for (int i = 1; i < len; i++)
  5247.  
  5248. '                if (a[i] > max)
  5249.  
  5250. '                      max = a[i];
  5251.  
  5252.  
  5253.  
  5254. '        // allocating memory
  5255.  
  5256. '        unsigned char beads[max*len];
  5257.  
  5258. '        memset(beads, 0, sizeof(beads));
  5259.  
  5260.  
  5261.  
  5262. '        // mark the beads
  5263.  
  5264. '        for (int i = 0; i < len; i++)
  5265.  
  5266. '                for (int j = 0; j < a[i]; j++)
  5267.  
  5268. '                        BEAD(i, j) = 1;
  5269.  
  5270.  
  5271.  
  5272. '        for (int j = 0; j < max; j++)
  5273.  
  5274. '        {
  5275.  
  5276. '                // count how many beads are on each post
  5277.  
  5278. '                int sum = 0;
  5279.  
  5280. '                for (int i=0; i < len; i++)
  5281.  
  5282. '                {
  5283.  
  5284. '                        sum += BEAD(i, j);
  5285.  
  5286. '                        BEAD(i, j) = 0;
  5287.  
  5288. '                }
  5289.  
  5290.  
  5291.  
  5292. '                // Move beads down
  5293.  
  5294. '                for (int i = len - sum; i < len; i++)
  5295.  
  5296. '                        BEAD(i, j) = 1;
  5297.  
  5298. '        }
  5299.  
  5300.  
  5301.  
  5302. '        // Put sorted values in array using beads
  5303.  
  5304. '        for (int i = 0; i < len; i++)
  5305.  
  5306. '        {
  5307.  
  5308. '                int j;
  5309.  
  5310. '                for (j = 0; j < max && BEAD(i, j); j++);
  5311.  
  5312.  
  5313.  
  5314. '                a[i] = j;
  5315.  
  5316. '        }
  5317.  
  5318. '}
  5319.  
  5320.  
  5321. '* to this:
  5322. '***************************************************
  5323. '* BeadSortInteger() is VERY fast. Typical performance is O(N), meaning only a constant extra
  5324. '* time per additional element. There was no QB64 code for this sort, so I whipped one up from
  5325. '* a c++ example. Translated, tested and such 06 Apr 2018. Everyone seems to think arrays always
  5326. '* need to be manipulated across their entire length. Sometimes a partial is all that's really
  5327. '* necessary. BeadSort performs in O(NlogN) (roughly same as quicksort -- WORST case. Usually O(n).
  5328. '* As key values (array values and ranges) go up, performance remains steady and predictable although
  5329. '* at larger bit ranges, it slows appreciably but still performs categorically O(NLogN), with a higher
  5330. '* constant. excellent for all integer array numerical distributions provided there is enough memory.
  5331. '* Integer/positive only at this point. Throughput is roughly 1000k+/GHzS for double-precision. This
  5332. '* is Related to CountingSort(). So far only useful for integers but I'm working on a modification
  5333. '* like I did to CountingSort() so it can be used with non-integer arrays as well.
  5334. '***************************************************
  5335. SUB BeadSortInteger (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5336.     DIM BeadSort_MAX AS DOUBLE: BeadSort_MAX = CGSortLibArray(start)
  5337.     DIM BeadSort_Sum AS DOUBLE
  5338.     DIM BeadSort_I AS LONG
  5339.     DIM BeadSort_J AS LONG
  5340.  
  5341.     FOR BeadSort_I = start + 1 TO (finish - start)
  5342.         IF (CGSortLibArray(BeadSort_I) > BeadSort_MAX) THEN BeadSort_MAX = CGSortLibArray(BeadSort_I)
  5343.     NEXT
  5344.  
  5345.     REDIM beads((finish - start), BeadSort_MAX) AS _UNSIGNED _BIT
  5346.     FOR BeadSort_I = 0 TO (finish - start) - 1
  5347.         FOR BeadSort_J = 0 TO CGSortLibArray(BeadSort_I) - 1
  5348.             beads(BeadSort_I, BeadSort_J) = 1
  5349.         NEXT
  5350.     NEXT
  5351.  
  5352.     IF order& = 1 THEN
  5353.         FOR BeadSort_J = 0 TO BeadSort_MAX
  5354.             BeadSort_Sum = 0
  5355.             FOR BeadSort_I = 0 TO (finish - start)
  5356.                 BeadSort_Sum = BeadSort_Sum + beads(BeadSort_I, BeadSort_J)
  5357.                 beads(BeadSort_I, BeadSort_J) = 0
  5358.             NEXT
  5359.             FOR BeadSort_I = (finish - start) - BeadSort_Sum TO (finish - start)
  5360.                 beads(BeadSort_I, BeadSort_J) = 1
  5361.             NEXT
  5362.         NEXT
  5363.         FOR BeadSort_I = 0 TO (finish - start)
  5364.             BeadSort_J = 0
  5365.             WHILE BeadSort_J < BeadSort_MAX AND beads(BeadSort_I, BeadSort_J)
  5366.                 BeadSort_J = BeadSort_J + 1
  5367.             WEND
  5368.             CGSortLibArray(BeadSort_I) = BeadSort_J
  5369.         NEXT
  5370.     ELSE
  5371.         FOR BeadSort_J = BeadSort_MAX TO 0 STEP -1
  5372.             BeadSort_Sum = 0
  5373.             FOR BeadSort_I = 0 TO (finish - start)
  5374.                 BeadSort_Sum = BeadSort_Sum + beads(BeadSort_I, BeadSort_J)
  5375.                 beads(BeadSort_I, BeadSort_J) = 0
  5376.             NEXT
  5377.             FOR BeadSort_I = (finish - start) TO (finish - start) - BeadSort_Sum STEP -1
  5378.                 beads(BeadSort_I, BeadSort_J) = 1
  5379.             NEXT
  5380.         NEXT
  5381.         FOR BeadSort_I = 0 TO (finish - start)
  5382.             BeadSort_J = 0
  5383.             WHILE BeadSort_J < max AND beads(BeadSort_I, BeadSort_J)
  5384.                 BeadSort_J = BeadSort_J + 1
  5385.             WEND
  5386.             CGSortLibArray(finish - BeadSort_I) = BeadSort_J
  5387.         NEXT
  5388.     END IF
  5389.  
  5390. '***************************************************
  5391. '* BeadSortInteger() is VERY fast. Typical performance is O(N), meaning only a constant extra
  5392. '* time per additional element. There was no QB64 code for this sort, so I whipped one up from
  5393. '* a c++ example. Translated, tested and such 06 Apr 2018. Everyone seems to think arrays always
  5394. '* need to be manipulated across their entire length. Sometimes a partial is all that's really
  5395. '* necessary. BeadSort performs in O(NlogN) (roughly same as quicksort -- WORST case. Usually O(n).
  5396. '* As key values (array values and ranges) go up, performance remains steady and predictable although
  5397. '* at larger bit ranges, it slows appreciably but still performs categorically O(NLogN), with a higher
  5398. '* constant. excellent for all integer array numerical distributions provided there is enough memory.
  5399. '* Integer/positive only at this point. Throughput is roughly 1000k+/GHzS for double-precision. This
  5400. '* is Related to CountingSort(). So far only useful for integers but I'm working on a modification
  5401. '* like I did to CountingSort() so it can be used with non-integer arrays as well.
  5402. '***************************************************
  5403. SUB BeadSortNonInteger (CGSortLibArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5404.     DIM mmrec AS MinMaxRec
  5405.     mmrec.min = start
  5406.     mmrec.max = start
  5407.     FOR u& = start TO finish
  5408.         IF CGSortLibArray(u&) < CGSortLibArray(mmrec.min) THEN mmrec.min = u&
  5409.         IF CGSortLibArray(u&) > CGSortLibArray(mmrec.max) THEN mmrec.max = u&
  5410.     NEXT
  5411.     DIM BSNI_dmin AS DOUBLE
  5412.     DIM BSNIScale AS DOUBLE
  5413.     DIM BeadSort_MAX AS DOUBLE: BeadSort_MAX = CGSortLibArray(mmrec.max)
  5414.     BSNI_dmin = CGSortLibArray(mmrec.min)
  5415.     FindNonZeroElement CGSortLibArray(), start, finish, BSNIScale
  5416.     IF BSNIScale > 1 THEN
  5417.         FOR u& = start TO finish
  5418.             CGSortLibArray(u&) = (CGSortLibArray(u&) - BSNI_dmin) * BSNIScale
  5419.         NEXT
  5420.     END IF
  5421.  
  5422.     DIM BeadSort_Sum AS _INTEGER64
  5423.     DIM BeadSort_I AS _INTEGER64
  5424.     DIM BeadSort_J AS _INTEGER64
  5425.  
  5426.     REDIM beads((finish - start), BeadSort_MAX) AS _UNSIGNED _BIT
  5427.     FOR BeadSort_I = 0 TO (finish - start) - 1
  5428.         FOR BeadSort_J = 0 TO CGSortLibArray(BeadSort_I) - 1
  5429.             beads(BeadSort_I, BeadSort_J) = 1
  5430.         NEXT
  5431.     NEXT
  5432.  
  5433.     IF order& = 1 THEN
  5434.         FOR BeadSort_J = 0 TO BeadSort_MAX
  5435.             BeadSort_Sum = 0
  5436.             FOR BeadSort_I = 0 TO (finish - start)
  5437.                 BeadSort_Sum = BeadSort_Sum + beads(BeadSort_I, BeadSort_J)
  5438.                 beads(BeadSort_I, BeadSort_J) = 0
  5439.             NEXT
  5440.             FOR BeadSort_I = (finish - start) - BeadSort_Sum TO (finish - start)
  5441.                 beads(BeadSort_I, BeadSort_J) = 1
  5442.             NEXT
  5443.         NEXT
  5444.         FOR BeadSort_I = 0 TO (finish - start)
  5445.             BeadSort_J = 0
  5446.             WHILE BeadSort_J < BeadSort_MAX AND beads(BeadSort_I, BeadSort_J)
  5447.                 BeadSort_J = BeadSort_J + 1
  5448.             WEND
  5449.             CGSortLibArray(BeadSort_I) = BeadSort_J
  5450.         NEXT
  5451.     ELSE
  5452.         FOR BeadSort_J = BeadSort_MAX TO 0 STEP -1
  5453.             BeadSort_Sum = 0
  5454.             FOR BeadSort_I = 0 TO (finish - start)
  5455.                 BeadSort_Sum = BeadSort_Sum + beads(BeadSort_I, BeadSort_J)
  5456.                 beads(BeadSort_I, BeadSort_J) = 0
  5457.             NEXT
  5458.             FOR BeadSort_I = (finish - start) TO (finish - start) - BeadSort_Sum STEP -1
  5459.                 beads(BeadSort_I, BeadSort_J) = 1
  5460.             NEXT
  5461.         NEXT
  5462.         FOR BeadSort_I = 0 TO (finish - start)
  5463.             BeadSort_J = 0
  5464.             WHILE BeadSort_J < max AND beads(BeadSort_I, BeadSort_J)
  5465.                 BeadSort_J = BeadSort_J + 1
  5466.             WEND
  5467.             CGSortLibArray(finish - BeadSort_I) = BeadSort_J
  5468.         NEXT
  5469.     END IF
  5470.  
  5471.  
  5472.  
  5473.  
  5474.  
  5475. SUB PancakeSort (strawberries() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5476.     IF start < finish THEN
  5477.         DIM syrup AS MinMaxRec
  5478.         butter& = finish
  5479.         whippedcream& = start
  5480.         DO UNTIL butter& < whippedcream&
  5481.             GetMinMaxArray strawberries(), whippedcream&, butter&, syrup
  5482.             IF strawberries(syrup.max) > strawberries(butter&) THEN
  5483.                 StableInvert strawberries(), syrup.max, butter&, 1
  5484.             ELSE
  5485.                 IF strawberries(syrup.min) < strawberries(whippedcream&) THEN
  5486.                     StableInvert strawberries(), whippedcream&, syrup.min, 1
  5487.                 END IF
  5488.                 whippedcream& = whippedcream& + 1
  5489.             END IF
  5490.             butter& = butter& - 1
  5491.         LOOP
  5492.         IF order& <> 1 THEN
  5493.             StableInvert strawberries(), statrt, finish, 1
  5494.         END IF
  5495.     END IF
  5496.  
  5497.  
  5498.  
  5499.  
  5500.  
  5501.  
  5502. SUB AnalyzeArray (CG() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5503.     REDIM Inverted(start TO finish) AS DOUBLE
  5504.     REDIM InOrder(start TO finish) AS DOUBLE
  5505.     SELECT CASE order&
  5506.         CASE 1
  5507.             h& = start
  5508.             FOR q& = start TO finish
  5509.                 IF CG(q&) < CG(h&) THEN
  5510.                     SWAP CG(q&), CG(h&)
  5511.                     Inverted(Invertcount&) = CG(q&)
  5512.                     Invertcount& = Invertcount& + 1
  5513.                 ELSE
  5514.                     InOrder(inordercount&) = CG(q&)
  5515.                     inordercount& = inordercount& + 1
  5516.                 END IF
  5517.                 h& = q&
  5518.             NEXT
  5519.         CASE ELSE
  5520.     END SELECT
  5521.  
  5522. SUB OneZeroSort (cg() AS DOUBLE, start, finish, order&)
  5523.     DIM left AS LONG: left = start
  5524.     DIM right AS LONG: right = finish
  5525.     SELECT CASE order&
  5526.         CASE 1
  5527.             WHILE left < right
  5528.                 WHILE cg(left) = 0
  5529.                     left = left + 1
  5530.                 WEND
  5531.                 WHILE cg(right)
  5532.                     right = right - 1
  5533.                 WEND
  5534.                 IF left < right THEN
  5535.                     SWAP cg(left), cg(right)
  5536.                     left = left + 1
  5537.                     right = right - 1
  5538.                 END IF
  5539.             WEND
  5540.         CASE ELSE
  5541.             WHILE left < right
  5542.                 WHILE cg(left)
  5543.                     left = left + 1
  5544.                 WEND
  5545.                 WHILE cg(right) = 0
  5546.                     right = right - 1
  5547.                 WEND
  5548.                 IF left < right THEN
  5549.                     SWAP cg(left), cg(right)
  5550.                     left = left + 1
  5551.                     right = right - 1
  5552.                 END IF
  5553.             WEND
  5554.     END SELECT
  5555.  
  5556. '***************************
  5557. '* Another specialized numeric sort: It is asymmetric, meaning sortation from reverse-ordered
  5558. '* datasets takes roughly twice as long. Even in this case, it is faster than FlashSort. This
  5559. '* sort method is EXTREMELY fast when used within design constraints: namely, integer and
  5560. '* consecutive sequential. UniqueIntegerSort is between 3 and 4 times
  5561. '* as fast as FlashSort, the fastest general-purpose number-specific sort in this library.
  5562. '* works only under specific circumstances and not easily adaptable to nonnumeric string
  5563. '* complexity class: O(n).
  5564. '***************************
  5565. SUB UniqueIntegerSort (cgSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5566.     FOR c& = start TO finish
  5567.         DO UNTIL cgSortLibArr(c&) <= c& '* can be just = too.
  5568.             SWAP cgSortLibArr(c&), cgSortLibArr(cgSortLibArr(c&))
  5569.         LOOP
  5570.     NEXT
  5571.     IF order& <> 1 THEN
  5572.         '* this step corrects asymmetric performance
  5573.         '* since these are unique integers in a range,
  5574.         '* restabilization is unnecessary.
  5575.         StableInvert cgSortLibArr(), start, finish, 0
  5576.     END IF
  5577.  
  5578.     'IF order& = 1 THEN
  5579.     '    FOR c& = start TO finish
  5580.     '        DO UNTIL a(c&) = c&
  5581.     '            SWAP a(c&), a(a(c&))
  5582.     '        LOOP
  5583.     '    NEXT
  5584.     'ELSE
  5585.     '    FOR c& = start TO finish
  5586.     '        k& = finish - c&
  5587.     '        DO UNTIL a(c&) = k&
  5588.     '            SWAP a(c&), a(finish - a(c&))
  5589.     '        LOOP
  5590.     '    NEXT
  5591.     'END IF
  5592.  
  5593. '* do you need to know where an array of values balances? Good for balancing and constraint problems
  5594. '* such as maximum loading capacity.
  5595. SUB ApproximatelyEqualSums (cg() AS DOUBLE, start AS LONG, finish AS LONG, order&, PartitionIndex&)
  5596.     DIM lsum AS DOUBLE: lindex& = start
  5597.     DIM rsum AS DOUBLE: rindex& = finish
  5598.     lsum = 0
  5599.     rsum = 0
  5600.     SELECT CASE order&
  5601.         CASE -1, 1
  5602.             MergeSortEmerge cg(), start, finish, order&
  5603.             WHILE lindex& < rindex&
  5604.                 IF rsum < lsum THEN
  5605.                     rsum = rsum + cg(rindex&)
  5606.                     rindex& = rindex& - 1
  5607.                 ELSE
  5608.                     lsum = lsum + cg(lindex&)
  5609.                     lindex& = lindex& + 1
  5610.                 END IF
  5611.             WEND
  5612.         CASE ELSE
  5613.             WHILE lindex& < rindex&
  5614.                 IF rsum > lsum THEN
  5615.                     lsum = lsum + cg(rindex&)
  5616.                     lindex& = lindex& + 1
  5617.                 ELSE
  5618.                     rsum = rsum + cg(rindex&)
  5619.                     rindex& = rindex& - 1
  5620.                 END IF
  5621.             WEND
  5622.     END SELECT
  5623.     PartitionIndex& = lindex&
  5624.  
  5625.  
  5626. '* Only works for integer nonnegative arrays
  5627. SUB AverageArray (cg() AS DOUBLE, start AS LONG, finish AS LONG, Average#)
  5628.     DIM xP AS DOUBLE
  5629.     DIM YP AS DOUBLE
  5630.     DIM yn AS DOUBLE
  5631.     DIM xn AS DOUBLE
  5632.     DIM bn AS DOUBLE
  5633.     Average# = 0
  5634.     StatN& = finish - start + 1
  5635.     FOR i& = start& TO finish&
  5636.         IF cg(i&) < 0 THEN
  5637.             xn = xn + ch(i&) / StatN&
  5638.             bn = cg(i&) MOD StatN&
  5639.             IF yn >= StatN& - bn THEN
  5640.                 xn = xn + 1
  5641.                 yn = yn - StatN& - cg(i&)
  5642.             ELSE
  5643.                 xn = xn - cg(i&)
  5644.             END IF
  5645.         ELSEIF cg(i&) > 0 THEN
  5646.             xP = xP + cg(i&) / StatN&
  5647.             B = cg(i&) MOD StatN&
  5648.             IF YP >= N - B THEN
  5649.                 xP = xP + 1
  5650.                 YP = YP - N + B
  5651.             ELSE
  5652.                 YP = YP + B
  5653.             END IF
  5654.         END IF
  5655.     NEXT
  5656.     Average# = xP + YP / StatN&
  5657.  
  5658. SUB CGStatMode (CGSortLibArr() AS LONG, start AS LONG, finish AS LONG, CGModeCountMaximum AS LONG, CGModeCountMaximumIndex AS LONG)
  5659.     DIM CGModeNext AS LONG: CGModeNext = start + 1
  5660.     DIM CGModePrev AS LONG: CGModePrev = start
  5661.     DIM CModeCountCurrent AS LONG: CModeCountCurrent = 0
  5662.     DO UNTIL CGModeNext > finish
  5663.         CModeCountCurrent = 0
  5664.         DO
  5665.             IF CGModeNext > finish THEN
  5666.                 EXIT DO
  5667.             ELSE
  5668.                 IF CGSortLibArr(CGModeNext) = CGSortLibArr(CGModePrev) THEN
  5669.                     CGModeNext = CGModeNext + 1
  5670.                     CModeCountCurrent = CModeCountCurrent + 1
  5671.                 ELSE
  5672.                     EXIT DO
  5673.                 END IF
  5674.             END IF
  5675.         LOOP
  5676.         IF CModeCountCurrent > CGModeCountMaximum THEN
  5677.             CGModeCountMaximumIndex = CGModePrev
  5678.             CGModeCountMaximum = CModeCountCurrent
  5679.         END IF
  5680.         CGModePrev = CGModeNext
  5681.         CGModeNext = CGModePrev + 1
  5682.     LOOP
  5683.  
  5684. '************************ 8156035173
  5685.  
  5686. SUB UniqueNumnberSort (cgSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5687.     DIM UNSMMrec AS MinMaxRec
  5688.     DIM UNSRange AS DOUBLE
  5689.     GetMinMaxArray cgSortLibArr(), start, finish, UNSMMrec
  5690.     UNSRange = cgSortLibArr(UNSMMrec.max) - cgSortLibArr(UNSMMrec.min)
  5691.     IF UNSRange > 0 THEN
  5692.         SWAP cgSortLibArr(start), cgSortLibArr(UNSMMrec.min)
  5693.         SWAP cgSortLibArr(finish), cgSortLibArr(UNSMMrec.max)
  5694.         RangeDeltaPerOne# = UNSRange / (finish - start + 1)
  5695.         ISum# = cgSortLibArr(UNSMMrec.min)
  5696.         FOR c& = start + 1 TO finish - 1
  5697.             DO UNTIL cgSortLibArr(c&) <= ISum# '* can be just = too.
  5698.                 SWAP cgSortLibArr(c&), cgSortLibArr(cgSortLibArr(c&))
  5699.             LOOP
  5700.             ISum# = ISum# + RangeDeltaPerOne#
  5701.         NEXT
  5702.         IF order& <> 1 THEN
  5703.             '* this step corrects asymmetric performance
  5704.             '* since these are unique integers in a range,
  5705.             '* restabilization is unnecessary.
  5706.             StableInvert cgSortLibArr(), start, finish, 0
  5707.         END IF
  5708.     END IF
  5709.     InsertionSort cgSortLibArr(), start, finish, order&
  5710.  
  5711. '**********************************************
  5712. '* the even FASTER version of FlashSort using the fastest vector min-max search I know.
  5713. '* short of using c++ STL, I don't believe there to be any faster method, even STL itself.
  5714. '*********************************************
  5715. SUB FlashSortGMMA (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5716.     DIM FlashMM AS MinMaxRec
  5717.     '* GetMinMaxArrayIndexes CGSortLibArr(), start, finish, FlashMM.min, FlashMM.max
  5718.     GetMinMaxArray CGSortLibArr(), start, finish, FlashMM
  5719.     '* change these:
  5720.     DIM hold AS DOUBLE
  5721.     DIM flash AS DOUBLE
  5722.     DIM ANMiN AS DOUBLE
  5723.     '* to the same type as the array being sorted
  5724.  
  5725.     '* change these:
  5726.     DIM KIndex AS LONG
  5727.     DIM MIndex AS LONG
  5728.     DIM SIndex AS LONG
  5729.     '* to long for qbxx as qbxx has no _unsigned types
  5730.  
  5731.     '* the original ratio was .125 but i kept getting array bounds errors
  5732.     MIndex = (INT(.128 * (finish - start + 1)) + 1) OR 2
  5733.  
  5734.     '* change these:
  5735.     DIM FlashTrackL(0 TO MIndex) AS DOUBLE
  5736.     DIM FlashI AS LONG
  5737.     DIM FlashJ AS LONG
  5738.     DIM NextFlashJ AS LONG
  5739.     DIM FlashNMove AS LONG
  5740.     DIM FinishMinusOne AS LONG
  5741.     '* to the appropriate type for the range being sorted (must match start, finish variables)
  5742.  
  5743.     '* don't mess:
  5744.     DIM FlashC1 AS DOUBLE '* for some reason does not work with _float
  5745.     '* with this. it needs to be a double at the very least but float gives this a far greater range
  5746.     '* more than likely more range than is practical. but ya never know (change this to double for qbxx)
  5747.  
  5748.     ' sorts array A with finish elements by use of
  5749.     ' index vector L with M elements, with M ca. 0.128(finish-start).
  5750.     ' Translation of Karl-Dietrich Neubert's FlashSort
  5751.     ' algorithm into BASIC by Erdmann Hess.
  5752.     ' Generalized Numeric Version -- recoded by codeguy
  5753.  
  5754.     '* This is the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  5755.     '* strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  5756.     '* namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  5757.     '* kept popping up. Traced it to FlashTrackL() and added a minor (about 2.4&) increase in the upper bound of FlashTrackL().
  5758.     '* I suppose this could also be used for non-integer and non-string types as well. Note: For very large N, HashListSort()
  5759.     '* works even faster and has a similar memory footprint. But yes, this is still faster than QuickSort for N>10000 and like
  5760.     '* HashListSort, operates in asymptotically close to O(N) time.
  5761.  
  5762.     REM =============== CLASS FORMATION =================
  5763.  
  5764.     ';* ANMiN = CGSortLibArr(start)
  5765.  
  5766.     SWAP CGSortLibArr(FlashMM.min), CGSortLibArr(start): FlashMM.min = start: ANMiN = CGSortLibArr(FlashMM.min)
  5767.     SWAP CGSortLibArr(FlashMM.max), CGSortLibArr(finish): FlashMM.max = finish
  5768.  
  5769.     IF ANMiN = CGSortLibArr(FlashMM.max) THEN
  5770.         '* this is a monotonic sequence array and by definition is already sorted
  5771.         EXIT SUB
  5772.     END IF
  5773.  
  5774.     DIM FlashTrackL(MIndex)
  5775.     FlashC1 = (MIndex - 1) / (CGSortLibArr(FlashMM.max) - ANMiN)
  5776.  
  5777.     FOR FlashI = start + 1 TO finish - 1
  5778.         KIndex = INT(FlashC1 * (CGSortLibArr(FlashI) - ANMiN)) + 1
  5779.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + 1
  5780.     NEXT
  5781.  
  5782.     FOR KIndex = LBOUND(FlashTrackL) + 1 TO MIndex
  5783.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + FlashTrackL(KIndex - 1)
  5784.     NEXT KIndex
  5785.  
  5786.     REM ==================== PERMUTATION ================
  5787.     FlashNMove = 0
  5788.     FlashJ = start + 1
  5789.     KIndex = MIndex
  5790.     FinishMinusOne = finish - 1
  5791.     WHILE (FlashNMove < FinishMinusOne)
  5792.         WHILE (FlashJ > FlashTrackL(KIndex))
  5793.             FlashJ = FlashJ + 1
  5794.             KIndex = INT(FlashC1 * (CGSortLibArr(FlashJ) - ANMiN)) + 1
  5795.         WEND
  5796.         flash = CGSortLibArr(FlashJ)
  5797.         DO
  5798.             IF (FlashJ = (FlashTrackL(KIndex) + 1)) THEN
  5799.                 EXIT DO
  5800.             ELSE
  5801.                 IF FlashNMove < (FinishMinusOne) THEN
  5802.                     KIndex = INT(FlashC1 * (flash - ANMiN)) + 1
  5803.                     hold = CGSortLibArr(FlashTrackL(KIndex))
  5804.                     CGSortLibArr(FlashTrackL(KIndex)) = flash
  5805.                     flash = hold
  5806.                     FlashTrackL(KIndex) = FlashTrackL(KIndex) - 1
  5807.                     FlashNMove = FlashNMove + 1
  5808.                 ELSE
  5809.                     EXIT DO
  5810.                 END IF
  5811.             END IF
  5812.         LOOP
  5813.     WEND
  5814.     '================= Insertion Sort============
  5815.     FOR SIndex = LBOUND(FlashtrackL) + 1 TO MIndex
  5816.         '* sort subranges
  5817.         '********************* insertionsortz CGSortLibArr(), FlashTrackL(SIndex - 1), FlashTrackL(SIndex) - 1, order&
  5818.         FOR FlashI = FlashTrackL(SIndex) - 1 TO FlashTrackL(SIndex - 1) STEP -1
  5819.             IF (CGSortLibArr(FlashI + 1) < CGSortLibArr(FlashI)) THEN
  5820.                 hold = CGSortLibArr(FlashI)
  5821.                 NextFlashJ = FlashI
  5822.                 DO
  5823.                     FlashJ = NextFlashJ
  5824.                     IF FlashJ < FlashTrackL(SIndex) THEN
  5825.                         NextFlashJ = FlashJ + 1
  5826.                         IF (CGSortLibArr(NextFlashJ) < hold) THEN
  5827.                             SWAP CGSortLibArr(FlashJ), CGSortLibArr(NextFlashJ)
  5828.                         ELSE
  5829.                             EXIT DO
  5830.                         END IF
  5831.                     ELSE
  5832.                         EXIT DO
  5833.                     END IF
  5834.                 LOOP
  5835.                 CGSortLibArr(FlashJ) = hold
  5836.             END IF
  5837.         NEXT
  5838.         '* 914k/Ghz when it reaches this point, assuming this array is mostly sorted.
  5839.     NEXT
  5840.     IF order& = 1 THEN EXIT SUB
  5841.     FlashI = start
  5842.     FlashJ = finish
  5843.     WHILE FlashJ > FlashI
  5844.         SWAP CGSortLibArr(FlashI), CGSortLibArr(FlashJ)
  5845.         FlashI = FlashI + 1
  5846.         FlashJ = FlashJ - 1
  5847.     WEND
  5848.  
  5849. SUB GetMinMaxArrayIndexes (cg() AS DOUBLE, start&, finish&, MinMaxArrayMin AS LONG, MinMaxArrayMax AS LONG)
  5850.     'DIM GetMinMaxArray_i AS LONG
  5851.     DIM GetMinMaxArray_i AS LONG
  5852.     DIM GetMinMaxArray_n AS LONG
  5853.     DIM GetMinMaxArray_TT AS LONG
  5854.     DIM GetMinMaxArray_NMod2 AS INTEGER
  5855.     '* this is a workaround for the irritating malfunction
  5856.     '* of MOD using larger numbers and small divisors
  5857.     GetMinMaxArray_n = finish& - start&
  5858.     int10000& = (finish& - start&) \ 10000
  5859.     GetMinMaxArray_NMod2 = (finish& - start&) - 10000 * int10000&
  5860.     '* GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  5861.     IF (GetMinMaxArray_NMod2 MOD 2) THEN
  5862.         MinMaxArrayMin = start&
  5863.         MinMaxArrayMax = start&
  5864.         GetMinMaxArray_i = start& + 1
  5865.     ELSE
  5866.         IF cg(start&) > cg(finish&) THEN
  5867.             MinMaxArrayMax = start&
  5868.             MinMaxArrayMin = finish&
  5869.         ELSE
  5870.             MinMaxArrayMin = finish&
  5871.             MinMaxArrayMax = start&
  5872.         END IF
  5873.         GetMinMaxArray_i = start& + 2
  5874.     END IF
  5875.  
  5876.     WHILE GetMinMaxArray_i < finish&
  5877.         IF cg(GetMinMaxArray_i) > cg(GetMinMaxArray_i + 1) THEN
  5878.             IF cg(GetMinMaxArray_i) > cg(MinMaxArrayMax) THEN
  5879.                 MinMaxArrayMax = GetMinMaxArray_i
  5880.             END IF
  5881.             IF cg(GetMinMaxArray_i + 1) < cg(MinMaxArrayMin) THEN
  5882.                 MinMaxArrayMin = GetMinMaxArray_i + 1
  5883.             END IF
  5884.         ELSE
  5885.             IF cg(GetMinMaxArray_i + 1) > cg(MinMaxArrayMax) THEN
  5886.                 MinMaxArrayMax = GetMinMaxArray_i + 1
  5887.             END IF
  5888.             IF cg(GetMinMaxArray_i) < cg(MinMaxArrayMin) THEN
  5889.                 MinMaxArrayMin = GetMinMaxArray_i
  5890.             END IF
  5891.         END IF
  5892.         GetMinMaxArray_i = GetMinMaxArray_i + 2
  5893.     WEND
  5894.  
  5895. '***********************************
  5896. '* compares 2 arrays for similarity (equality or inequality).
  5897. '* equality will ONLY be satisfied if the range is the same AND all elements of subarrays are equal.
  5898. '***********************************
  5899. FUNCTION VectorComp% (CgSortArrayA() AS LONG, astart AS LONG, afinish AS LONG, CgSortArrayB() AS LONG, bstart AS LONG, bfinish AS LONG)
  5900.     VectorCompA& = astart
  5901.     VectorCompB& = bstart
  5902.     DO
  5903.         IF VectorCompA& > afinish THEN
  5904.             IF VectorCompB& > bfinish THEN
  5905.                 VectorComp% = 0
  5906.             ELSE
  5907.                 VectorComp% = -1
  5908.             END IF
  5909.             EXIT FUNCTION
  5910.         ELSE
  5911.             IF VectorCompB& > bfinish THEN
  5912.                 VectorComp% = 1
  5913.                 EXIT FUNCTION
  5914.             ELSE
  5915.                 IF CgSortArrayA(VectorCompA&) = CgSortArrayB(VectorCompB&) THEN
  5916.                     VectorCompA& = VectorCompA& + 1
  5917.                     VectorCompB& = VectorCompB& + 1
  5918.                 ELSEIF CgSortArrayA(VectorCompA&) < CgSortArrayB(VectorCompB&) THEN
  5919.                     VectorComp% = -1
  5920.                     EXIT FUNCTION
  5921.                 ELSE
  5922.                     VectorComp% = 1
  5923.                     EXIT FUNCTION
  5924.                 END IF
  5925.             END IF
  5926.         END IF
  5927.     LOOP
  5928.  
  5929. '****************************
  5930. '* THE fastest stable sort I Invented. Just how fast? Compared to standard MergeSort,
  5931. '* MergeInsert is 25 percent faster and uses only half the memory. My other method may
  5932. '* be stable but it is not guaranteed.
  5933. '***************************
  5934. SUB MergeInsert (CGSortArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5935.     IF finish - start > 5 THEN
  5936.         IF (finish - start) AND 0 THEN
  5937.             m& = start + (finish - start) / 4.390647888183594
  5938.             MergeInsert CGSortArray(), start, m&, order&
  5939.             MergeInsert CGSortArray(), m& + 1, finish, order&
  5940.             Tim_merge CGSortArray(), start, m&, finish, order&
  5941.         ELSE
  5942.             m& = start + (finish - start) / 2
  5943.             MergeInsert CGSortArray(), start, m&, order&
  5944.             MergeInsert CGSortArray(), m& + 1, finish, order&
  5945.             EfficientMerge CGSortArray(), start, finish, order&
  5946.         END IF
  5947.         'ELSE
  5948.         '    InsertionSort CGSortArray(), start, finish, order&
  5949.     END IF
  5950.  
  5951. SUB ExchangeSort (CgSortArray() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  5952.     DIM ExchangeSort_i AS LONG
  5953.     DIM ExchangeSort_j AS LONG
  5954.     SELECT CASE order&
  5955.         CASE 1
  5956.             FOR ExchangeSort_i = start TO finish - 1
  5957.                 FOR ExchangeSort_j = ExchangeSort_i + 1 TO finish
  5958.                     IF (CgSortArray(ExchangeSort_i) > CgSortArray(ExchangeSort_j)) THEN
  5959.                         SWAP CgSortArray(ExchangeSort_i), CgSortArray(ExchangeSort_j)
  5960.                     END IF
  5961.                 NEXT
  5962.             NEXT
  5963.         CASE ELSE
  5964.             FOR ExchangeSort_i = start TO finish - 1
  5965.                 FOR ExchangeSort_j = ExchangeSort_i + 1 TO finish
  5966.                     IF (CgSortArray(ExchangeSort_i) < CgSortArray(ExchangeSort_j)) THEN
  5967.                         SWAP CgSortArray(ExchangeSort_i), CgSortArray(ExchangeSort_j)
  5968.                     END IF
  5969.                 NEXT
  5970.             NEXT
  5971.     END SELECT
  5972.  

corrected the incorrect <,> in ExchangeSort 2018 sep 3
« Last Edit: October 06, 2018, 12:25:40 am by codeguy »

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #1 on: September 04, 2018, 09:55:29 pm »
Yes, this library has undergone probably 100+ revisions. I have included a generous selection of hybrids and split versions (like MergeInsert). Even some with nonstandard recursion because in some cases, the recursion splits weren't cutting it. Every effort has been made to assure correctness, with a sequence verification step, which is optional. As usual. the sorting range can be specified, except FlashSort, which I have yet to modify for such. All sortation is performed in memory, although this too can be adapted as you like using random access files.

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #2 on: September 04, 2018, 09:58:13 pm »
Finding median of an unsorted array in nearly O(n) time. Usually, this task is relegated to O(NLogN), which is the same complexity as Quicksort, but with this method, the waiting is reduced considerably.
Code: QB64: [Select]
  1. TYPE minmaxrec
  2.     min AS LONG
  3.     max AS LONG
  4. n& = 2
  5. DO 'n& = 2 * (2 * (16777215 * 2 + 1) + 1) + 1
  6.     n& = n& + 1
  7.     PRINT n&, ;
  8.     FOR r& = 0 TO 1
  9.         REDIM A(0 TO n&) AS DOUBLE
  10.         FOR I& = 0 TO n&
  11.             A(I&) = RND
  12.         NEXT
  13.         'RecurseSwap A(), 0, n&
  14.         flushtimer a!
  15.         SELECT CASE r&
  16.             CASE 0
  17.                 FlashSortExternalInsertionSort A(), 0, UBOUND(a), 1
  18.             CASE ELSE
  19.                 FlashSortGamm A(), 0, UBOUND(a), 1
  20.         END SELECT
  21.         b! = TIMER(.001)
  22.         PRINT LTRIM$(STR$(1000 * (b! - a!))), ;
  23.     NEXT
  24.     PRINT
  25.     n& = n& * 2
  26.  
  27. SUB RecurseSwap (a() AS DOUBLE, start AS LONG, finish AS LONG)
  28.     IF finish > start THEN
  29.         m& = start + (finish - start) \ 2
  30.         IF a(start) <> a(finish) THEN
  31.             SWAP a(start), a(finish)
  32.         END IF
  33.         RecurseSwap a(), start, m&
  34.         RecurseSwap a(), m& + 1, finish
  35.     END IF
  36. 'u! = TIMER(.001)
  37. 'RecursiveMinMax A(), 0, n&, L&, R&
  38. 'v! = TIMER(.001)
  39. 'PRINT "r"; A(L&), A(R&), v! - u!
  40. 'w! = TIMER(.001)
  41. 'SerialMinMax A(), 0, n&, L&, R&
  42. 'x! = TIMER(.001)
  43. 'PRINT "s"; A(L&), A(R&), x! - w!
  44.  
  45. 'y! = TIMER(.001)
  46. 'FindX A(), 0, n&, L&, -1
  47. 'FindX A(), 0, n&, R&, 1
  48. 'z! = TIMER(.001)
  49. 'PRINT "FindX"; z! - y!
  50. 'd! = TIMER(.001)
  51. 'DIM mmr AS minmaxrec
  52. 'GetMinMaxArray A(), 0, n&, mmr
  53. 'e! = TIMER(.001)
  54. 'PRINT "gmma"; e! - d!; A(L&); A(mmr.min); A(R&); A(mmr.max)
  55. SUB flushtimer (a!)
  56.     a! = TIMER(.001)
  57.     DO WHILE a! = TIMER(.001)
  58.     LOOP
  59.     a! = TIMER(.001)
  60. SUB SerialMinMax (a() AS DOUBLE, start AS LONG, finish AS LONG, minptr&, maxptr&)
  61.     SELECT CASE finish - start
  62.         CASE IS > 1
  63.             IF a(start) <= a(finish) THEN
  64.                 minptr& = start
  65.                 maxptr& = finish
  66.             ELSE
  67.                 minptr& = finish
  68.                 maxptr& = start
  69.             END IF
  70.             FOR i& = start + 1 TO finish - 1
  71.                 IF a(i&) < a(minptr&) THEN minptr& = i&
  72.                 IF a(i&) > a(maxptr&) THEN maxptr& = i&
  73.             NEXT
  74.         CASE 1
  75.             IF a(start) > a(finish) THEN
  76.                 minptr& = finish
  77.                 maxptr = start
  78.             ELSE
  79.                 minptr& = start
  80.                 maxptr& = finish
  81.             END IF
  82.         CASE 0
  83.             minptr& = start
  84.             maxptr& = start
  85.     END SELECT
  86.  
  87. SUB FindX (a() AS DOUBLE, start AS LONG, finish AS LONG, xptr&, c%)
  88.     SELECT CASE c%
  89.         CASE -1
  90.             xptr& = start
  91.             FOR i& = start + 1 TO finish
  92.                 IF a(i&) < a(xptr&) THEN xptr& = i&
  93.             NEXT
  94.         CASE ELSE
  95.             xptr& = start
  96.             FOR i& = start + 1 TO finish
  97.                 IF a(i&) > a(xptr&) THEN xptr& = i&
  98.             NEXT
  99.     END SELECT
  100.  
  101. SUB RecursiveMinMax (a() AS DOUBLE, start AS LONG, finish AS LONG, minptr&, maxptr&)
  102.     SELECT CASE finish - start
  103.         CASE 0
  104.             minptr& = start
  105.             maxptr& = start
  106.         CASE 1
  107.             IF a(start) > a(finish) THEN
  108.                 minptr& = finish
  109.                 maxptr& = start
  110.             ELSE
  111.                 minptr& = start
  112.                 maxptr& = finish
  113.             END IF
  114.         CASE IS > 1
  115.             MID& = start + (finish - start) \ 2
  116.             RecursiveMinMax a(), start, MID&, LLowPtr&, LHighPtr&
  117.             RecursiveMinMax a(), MID& + 1, finish, RLowPtr&, RHighPtr&
  118.             IF a(LLowPtr&) <= a(RLowPtr&) THEN
  119.                 minptr& = LLowPtr&
  120.             ELSE
  121.                 minptr& = RLowPtr&
  122.             END IF
  123.             IF a(LHighPtr&) >= a(RHighPtr&) THEN
  124.                 maxptr& = LHighPtr&
  125.             ELSE
  126.                 maxptr& = RHighPtr&
  127.             END IF
  128.     END SELECT
  129.  
  130. SUB GetMinMaxArray (cg() AS DOUBLE, start&, finish&, MinMaxArray AS minmaxrec)
  131.     IF finish& - start& > 31 THEN
  132.         'DIM GetMinMaxArray_i AS LONG
  133.         DIM GetMinMaxArray_i AS LONG
  134.         DIM GetMinMaxArray_n AS LONG
  135.         'DIM GetMinMaxArray_TT AS LONG
  136.         DIM GetMinMaxArray_NMod2 AS INTEGER
  137.         '* this is a workaround for the irritating malfunction
  138.         '* of MOD using larger numbers and small divisors
  139.         GetMinMaxArray_n = finish& - start&
  140.         'int10000& = (finish& - start&) \ 10000
  141.         'GetMinMaxArray_NMod2 = (finish& - start&) - 10000 * int10000&
  142.         GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * (GetMinMaxArray_n \ 10000)
  143.         '* GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  144.         IF (GetMinMaxArray_NMod2 MOD 2) THEN
  145.             MinMaxArray.min = start&
  146.             MinMaxArray.max = start&
  147.             GetMinMaxArray_i = start& + 1
  148.         ELSE
  149.             IF cg(start&) > cg(finish&) THEN
  150.                 MinMaxArray.max = start&
  151.                 MinMaxArray.min = finish&
  152.             ELSE
  153.                 MinMaxArray.min = finish&
  154.                 MinMaxArray.max = start&
  155.             END IF
  156.             GetMinMaxArray_i = start& + 2
  157.         END IF
  158.  
  159.         WHILE GetMinMaxArray_i < finish&
  160.             IF cg(GetMinMaxArray_i) > cg(GetMinMaxArray_i + 1) THEN
  161.                 IF cg(GetMinMaxArray_i) > cg(MinMaxArray.max) THEN
  162.                     MinMaxArray.max = GetMinMaxArray_i
  163.                 END IF
  164.                 IF cg(GetMinMaxArray_i + 1) < cg(MinMaxArray.min) THEN
  165.                     MinMaxArray.min = GetMinMaxArray_i + 1
  166.                 END IF
  167.             ELSE
  168.                 IF cg(GetMinMaxArray_i + 1) > cg(MinMaxArray.max) THEN
  169.                     MinMaxArray.max = GetMinMaxArray_i + 1
  170.                 END IF
  171.                 IF cg(GetMinMaxArray_i) < cg(MinMaxArray.min) THEN
  172.                     MinMaxArray.min = GetMinMaxArray_i
  173.                 END IF
  174.             END IF
  175.             GetMinMaxArray_i = GetMinMaxArray_i + 2
  176.         WEND
  177.     ELSE
  178.         GetArrayMinmax cg(), start&, finish&, MinMaxArray
  179.     END IF
  180.  
  181. SUB GetArrayMinmax (a() AS DOUBLE, start&, finish&, arec AS minmaxrec)
  182.     arec.min = start&
  183.     arec.max = start&
  184.     DIM GetArrayMinmax_u AS LONG
  185.     FOR GetArrayMinmax_u = start& + 1 TO finish&
  186.         IF a(GetArrayMinmax_u) < a(arec.min) THEN arec.min = GetArrayMinmax_u
  187.         IF a(GetArrayMinmax_u) > a(arec.max) THEN arec.max = GetArrayMinmax_u
  188.     NEXT
  189.  
  190. SUB FlashSortGamm (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  191.     DIM FlashMM AS minmaxrec
  192.     GetMinMaxArray CGSortLibArr(), start, finish, FlashMM
  193.     '* change these:
  194.     DIM hold AS DOUBLE
  195.     DIM flash AS DOUBLE
  196.     DIM ANMiN AS DOUBLE
  197.     '* to the same type as the array being sorted
  198.  
  199.     '* change these:
  200.     DIM KIndex AS LONG
  201.     DIM MIndex AS LONG
  202.     DIM SIndex AS LONG
  203.     '* to long for qbxx as qbxx has no _unsigned types
  204.  
  205.     '* the original ratio was .125 but i kept getting array bounds errors
  206.     MIndex = (INT(.128 * (finish - start + 1)) + 1) OR 2
  207.  
  208.     '* change these:
  209.     DIM FlashTrackL(0 TO MIndex) AS DOUBLE
  210.     DIM FlashI AS LONG
  211.     DIM FlashJ AS LONG
  212.     DIM NextFlashJ AS LONG
  213.     DIM FlashNMove AS LONG
  214.     DIM FinishMinusOne AS LONG
  215.     '* to the appropriate type for the range being sorted (must match start, finish variables)
  216.  
  217.     '* don't mess:
  218.     DIM FlashC1 AS DOUBLE '* for some reason does not work with _float
  219.     '* with this. it needs to be a double at the very least but float gives this a far greater range
  220.     '* more than likely more range than is practical. but ya never know (change this to double for qbxx)
  221.  
  222.     ' sorts array A with finish elements by use of
  223.     ' index vector L with M elements, with M ca. 0.128(finish-start).
  224.     ' Translation of Karl-Dietrich Neubert's FlashSort
  225.     ' algorithm into BASIC by Erdmann Hess.
  226.     ' Generalized Numeric Version -- recoded by codeguy
  227.  
  228.     '* This is the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  229.     '* strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  230.     '* namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  231.     '* kept popping up. Traced it to FlashTrackL() and added a minor (about 2.4&) increase in the upper bound of FlashTrackL().
  232.     '* I suppose this could also be used for non-integer and non-string types as well. Note: For very large N, HashListSort()
  233.     '* works even faster and has a similar memory footprint. But yes, this is still faster than QuickSort for N>10000 and like
  234.     '* HashListSort, operates in asymptotically close to O(N) time.
  235.  
  236.     REM =============== CLASS FORMATION =================
  237.  
  238.     ';* ANMiN = CGSortLibArr(start)
  239.  
  240.     SWAP CGSortLibArr(FlashMM.min), CGSortLibArr(start): FlashMM.min = start: ANMiN = CGSortLibArr(FlashMM.min)
  241.     SWAP CGSortLibArr(FlashMM.max), CGSortLibArr(finish): FlashMM.max = finish
  242.  
  243.     IF ANMiN = CGSortLibArr(FlashMM.max) THEN
  244.         '* this is a monotonic sequence array and by definition is already sorted
  245.         EXIT SUB
  246.     END IF
  247.  
  248.     DIM FlashTrackL(MIndex)
  249.     FlashC1 = (MIndex - 1) / (CGSortLibArr(FlashMM.max) - ANMiN)
  250.  
  251.     FOR FlashI = start + 1 TO finish - 1
  252.         KIndex = INT(FlashC1 * (CGSortLibArr(FlashI) - ANMiN)) + 1
  253.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + 1
  254.     NEXT
  255.  
  256.     FOR KIndex = LBOUND(FlashTrackL) + 1 TO MIndex
  257.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + FlashTrackL(KIndex - 1)
  258.     NEXT KIndex
  259.  
  260.     REM ==================== PERMUTATION ================
  261.     FlashNMove = 0
  262.     FlashJ = start + 1
  263.     KIndex = MIndex
  264.     FinishMinusOne = finish - 1
  265.     WHILE (FlashNMove < FinishMinusOne)
  266.         WHILE (FlashJ > FlashTrackL(KIndex))
  267.             FlashJ = FlashJ + 1
  268.             KIndex = INT(FlashC1 * (CGSortLibArr(FlashJ) - ANMiN)) + 1
  269.         WEND
  270.         flash = CGSortLibArr(FlashJ)
  271.         DO
  272.             IF (FlashJ = (FlashTrackL(KIndex) + 1)) THEN
  273.                 EXIT DO
  274.             ELSE
  275.                 IF FlashNMove < (FinishMinusOne) THEN
  276.                     KIndex = INT(FlashC1 * (flash - ANMiN)) + 1
  277.                     hold = CGSortLibArr(FlashTrackL(KIndex))
  278.                     CGSortLibArr(FlashTrackL(KIndex)) = flash
  279.                     flash = hold
  280.                     FlashTrackL(KIndex) = FlashTrackL(KIndex) - 1
  281.                     FlashNMove = FlashNMove + 1
  282.                 ELSE
  283.                     EXIT DO
  284.                 END IF
  285.             END IF
  286.         LOOP
  287.     WEND
  288.     '================= Insertion Sort============
  289.     FOR SIndex = LBOUND(FlashtrackL) + 1 TO MIndex
  290.         '* sort subranges
  291.         '********************* insertionsortz CGSortLibArr(), FlashTrackL(SIndex - 1), FlashTrackL(SIndex) - 1, order&
  292.         'InsertionSortBinary CGSortLibArr(), FlashTrackL(SIndex - 1), FlashTrackL(SIndex) - 1, order&
  293.         FOR FlashI = FlashTrackL(SIndex) - 1 TO FlashTrackL(SIndex - 1) STEP -1
  294.             IF (CGSortLibArr(FlashI + 1) < CGSortLibArr(FlashI)) THEN
  295.                 hold = CGSortLibArr(FlashI)
  296.                 NextFlashJ = FlashI
  297.                 DO
  298.                     FlashJ = NextFlashJ
  299.                     IF FlashJ < FlashTrackL(SIndex) THEN
  300.                         NextFlashJ = FlashJ + 1
  301.                         IF (CGSortLibArr(NextFlashJ) < hold) THEN
  302.                             SWAP CGSortLibArr(FlashJ), CGSortLibArr(NextFlashJ)
  303.                         ELSE
  304.                             EXIT DO
  305.                         END IF
  306.                     ELSE
  307.                         EXIT DO
  308.                     END IF
  309.                 LOOP
  310.                 CGSortLibArr(FlashJ) = hold
  311.             END IF
  312.         NEXT
  313.         '* 914k/Ghz when it reaches this point, assuming this array is mostly sorted.
  314.     NEXT
  315.     IF order& = 1 THEN EXIT SUB
  316.     FlashI = start
  317.     FlashJ = finish
  318.     WHILE FlashJ > FlashI
  319.         SWAP CGSortLibArr(FlashI), CGSortLibArr(FlashJ)
  320.         FlashI = FlashI + 1
  321.         FlashJ = FlashJ - 1
  322.     WEND
  323.  
  324. SUB FlashSortExternalInsertionSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  325.     '* change these:
  326.     DIM hold AS DOUBLE
  327.     DIM flash AS DOUBLE
  328.     DIM ANMiN AS DOUBLE
  329.     '* to the same type as the array being sorted
  330.  
  331.     '* change these:
  332.     DIM KIndex AS LONG
  333.     DIM MIndex AS LONG
  334.     DIM SIndex AS LONG
  335.     '* to long for qbxx as qbxx has no _unsigned types
  336.  
  337.     '* the original ratio was .125 but i kept getting array bounds errors
  338.     MIndex = (INT(.128 * (finish - start + 1)) + 1) OR 2
  339.  
  340.     '* change these:
  341.     DIM FlashTrackL(0 TO MIndex) AS LONG
  342.     DIM FlashI AS LONG
  343.     DIM FlashJ AS LONG
  344.     DIM NextFlashJ AS LONG
  345.     DIM FlashNMove AS LONG
  346.     DIM MaxValueIndex AS LONG
  347.     DIM MinValueIndex AS LONG
  348.     DIM FinishMinusOne AS LONG
  349.     '* to the appropriate type for the range being sorted (must match start, finish variables)
  350.  
  351.     '* don't mess:
  352.     DIM FlashC1 AS DOUBLE '* for some reason does not work with _float
  353.     '* with this. it needs to be a double at the very least but float gives this a far greater range
  354.     '* more than likely more range than is practical. but ya never know (change this to double for qbxx)
  355.  
  356.     ' sorts array A with finish elements by use of
  357.     ' index vector L with M elements, with M ca. 0.128(finish-start).
  358.     ' Translation of Karl-Dietrich Neubert's FlashSort
  359.     ' algorithm into BASIC by Erdmann Hess.
  360.     ' Generalized Numeric Version -- recoded by codeguy
  361.  
  362.     '* This is the absolute quickest sorting algorithm I can find for numeric arrays. Unfortunately, generalizing this for
  363.     '* strings may require some work. sounds like a project to me. I have changed a couple things from the original,
  364.     '* namely that .125 has been changed to .128. It seems that after a few runs on randomized data, a subscript error
  365.     '* kept popping up. Traced it to FlashTrackL() and added a minor (about 2.4&) increase in the upper bound of FlashTrackL().
  366.     '* I suppose this could also be used for non-integer and non-string types as well. Note: For very large N, HashListSort()
  367.     '* works even faster and has a similar memory footprint. But yes, this is still faster than QuickSort for N>10000 and like
  368.     '* HashListSort, operates in asymptotically close to O(N) time.
  369.  
  370.     REM =============== CLASS FORMATION =================
  371.     'GetMinMaxArrayPtrs CGSortLibArr(), start, finish, MinValueIndex, MaxValueIndex
  372.     MaxValueIndex = finish
  373.     MinValueIndex = start
  374.     FOR FlashI = start TO finish
  375.         IF (CGSortLibArr(FlashI) > CGSortLibArr(MaxValueIndex)) THEN MaxValueIndex = FlashI
  376.         IF (CGSortLibArr(FlashI) < CGSortLibArr(MinValueIndex)) THEN MinValueIndex = FlashI
  377.     NEXT FlashI
  378.     SWAP CGSortLibArr(MinValueIndex), CGSortLibArr(start): MinValueIndex = start: ANMiN = CGSortLibArr(MinValueIndex)
  379.     SWAP CGSortLibArr(MaxValueIndex), CGSortLibArr(finish): MaxValueIndex = finish
  380.  
  381.     IF ANMiN = CGSortLibArr(MaxValueIndex) THEN
  382.         '* this is a monotonic sequence array and by definition is already sorted
  383.         EXIT SUB
  384.     END IF
  385.  
  386.     'DIM FlashTrackL(0 TO MIndex)
  387.     FlashC1 = (MIndex - 1) / (CGSortLibArr(MaxValueIndex) - ANMiN)
  388.  
  389.     FOR FlashI = start + 1 TO finish - 1
  390.         KIndex = INT(FlashC1 * (CGSortLibArr(FlashI) - ANMiN)) + 1
  391.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + 1
  392.     NEXT
  393.  
  394.     FOR KIndex = LBOUND(FlashTrackL) + 1 TO MIndex
  395.         FlashTrackL(KIndex) = FlashTrackL(KIndex) + FlashTrackL(KIndex - 1)
  396.     NEXT KIndex
  397.  
  398.     REM ==================== PERMUTATION ================
  399.     FlashNMove = 0
  400.     FlashJ = start + 1
  401.     KIndex = MIndex
  402.     FinishMinusOne = finish - 1
  403.     WHILE (FlashNMove < FinishMinusOne)
  404.         WHILE (FlashJ > FlashTrackL(KIndex))
  405.             FlashJ = FlashJ + 1
  406.             KIndex = INT(FlashC1 * (CGSortLibArr(FlashJ) - ANMiN)) + 1
  407.         WEND
  408.         flash = CGSortLibArr(FlashJ)
  409.         DO
  410.             IF (FlashJ = (FlashTrackL(KIndex) + 1)) THEN
  411.                 EXIT DO
  412.             ELSE
  413.                 IF FlashNMove < (FinishMinusOne) THEN
  414.                     KIndex = INT(FlashC1 * (flash - ANMiN)) + 1
  415.                     hold = CGSortLibArr(FlashTrackL(KIndex))
  416.                     CGSortLibArr(FlashTrackL(KIndex)) = flash
  417.                     flash = hold
  418.                     FlashTrackL(KIndex) = FlashTrackL(KIndex) - 1
  419.                     FlashNMove = FlashNMove + 1
  420.                 ELSE
  421.                     EXIT DO
  422.                 END IF
  423.             END IF
  424.         LOOP
  425.     WEND
  426.     '================= Insertion Sort============
  427.     FOR SIndex = LBOUND(FlashtrackL) + 1 TO MIndex
  428.         '* sort subranges
  429.         '*********************
  430.         InsertionSort CGSortLibArr(), FlashTrackL(SIndex - 1), FlashTrackL(SIndex) - 1, 1
  431.         'FOR FlashI = FlashTrackL(SIndex) - 1 TO FlashTrackL(SIndex - 1) STEP -1
  432.         '    IF (CGSortLibArr(FlashI + 1) < CGSortLibArr(FlashI)) THEN
  433.         '        hold = CGSortLibArr(FlashI)
  434.         '        NextFlashJ = FlashI
  435.         '        DO
  436.         '            FlashJ = NextFlashJ
  437.         '            IF FlashJ < FlashTrackL(SIndex) THEN
  438.         '                NextFlashJ = FlashJ + 1
  439.         '                IF (CGSortLibArr(NextFlashJ) < hold) THEN
  440.         '                    SWAP CGSortLibArr(FlashJ), CGSortLibArr(NextFlashJ)
  441.         '                ELSE
  442.         '                    EXIT DO
  443.         '                END IF
  444.         '            ELSE
  445.         '                EXIT DO
  446.         '            END IF
  447.         '        LOOP
  448.         '        CGSortLibArr(FlashJ) = hold
  449.         '    END IF
  450.         'NEXT
  451.         '* 914k/Ghz when it reaches this point, assuming this array is mostly sorted.
  452.     NEXT
  453.     '* InsertionSort CGSortLibArr(), start, finish, 1  '* SLOWER
  454.     IF order& = 1 THEN EXIT SUB
  455.     FlashI = start
  456.     FlashJ = finish
  457.     WHILE FlashJ > FlashI
  458.         SWAP CGSortLibArr(FlashI), CGSortLibArr(FlashJ)
  459.         FlashI = FlashI + 1
  460.         FlashJ = FlashJ - 1
  461.     WEND
  462.  
  463. SUB InsertionSortBinary (CGSortLibArr() AS DOUBLE, start&, finish&, order&)
  464.     DIM InSortBinary_NSorted AS LONG
  465.     DIM InSortBinary_F AS LONG
  466.     DIM InSortBinary_P AS LONG
  467.     DIM InSortBinary_X AS LONG
  468.     InSortBinary_NSorted = 0
  469.     DO
  470.         InSortBinary_F = InsertionBinaryB&(CGSortLibArr(), start&, InSortBinary_NSorted, order&)
  471.         InSortBinary_P = start& + InSortBinary_NSorted
  472.         WHILE InSortBinary_P > InSortBinary_F
  473.             InSortBinary_X = InSortBinary_P - 1
  474.             SWAP CGSortLibArr(InSortBinary_P), CGSortLibArr(InSortBinary_X)
  475.             InSortBinary_P = InSortBinary_X
  476.         WEND
  477.         InSortBinary_NSorted = InSortBinary_NSorted + 1
  478.     LOOP UNTIL InSortBinary_NSorted > finish& - start&
  479.  
  480. FUNCTION InsertionBinaryB& (CGSortLibArr() AS DOUBLE, start&, NumberAlreadyOrdered&, order&)
  481.     IF NumberAlreadyOrdered& > 0 THEN
  482.         IF order& = 1 THEN
  483.             Bsrcha& = start&
  484.             BsrchB& = start& + NumberAlreadyOrdered&
  485.             IF CGSortLibArr(start& + NumberAlreadyOrdered&) < CGSortLibArr(start& + NumberAlreadyOrdered& - 1) THEN
  486.                 DO
  487.                     BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  488.                     IF CGSortLibArr(BsrchC&) < CGSortLibArr(NumberAlreadyOrdered&) THEN
  489.                         Bsrcha& = BsrchC& + 1
  490.                     ELSE
  491.                         BsrchB& = BsrchC&
  492.                     END IF
  493.                 LOOP WHILE Bsrcha& < BsrchB&
  494.             END IF
  495.             InsertionBinaryB& = BsrchB&
  496.         ELSE
  497.             Bsrcha& = start&
  498.             BsrchB& = start& + NumberAlreadyOrdered&
  499.             IF CGSortLibArr(start& + NumberAlreadyOrdered&) > CGSortLibArr(start& + NumberAlreadyOrdered& - 1) THEN
  500.                 DO
  501.                     BsrchC& = Bsrcha& + (BsrchB& - Bsrcha&) \ 2
  502.                     IF CGSortLibArr(BsrchC&) > CGSortLibArr(NumberAlreadyOrdered&) THEN
  503.                         Bsrcha& = BsrchC& + 1
  504.                     ELSE
  505.                         BsrchB& = BsrchC&
  506.                     END IF
  507.                 LOOP WHILE Bsrcha& < BsrchB&
  508.             END IF
  509.             InsertionBinaryB& = BsrchB&
  510.         END IF
  511.     ELSE
  512.         InsertionBinaryB& = start&
  513.     END IF
  514.  
  515. SUB InsertionSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  516.     DIM InSort_Local_ArrayTemp AS DOUBLE
  517.     DIM InSort_Local_i AS LONG
  518.     DIM InSort_Local_j AS LONG
  519.     SELECT CASE order&
  520.         CASE 1
  521.             FOR InSort_Local_i = start + 1 TO finish
  522.                 InSort_Local_j = InSort_Local_i - 1
  523.                 IF CGSortLibArr(InSort_Local_i) < CGSortLibArr(InSort_Local_j) THEN
  524.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  525.                     DO UNTIL InSort_Local_j < start
  526.                         IF (InSort_Local_ArrayTemp < CGSortLibArr(InSort_Local_j)) THEN
  527.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  528.                             InSort_Local_j = InSort_Local_j - 1
  529.                         ELSE
  530.                             EXIT DO
  531.                         END IF
  532.                     LOOP
  533.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  534.                 END IF
  535.             NEXT
  536.         CASE ELSE
  537.             FOR InSort_Local_i = start + 1 TO finish
  538.                 InSort_Local_j = InSort_Local_i - 1
  539.                 IF CGSortLibArr(InSort_Local_i) > CGSortLibArr(InSort_Local_j) THEN
  540.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  541.                     DO UNTIL InSort_Local_j < start
  542.                         IF (InSort_Local_ArrayTemp > CGSortLibArr(InSort_Local_j)) THEN
  543.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  544.                             InSort_Local_j = InSort_Local_j - 1
  545.                         ELSE
  546.                             EXIT DO
  547.                         END IF
  548.                     LOOP
  549.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  550.                 END IF
  551.             NEXT
  552.     END SELECT
  553.  
  554. SUB GetMinMaxArrayPtrs (cg() AS DOUBLE, start&, finish&, MinMaxArrayMin AS LONG, MinMaxArrayMax AS LONG)
  555.     DIM GetMinMaxArray_i AS LONG
  556.     DIM GetMinMaxArray_n AS LONG
  557.     DIM GetMinMaxArray_NMod2 AS INTEGER
  558.     '* this is a workaround for the irritating malfunction
  559.     '* of MOD using larger numbers and small divisors
  560.     GetMinMaxArray_n = finish& - start&
  561.     int10000& = (finish& - start&) \ 10000
  562.     GetMinMaxArray_NMod2 = ((finish& - start&) - 10000 * int10000&) AND 32767
  563.     'GetMinMaxArray_NMod2 = GetMinMaxArray_n AND 1
  564.     '* GetMinMaxArray_NMod2 = GetMinMaxArray_n - 10000 * ((GetMinMaxArray_n - GetMinMaxArray_TT) / 10000)
  565.     IF (GetMinMaxArray_NMod2 MOD 2) THEN
  566.         MinMaxArrayMin = start&
  567.         MinMaxArrayMax = start&
  568.         GetMinMaxArray_i = start& + 1
  569.     ELSE
  570.         IF cg(start&) > cg(finish&) THEN
  571.             MinMaxArrayMax = start&
  572.             MinMaxArrayMin = finish&
  573.         ELSE
  574.             MinMaxArrayMin = finish&
  575.             MinMaxArrayMax = start&
  576.         END IF
  577.         GetMinMaxArray_i = start& + 2
  578.     END IF
  579.  
  580.     WHILE GetMinMaxArray_i < finish&
  581.         IF cg(GetMinMaxArray_i) > cg(GetMinMaxArray_i + 1) THEN
  582.             IF cg(GetMinMaxArray_i) > cg(MinMaxArrayMax) THEN
  583.                 MinMaxArrayMax = GetMinMaxArray_i
  584.             END IF
  585.             IF cg(GetMinMaxArray_i + 1) < cg(MinMaxArrayMin) THEN
  586.                 MinMaxArrayMin = GetMinMaxArray_i + 1
  587.             END IF
  588.         ELSE
  589.             IF cg(GetMinMaxArray_i + 1) > cg(MinMaxArrayMax) THEN
  590.                 MinMaxArrayMax = GetMinMaxArray_i + 1
  591.             END IF
  592.             IF cg(GetMinMaxArray_i) < cg(MinMaxArrayMin) THEN
  593.                 MinMaxArrayMin = GetMinMaxArray_i
  594.             END IF
  595.         END IF
  596.         GetMinMaxArray_i = GetMinMaxArray_i + 2
  597.     WEND
  598.  
« Last Edit: September 04, 2018, 10:00:47 pm by codeguy »

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #3 on: September 05, 2018, 12:36:42 pm »
Thank you!
In order to understand recursion, one must first understand recursion.

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #4 on: September 05, 2018, 12:57:50 pm »
As I have painfully learned, there's little recourse to losing access to a site and consequently the cool stuff other people have submitted. Yes, it's no fun. Even stuff like dropbox isn't safe. The only relatively safe way would be archival on solid media in triplicate and some offsite or on other sites. The danger with that last approach is someone will repost without confirmation of correctness. There are several versions of this code posted by others that is incomplete or not completely tested. I did find one error in my latest contribution, but it is minor and I will be submitting the CORRECTED code. ExchangeSort in the current post is incorrect ONLY in that < and > are supposed to be > and <. Beyond that, it's PERFECT. There are some algos STOLEN from Quora and ones I submitted as Most Viewed In Sorting Algorithms with one hit called "Will There Ever Be An O(n) Sort Algorithm?" I remain oblique about techniques for finding median in O(n) time, as well as anything about my faster-than-flashsort for large N (in the higher single and double-digit millions) numeric sorting algorithm. Sometimes the slow start (due to initial overhead) is WELL worth the wait. For smaller N, it keeps pace very nicely with FlashSort. For larger N, it stomps FlashSort by (15-20)%. And yes, you're very welcome, Terry.
« Last Edit: September 05, 2018, 01:15:09 pm by codeguy »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #5 on: September 05, 2018, 01:57:59 pm »
I'll still stick with _MEMSORT.  It's the fastest way to sort Integers and Bytes, signed and unsigned -- by far -- and it's one routine you can use with multiple data types.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1280, 720, 256)
  2.  
  3. DIM x(5) AS _BYTE
  4. DIM z(5) AS STRING * 5
  5.  
  6. 'Let's see if we can sort the integer array
  7. 'Initialize Data
  8. FOR i = 0 TO 5: x(i) = RND * 100: PRINT x(i),: NEXT: PRINT
  9. PRINT "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
  10.  
  11. 'Sort
  12. m = _MEM(x())
  13. Sort m
  14.  
  15. 'Result
  16. FOR i = 0 TO 5: PRINT x(i),: NEXT: PRINT
  17.  
  18. PRINT "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
  19.  
  20.  
  21. 'Try the same routine with a different data type array to sort
  22. 'Initialize Data
  23. FOR i = 0 TO 7: y(i) = RND * 100: PRINT y(i),: NEXT
  24. PRINT "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
  25.  
  26. 'Sort
  27. m = _MEM(y())
  28. Sort m
  29.  
  30. 'Result
  31. FOR i = 0 TO 7: PRINT y(i),: NEXT: PRINT
  32. PRINT "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
  33.  
  34.  
  35. 'To test with fixed length string arrays
  36. z(0) = "Doggy": z(1) = "Pudding": z(2) = "Frog ": z(3) = "test2": z(4) = "Test2": z(5) = "test1"
  37. FOR i = 0 TO 5: PRINT z(i),: NEXT: PRINT
  38. PRINT "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
  39.  
  40. m = _MEM(z())
  41. Sort m
  42.  
  43. 'Result
  44. FOR i = 0 TO 5: PRINT z(i),: NEXT: PRINT
  45.  
  46.  
  47.  
  48.  
  49. SUB Sort (m AS _MEM)
  50. $IF 64BIT THEN
  51.     DIM ES AS LONG, EC AS LONG
  52.  
  53. IF NOT m.TYPE AND 65536 THEN EXIT SUB 'We won't work without an array
  54. IF m.TYPE AND 1024 THEN DataType = 10
  55. IF m.TYPE AND 1 THEN DataType = DataType + 1
  56. IF m.TYPE AND 2 THEN DataType = DataType + 2
  57. IF m.TYPE AND 4 THEN IF m.TYPE AND 128 THEN DataType = DataType + 4 ELSE DataType = 3
  58. IF m.TYPE AND 8 THEN IF m.TYPE AND 128 THEN DataType = DataType + 8 ELSE DataType = 5
  59. IF m.TYPE AND 32 THEN DataType = 6
  60. IF m.TYPE AND 512 THEN DataType = 7
  61.  
  62. 'Convert our offset data over to something we can work with
  63. DIM m1 AS _MEM: m1 = _MEMNEW(LEN(ES))
  64. _MEMPUT m1, m1.OFFSET, m.ELEMENTSIZE: _MEMGET m1, m1.OFFSET, ES 'Element Size
  65. _MEMPUT m1, m1.OFFSET, m.SIZE: _MEMGET m1, m1.OFFSET, EC 'Element Count will temporily hold the WHOLE array size
  66.  
  67. EC = EC / ES - 1 'Now we take the whole element size / the size of the elements and get our actual element count.  We subtract 1 so our arrays start at 0 and not 1.
  68. 'And work with it!
  69.  
  70. SELECT CASE DataType
  71.     CASE 1 'BYTE
  72.         DIM temp1(-128 TO 127) AS _UNSIGNED LONG
  73.         DIM t1 AS _BYTE
  74.         i = 0
  75.         DO
  76.             _MEMGET m, m.OFFSET + i, t1
  77.             temp1(t1) = temp1(t1) + 1
  78.             i = i + 1
  79.         LOOP UNTIL i > EC
  80.         i1 = -128
  81.         DO
  82.             DO UNTIL temp1(i1) = 0
  83.                 _MEMPUT m, m.OFFSET + counter, i1 AS _BYTE
  84.                 counter = counter + 1
  85.                 temp1(i1) = temp1(i1) - 1
  86.                 IF counter > EC THEN EXIT SUB
  87.             LOOP
  88.             i1 = i1 + 1
  89.         LOOP UNTIL i1 > 127
  90.     CASE 2: 'INTEGER
  91.         DIM temp2(-32768 TO 32767) AS _UNSIGNED LONG
  92.         DIM t2 AS INTEGER
  93.         i = 0
  94.         DO
  95.             _MEMGET m, m.OFFSET + i * 2, t2
  96.             temp2(t2) = temp2(t2) + 1
  97.             i = i + 1
  98.         LOOP UNTIL i > EC
  99.         i1 = -32768
  100.         DO
  101.             DO UNTIL temp2(i1) = 0
  102.                 _MEMPUT m, m.OFFSET + counter * 2, i1 AS INTEGER
  103.                 counter = counter + 1
  104.                 temp2(i1) = temp2(i1) - 1
  105.                 IF counter > EC THEN EXIT SUB
  106.             LOOP
  107.             i1 = i1 + 1
  108.         LOOP UNTIL i1 > 32767
  109.     CASE 3 'SINGLE
  110.         DIM T3a AS SINGLE, T3b AS SINGLE
  111.         gap = EC
  112.         DO
  113.             gap = 10 * gap \ 13
  114.             IF gap < 1 THEN gap = 1
  115.             i = 0
  116.             swapped = 0
  117.             DO
  118.                 o = m.OFFSET + i * 4
  119.                 o1 = m.OFFSET + (i + gap) * 4
  120.                 IF _MEMGET(m, o, SINGLE) > _MEMGET(m, o1, SINGLE) THEN
  121.                     _MEMGET m, o1, T3a
  122.                     _MEMGET m, o, T3b
  123.                     _MEMPUT m, o1, T3b
  124.                     _MEMPUT m, o, T3a
  125.                     swapped = -1
  126.                 END IF
  127.                 i = i + 1
  128.             LOOP UNTIL i + gap > EC
  129.         LOOP UNTIL gap = 1 AND swapped = 0
  130.     CASE 4 'LONG
  131.         DIM T4a AS LONG, T4b AS LONG
  132.         gap = EC
  133.         DO
  134.             gap = 10 * gap \ 13
  135.             IF gap < 1 THEN gap = 1
  136.             i = 0
  137.             swapped = 0
  138.             DO
  139.                 o = m.OFFSET + i * 4
  140.                 o1 = m.OFFSET + (i + gap) * 4
  141.                 IF _MEMGET(m, o, LONG) > _MEMGET(m, o1, LONG) THEN
  142.                     _MEMGET m, o1, T4a
  143.                     _MEMGET m, o, T4b
  144.                     _MEMPUT m, o1, T4b
  145.                     _MEMPUT m, o, T4a
  146.                     swapped = -1
  147.                 END IF
  148.                 i = i + 1
  149.             LOOP UNTIL i + gap > EC
  150.         LOOP UNTIL gap = 1 AND swapped = 0
  151.     CASE 5 'DOUBLE
  152.         DIM T5a AS DOUBLE, T5b AS DOUBLE
  153.         gap = EC
  154.         DO
  155.             gap = 10 * gap \ 13
  156.             IF gap < 1 THEN gap = 1
  157.             i = 0
  158.             swapped = 0
  159.             DO
  160.                 o = m.OFFSET + i * 8
  161.                 o1 = m.OFFSET + (i + gap) * 8
  162.                 IF _MEMGET(m, o, DOUBLE) > _MEMGET(m, o1, DOUBLE) THEN
  163.                     _MEMGET m, o1, T5a
  164.                     _MEMGET m, o, T5b
  165.                     _MEMPUT m, o1, T5b
  166.                     _MEMPUT m, o, T5a
  167.                     swapped = -1
  168.                 END IF
  169.                 i = i + 1
  170.             LOOP UNTIL i + gap > EC
  171.         LOOP UNTIL gap = 1 AND swapped = 0
  172.     CASE 6 ' _FLOAT
  173.         DIM T6a AS _FLOAT, T6b AS _FLOAT
  174.         gap = EC
  175.         DO
  176.             gap = 10 * gap \ 13
  177.             IF gap < 1 THEN gap = 1
  178.             i = 0
  179.             swapped = 0
  180.             DO
  181.                 o = m.OFFSET + i * 32
  182.                 o1 = m.OFFSET + (i + gap) * 32
  183.                 IF _MEMGET(m, o, _FLOAT) > _MEMGET(m, o1, _FLOAT) THEN
  184.                     _MEMGET m, o1, T6a
  185.                     _MEMGET m, o, T6b
  186.                     _MEMPUT m, o1, T6b
  187.                     _MEMPUT m, o, T6a
  188.                     swapped = -1
  189.                 END IF
  190.                 i = i + 1
  191.             LOOP UNTIL i + gap > EC
  192.         LOOP UNTIL gap = 1 AND swapped = 0
  193.     CASE 7 'String
  194.         DIM T7a AS STRING, T7b AS STRING, T7c AS STRING
  195.         T7a = SPACE$(ES): T7b = SPACE$(ES): T7c = SPACE$(ES)
  196.         gap = EC
  197.         DO
  198.             gap = INT(gap / 1.247330950103979)
  199.             IF gap < 1 THEN gap = 1
  200.             i = 0
  201.             swapped = 0
  202.             DO
  203.                 o = m.OFFSET + i * ES
  204.                 o1 = m.OFFSET + (i + gap) * ES
  205.                 _MEMGET m, o, T7a
  206.                 _MEMGET m, o1, T7b
  207.                 IF T7a > T7b THEN
  208.                     T7c = T7b
  209.                     _MEMPUT m, o1, T7a
  210.                     _MEMPUT m, o, T7c
  211.                     swapped = -1
  212.                 END IF
  213.                 i = i + 1
  214.             LOOP UNTIL i + gap > EC
  215.         LOOP UNTIL gap = 1 AND swapped = false
  216.     CASE 8 '_INTEGER64
  217.         DIM T8a AS _INTEGER64, T8b AS _INTEGER64
  218.         gap = EC
  219.         DO
  220.             gap = 10 * gap \ 13
  221.             IF gap < 1 THEN gap = 1
  222.             i = 0
  223.             swapped = 0
  224.             DO
  225.                 o = m.OFFSET + i * 8
  226.                 o1 = m.OFFSET + (i + gap) * 8
  227.                 IF _MEMGET(m, o, _INTEGER64) > _MEMGET(m, o1, _INTEGER64) THEN
  228.                     _MEMGET m, o1, T8a
  229.                     _MEMGET m, o, T8b
  230.                     _MEMPUT m, o1, T8b
  231.                     _MEMPUT m, o, T8a
  232.                     swapped = -1
  233.                 END IF
  234.                 i = i + 1
  235.             LOOP UNTIL i + gap > EC
  236.         LOOP UNTIL gap = 1 AND swapped = 0
  237.     CASE 11: '_UNSIGNED _BYTE
  238.         DIM temp11(0 TO 255) AS _UNSIGNED LONG
  239.         DIM t11 AS _UNSIGNED _BYTE
  240.         i = 0
  241.         DO
  242.             _MEMGET m, m.OFFSET + i, t11
  243.             temp11(t11) = temp11(t11) + 1
  244.             i = i + 1
  245.         LOOP UNTIL i > EC
  246.         i1 = 0
  247.         DO
  248.             DO UNTIL temp11(i1) = 0
  249.                 _MEMPUT m, m.OFFSET + counter, i1 AS _UNSIGNED _BYTE
  250.                 counter = counter + 1
  251.                 temp11(i1) = temp11(i1) - 1
  252.                 IF counter > EC THEN EXIT SUB
  253.             LOOP
  254.             i1 = i1 + 1
  255.         LOOP UNTIL i1 > 255
  256.     CASE 12 '_UNSIGNED INTEGER
  257.         DIM temp12(0 TO 65535) AS _UNSIGNED LONG
  258.         DIM t12 AS _UNSIGNED INTEGER
  259.         i = 0
  260.         DO
  261.             _MEMGET m, m.OFFSET + i * 2, t12
  262.             temp12(t12) = temp12(t12) + 1
  263.             i = i + 1
  264.         LOOP UNTIL i > EC
  265.         i1 = 0
  266.         DO
  267.             DO UNTIL temp12(i1) = 0
  268.                 _MEMPUT m, m.OFFSET + counter * 2, i1 AS _UNSIGNED INTEGER
  269.                 counter = counter + 1
  270.                 temp12(i1) = temp12(i1) - 1
  271.                 IF counter > EC THEN EXIT SUB
  272.             LOOP
  273.             i1 = i1 + 1
  274.         LOOP UNTIL i1 > 65535
  275.     CASE 14 '_UNSIGNED LONG
  276.         DIM T14a AS _UNSIGNED LONG, T14b AS _UNSIGNED LONG
  277.         gap = EC
  278.         DO
  279.             gap = 10 * gap \ 13
  280.             IF gap < 1 THEN gap = 1
  281.             i = 0
  282.             swapped = 0
  283.             DO
  284.                 o = m.OFFSET + i * 4
  285.                 o1 = m.OFFSET + (i + gap) * 4
  286.                 IF _MEMGET(m, o, _UNSIGNED LONG) > _MEMGET(m, o1, _UNSIGNED LONG) THEN
  287.                     _MEMGET m, o1, T14a
  288.                     _MEMGET m, o, T14b
  289.                     _MEMPUT m, o1, T14b
  290.                     _MEMPUT m, o, T14a
  291.                     swapped = -1
  292.                 END IF
  293.                 i = i + 1
  294.             LOOP UNTIL i + gap > EC
  295.         LOOP UNTIL gap = 1 AND swapped = 0
  296.     CASE 18: '_UNSIGNED _INTEGER64
  297.         gap = EC
  298.         DO
  299.             gap = 10 * gap \ 13
  300.             IF gap < 1 THEN gap = 1
  301.             i = 0
  302.             swapped = 0
  303.             DO
  304.                 o = m.OFFSET + i * 8
  305.                 o1 = m.OFFSET + (i + gap) * 8
  306.                 IF _MEMGET(m, o, _UNSIGNED _INTEGER64) > _MEMGET(m, o1, _UNSIGNED _INTEGER64) THEN
  307.                     _MEMGET m, o1, T18a
  308.                     _MEMGET m, o, T18b
  309.                     _MEMPUT m, o1, T18b
  310.                     _MEMPUT m, o, T18a
  311.                     swapped = -1
  312.                 END IF
  313.                 i = i + 1
  314.             LOOP UNTIL i + gap > EC
  315.         LOOP UNTIL gap = 1 AND swapped = 0

It used to be buggy in 64-bit versions of QB64, but that was a glitch with QB64 itself and packing type data across various OSes.  Since that's been fixed, it's been working as a charm for me.  :D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #6 on: September 05, 2018, 06:39:30 pm »
These are methods meant to be used and adapted easily to any architecture, from the classical sorting algorithms to the exotic. Yes, there are a few specialized algorithms that only deal with very small-range numbers, but these are limited in their use and excluded from testing beyond initial verification runs. The code for this library is not meant as a contest, but if you must, yours for double is orders of magnitude slower than MergeInsert at 8,388,608 elements (also verified for correctness). Yes, it's fast for _BYTE, INTEGER and such, but there are better algorithms for other data types past this. Compared against in-place algorithms, yours finished roughly on par with PrimeGapSort2(), invented a while back by me and improved in concert by Zom-B and me. Feel free to use any you like.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #7 on: September 05, 2018, 07:14:50 pm »
_MEMSORT is primarily a Combsort, which is fast enough in most instances for people.  Its main allure for me is the flexibility which it offers for simply plug-and-play usage.  One SUB included in your library, and it's good to go for multiple data types, without the need to have a dozen sort routines with different names to keep up with.  (SORT routines would *really* benefit from function overloading, if QB64 ever starts to support it.)

If you find various other sorts to always be faster (as you mentioned MergeInsert for DOUBLE), they can always be substituted for the Combsort which the routine currently uses.  When I designed the sort, it was designed for versatility and enough speed to handle the majority of tasks, but there's always room for improvement in anything.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #8 on: October 06, 2018, 12:23:07 am »
For randomized datasets, this is roughly 12% faster than standard QuickSort choosing random pivots in the interval (start -> finish) inclusive. This algorithm is also faster than standard Median of Three strategy and because of randomized pivot selection, impervious to Median of Three killer sequences and equally immune to QuickSort Adversary for fixed-position pivot selection. Still no competition for FlashSort and HashListSort for numeric data, just like other Quick family sorts, but 12% improvement versus the second-fastest in the Quick family sorts, for an algorithm easily extended to string types, it's significantly better. Also MergeInsert() included in my library using EfficientMerge(), requiring n/2 versus N auxiliary is a significant improvement over standard MergeSort and is stable, as well as adaptable to other non-numeric data types, both important qualities in cases where this property and a more compact memory footprint is absolutely necessary. Enjoy. EDIT Just a final note about this algorithm. I have seen NO hybridization of this sort ANYWHERE. This code developed and verified through exhaustive testing on monotonic, ascending, descending and randomly generated arrays. Also, using my library ONLY the QuickSortIterativeMedianOf3Randomized() sub itself is necessary. All other methods are already contained in this library.
Code: QB64: [Select]
  1. SUB QuickSortIterativeMedianOf3Randomized (CGSortLibArr() AS DOUBLE, Start&, Finish&, order&)
  2.     DIM compare AS DOUBLE
  3.     QSM3InsertionThreshold& = 14
  4.     MinStack& = 2 * LOG(Finish& - Start& + 1) \ LOG(2) + 1
  5.     DIM LStack&(MinStack&, 1)
  6.     StackPtr& = 0
  7.     LStack&(StackPtr&, 0) = Start&
  8.     LStack&(StackPtr&, 1) = Finish&
  9.     DO
  10.         Low& = LStack&(StackPtr&, 0)
  11.         high& = LStack&(StackPtr&, 1)
  12.         DO
  13.             '* one more tactic to help defeat O(n^2) worst-case performance
  14.             '* pick a RANDOM pivot. Use of fixed pivot 1/2 distance from Low&+(high&-low&)\2 MAY result in infinite loop
  15.             '* or less than desirable O(N^2) performance.
  16.             SELECT CASE high& - Low&
  17.                 CASE 1
  18.                     SELECT CASE order&
  19.                         CASE 1
  20.                             IF CGSortLibArr(Low&) > CGSortLibArr(high&) THEN
  21.                                 SWAP CGSortLibArr(Low&), CGSortLibArr(high&)
  22.                             END IF
  23.                         CASE ELSE
  24.                             IF CGSortLibArr(Low&) > CGSortLibArr(high&) THEN
  25.                                 SWAP CGSortLibArr(Low&), CGSortLibArr(high&)
  26.                             END IF
  27.                     END SELECT
  28.                     EXIT DO
  29.                 CASE 2
  30.                     MedianOfThree CGSortLibArr(), Low&, Low& + 1, high&, medianIndex&
  31.                     compare = CGSortLibArr(medianIndex&)
  32.                 CASE 3 TO QSM3InsertionThreshold&
  33.                     InsertionSort CGSortLibArr(), Low&, high&, order&
  34.                     'nq& = (j& - i&) \ 2
  35.                     'i& = j& - nq& + 1
  36.                     'j& = i& + nq& - 1
  37.                     EXIT DO
  38.                 CASE IS > QSM3InsertionThreshold&
  39.                     MedianOfThree CGSortLibArr(), Low&, Low& + 1 + INT(RND * (high& - Low& - 2)), high&, medianIndex&
  40.                     compare = CGSortLibArr(medianIndex&)
  41.             END SELECT
  42.             i& = Low&
  43.             j& = high&
  44.             'IF high& - Low& >= 2 THEN
  45.             '    MedianOfThree CGSortLibArr(), Low&, Low& + (high& - Low&) \ 2, high&, medianIndex&
  46.             '    compare = CGSortLibArr(medianIndex&)
  47.             'ELSE
  48.             '    compare = CGSortLibArr(Low& + (high& - Low&) \ 2)
  49.             'END IF
  50.             SELECT CASE order&
  51.                 CASE 1
  52.                     DO
  53.                         DO WHILE CGSortLibArr(i&) < compare
  54.                             i& = i& + 1
  55.                         LOOP
  56.                         DO WHILE CGSortLibArr(j&) > compare
  57.                             j& = j& - 1
  58.                         LOOP
  59.                         IF i& <= j& THEN
  60.                             SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  61.                             i& = i& + 1
  62.                             j& = j& - 1
  63.                         END IF
  64.                     LOOP UNTIL i& > j&
  65.                 CASE ELSE
  66.                     DO
  67.                         DO WHILE CGSortLibArr(i&) > compare
  68.                             i& = i& + 1
  69.                         LOOP
  70.                         DO WHILE CGSortLibArr(j&) < compare
  71.                             j& = j& - 1
  72.                         LOOP
  73.                         IF i& < j& THEN
  74.                             SWAP CGSortLibArr(i&), CGSortLibArr(j&)
  75.                             i& = i& + 1
  76.                             j& = j& - 1
  77.                         ELSEIF i& = j& THEN
  78.                             i& = i& + 1
  79.                             j& = j& - 1
  80.                         END IF
  81.                     LOOP UNTIL i& > j&
  82.             END SELECT
  83.             IF j& - Low& < high& - i& THEN
  84.                 IF i& < high& THEN
  85.                     LStack&(StackPtr&, 0) = i&
  86.                     LStack&(StackPtr&, 1) = high&
  87.                     StackPtr& = StackPtr& + 1
  88.                 END IF
  89.                 high& = j&
  90.             ELSE
  91.                 IF Low& < j& THEN
  92.                     LStack&(StackPtr&, 0) = Low&
  93.                     LStack&(StackPtr&, 1) = j&
  94.                     StackPtr& = StackPtr& + 1
  95.                 END IF
  96.                 Low& = i&
  97.             END IF
  98.         LOOP WHILE Low& < high&
  99.         StackPtr& = StackPtr& - 1
  100.     LOOP UNTIL StackPtr& < 0
  101.  
  102. '* For QuickSort using the median of three partitioning method. Used to defeat "QuickSort Killer" arrays.
  103. SUB MedianOfThree (CGSortLibArr() AS DOUBLE, MotA AS LONG, MotB AS LONG, MotC AS LONG, MedianIndex AS LONG)
  104.     IF CGSortLibArr(MotA) > CGSortLibArr(MotB) THEN
  105.         IF CGSortLibArr(MotA) < CGSortLibArr(MotC) THEN
  106.             MedianIndex = MotA
  107.         ELSEIF CGSortLibArr(MotB) > CGSortLibArr(MotC) THEN
  108.             MedianIndex = MotB
  109.         ELSE
  110.             MedianIndex = MotC
  111.         END IF
  112.     ELSE
  113.         IF CGSortLibArr(MotA) > CGSortLibArr(MotC) THEN
  114.             MedianIndex = MotA
  115.         ELSEIF CGSortLibArr(MotB) < CGSortLibArr(MotC) THEN
  116.             MedianIndex = MotB
  117.         ELSE
  118.             MedianIndex = MotC
  119.         END IF
  120.     END IF
  121.  
  122. SUB InsertionSort (CGSortLibArr() AS DOUBLE, start AS LONG, finish AS LONG, order&)
  123.     DIM InSort_Local_ArrayTemp AS DOUBLE
  124.     DIM InSort_Local_i AS LONG
  125.     DIM InSort_Local_j AS LONG
  126.     SELECT CASE order&
  127.         CASE 1
  128.             FOR InSort_Local_i = start + 1 TO finish
  129.                 InSort_Local_j = InSort_Local_i - 1
  130.                 IF CGSortLibArr(InSort_Local_i) < CGSortLibArr(InSort_Local_j) THEN
  131.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  132.                     DO UNTIL InSort_Local_j < start
  133.                         IF (InSort_Local_ArrayTemp < CGSortLibArr(InSort_Local_j)) THEN
  134.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  135.                             InSort_Local_j = InSort_Local_j - 1
  136.                         ELSE
  137.                             EXIT DO
  138.                         END IF
  139.                     LOOP
  140.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  141.                 END IF
  142.             NEXT
  143.         CASE ELSE
  144.             FOR InSort_Local_i = start + 1 TO finish
  145.                 InSort_Local_j = InSort_Local_i - 1
  146.                 IF CGSortLibArr(InSort_Local_i) > CGSortLibArr(InSort_Local_j) THEN
  147.                     InSort_Local_ArrayTemp = CGSortLibArr(InSort_Local_i)
  148.                     DO UNTIL InSort_Local_j < start
  149.                         IF (InSort_Local_ArrayTemp > CGSortLibArr(InSort_Local_j)) THEN
  150.                             CGSortLibArr(InSort_Local_j + 1) = CGSortLibArr(InSort_Local_j)
  151.                             InSort_Local_j = InSort_Local_j - 1
  152.                         ELSE
  153.                             EXIT DO
  154.                         END IF
  155.                     LOOP
  156.                     CGSortLibArr(InSort_Local_j + 1) = InSort_Local_ArrayTemp
  157.                 END IF
  158.             NEXT
  159.     END SELECT
  160.  
« Last Edit: October 06, 2018, 02:45:53 am by codeguy »

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: CodeGuy Standard Sorting Library (Because QB64 dot net happens)
« Reply #9 on: October 06, 2018, 12:42:05 am »
Another code snippet to add to my collection ... check.

I'm glad there are people on this site much smarter than me with these types of algorithms. I can barely bubble-sort ... you and Steve rock!
In order to understand recursion, one must first understand recursion.