home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 6_2008-2009.ISO / data / zips / A_fast_met2161158282009.psc / RemDupes / modStableQuick.bas < prev   
BASIC Source File  |  2009-08-22  |  48KB  |  822 lines

  1. Attribute VB_Name = "modStableQuick"
  2. Option Explicit                           ' -⌐Rd 2006/2008-
  3.  
  4. ' + Stable QuickSort 2.3 +++++++++++++++++++++++++++++++++++++++++++
  5.  
  6. ' These sorting routines have the following features:
  7.  
  8. ' - They can handle sorting arrays of millions of string items.
  9. ' - They can handle sorting in ascending and descending order.
  10. ' - They can handle case-sensitive and case-insensitive criteria.
  11. ' - They can handle zero or higher based arrays.
  12. ' - They can handle negative lb and positive ub.
  13. ' - They can handle negative lb and zero or negative ub.
  14. ' - They can sort sub-sets of the array data.
  15.  
  16. ' + Background +++++++++++++++++++++++++++++++++++++++++++++++++++++
  17.  
  18. ' This is a non-recursive quicksort based algorithm that has been written from
  19. ' the ground up as a stable alternative to the blindingly fast quicksort.
  20.  
  21. ' It is not quite as fast as the outright fastest non-stable quicksort, but is
  22. ' still very fast as it uses buffers and copymemory and is beaten by none of my
  23. ' other string sorting algorithms except my fastest non-stable quicksort.
  24.  
  25. ' A standard quicksort only moves items that need swapping, while this stable
  26. ' algorithm manipulates all items on every iteration to keep them all in relative
  27. ' positions to one another. This algorithm I have dubbed the Avalanche⌐.
  28.  
  29. ' It is also important to note that this algorithm does not suffer at all from
  30. ' the traditional quicksort nemesis. It is in fact much faster at re-sorting data
  31. ' that has been pre-sorted, and sorting data with many repeated items, than most
  32. ' other sorting algorithms as it has been highly optimized for these data states!
  33.  
  34. ' + Version 2.1 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  35.  
  36. ' This is a re-working of my stable quicksort algorithm.
  37.  
  38. ' A runner section has been added to handle a very hard job for a stable sorter;
  39. ' reverse pretty sorting. This is case-insensitive sorting of data that has been
  40. ' pre-sorted case-sensitively in reverse order - lower-case first in ascending
  41. ' order, or capitals first in descending order.
  42.  
  43. ' It utilises a runner technique to boost this very demanding operation,
  44. ' down from 2.0 to 1.5 seconds on 100,000 items on my 866 MHz P3.
  45. ' Adding runners has also boosted same-direction pretty sorting operations.
  46.  
  47. ' Because all items are re-positioned based on the current value it can identify
  48. ' when the avalanche process is producing a zero count buffer one way and so is
  49. ' moving all items the other way, indicating that the data is in a pre-sorted state
  50. ' (shifting no items up/down in relation to the current item).
  51.  
  52. ' On each iteration a test of the buffer counts can identify when it is re-sorting
  53. ' or reverse-sorting, as well as producing distinctive indicators on reverse-pretty
  54. ' and same-direction pretty sorting operations. The range becomes very small on
  55. ' unsorted data before a small range produces a zero count buffer, so small ranges
  56. ' are ignored to skip false indicators.
  57.  
  58. ' So when performing a pretty or reverse-pretty operation the code can identify
  59. ' this state and the runners are turned on automatically.
  60.  
  61. ' + Version 2.2 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  62.  
  63. ' This version identifies sub-sets of pre-sorted data and delegates it to
  64. ' a built-in insert/binary hybrid algorithm dubbed the Twister⌐.
  65.  
  66. ' This delegation is the sole reason for the speed boost on all operations
  67. ' over version 2.1, and also the reason for the incredibly fast refresh
  68. ' sorting performance - it can refresh-sort 3,248,230 pre-sorted strings
  69. ' in around 2 and a half seconds on my 866MHz P3.
  70.  
  71. ' + Version 2.25 +++++++++++++++++++++++++++++++++++++++++++++++++++
  72.  
  73. ' Interim version 2.25 added safe addition and subtraction of unsigned
  74. ' long integers.
  75.  
  76. ' This guarantees safe arithmetic operations on memory address pointers
  77. ' which are used extensively by the runner sections of code.
  78.  
  79. ' This change imposed a slight performance degradation on all operations.
  80.  
  81. ' + Version 2.3 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  82.  
  83. ' The latest version of this algorithm employs a SAFEARRAY substitution
  84. ' technique to trick VB into thinking the four-byte string pointers in
  85. ' the string array are just VB longs in a native VB long array.
  86.  
  87. ' The technique simply uses CopyMemory to point a VB long array (defined
  88. ' in the module) at the first of the string pointers in memory, and sets
  89. ' its lower-bound and item count to match (as if it had been redimmed).
  90.  
  91. ' This allows us to treat the string pointers as if they were simply
  92. ' four-byte long values in a long array and can be swapped around as
  93. ' needed without touching the actual strings that are pointed to.
  94.  
  95. ' Reading and assigning to a VB long array is lightning fast, and proves
  96. ' to be considerably faster when copying only one item than the previous
  97. ' method of copying the string pointers using CopyMemory.
  98.  
  99. ' This stable algorithm is truely very fast at all sorting operations!
  100.  
  101. ' + Indexed Version ++++++++++++++++++++++++++++++++++++++++++++++++
  102.  
  103. ' This version receives a dynamic long array that holds references to the
  104. ' string arrays indices. This is known as an indexed sort. No changes are
  105. ' made to the source string array.
  106.  
  107. ' The index array is automatically initialized if it is passed erased or
  108. ' uninitialized. The index array can be passed again for sorting without
  109. ' erasing it.
  110.  
  111. ' After a sort procedure is run the long array is ready as a sorted index
  112. ' (lookup table) to the string array items.
  113.  
  114. ' E.G strA(idxA(lo)) returns the lo item in the string array whose index
  115. ' may be anywhere in the string array.
  116.  
  117. ' Usage Details:
  118.  
  119. ' The index array can be redimmed to match the source string array boundaries
  120. ' or it can be erased or left uninitialized before sorting a string array for
  121. ' the first time. However, if you modify string items and re-sort you should
  122. ' not redim or erase the index array which will take advantage of the fast
  123. ' refresh sorting performance. This also allows the index array to be passed
  124. ' on to other sorting processes to be further manipulated.
  125.  
  126. ' Even when using redim with the preserve keyword and adding more items to the
  127. ' string array you can pass the index array unchanged and the new items will be
  128. ' sorted into the previously sorted array. The index array will automatically
  129. ' return with boundaries matching the string array boundaries.
  130.  
  131. ' Only when you reload the string array items with new array boundaries should
  132. ' you erase the index array for the first sorting operation. Also, if you redim
  133. ' the source string array to smaller boundaries you should erase the index array
  134. ' before sorting the new smaller data set for the first time.
  135.  
  136. ' See the header comments for ValidateIndexArray for more details.
  137.  
  138. ' + Licence Agreement ++++++++++++++++++++++++++++++++++++++++++++++
  139.  
  140. ' You are free to use any part or all of this code even for commercial
  141. ' purposes in any way you wish under the one condition that no copyright
  142. ' notice is moved or removed from where it is.
  143.  
  144. ' For comments, suggestions or bug reports you can contact me at:
  145. ' rdòedwardsòbigpondòcom.
  146.  
  147. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  148.  
  149. ' Declare some CopyMemory Alias's (thanks Bruce :)
  150. Private Declare Sub CopyMemByV Lib "kernel32" Alias "RtlMoveMemory" (ByVal lpDest As Long, ByVal lpSrc As Long, ByVal lByteLen As Long)
  151. Private Declare Sub CopyMemByR Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal lByteLen As Long)
  152.  
  153. ' More efficient repeated use of numeric literals
  154. Private Const n0 = 0&, n1 = 1&, n2 = 2&, n3 = 3&, n4 = 4&, n5 = 5&, n6 = 6&
  155. Private Const n7 = 7&, n8 = 8&, n12 = 12&, n16 = 16&, n32 = 32&, n64 = 64&
  156. Private Const n10K As Long = 10000&
  157. Private Const n20K As Long = 20000&
  158. Private Const n50K As Long = 50000
  159.  
  160. Private Const rRunner4 As Single = 0.0025 '0.002<<reverse-sorting-0.003-unsorted>>0.004
  161. Private Const rRunner5 As Single = 0.0015 '0.001<<reverse-sorting-unsorted>>0.002
  162.  
  163. ' Used for unsigned arithmetic
  164. Private Const DW_MSB = &H80000000 ' DWord Most Significant Bit
  165.  
  166. Private Enum SAFEATURES
  167.     FADF_AUTO = &H1               ' Array is allocated on the stack
  168.     FADF_STATIC = &H2             ' Array is statically allocated
  169.     FADF_EMBEDDED = &H4           ' Array is embedded in a structure
  170.     FADF_FIXEDSIZE = &H10         ' Array may not be resized or reallocated
  171.     FADF_BSTR = &H100             ' An array of BSTRs
  172.     FADF_UNKNOWN = &H200          ' An array of IUnknown*
  173.     FADF_DISPATCH = &H400         ' An array of IDispatch*
  174.     FADF_VARIANT = &H800          ' An array of VARIANTs
  175.     FADF_RESERVED = &HFFFFF0E8    ' Bits reserved for future use
  176.     #If False Then
  177.         Dim FADF_AUTO, FADF_STATIC, FADF_EMBEDDED, FADF_FIXEDSIZE, FADF_BSTR, FADF_UNKNOWN, FADF_DISPATCH, FADF_VARIANT, FADF_RESERVED
  178.     #End If
  179. End Enum
  180. Private Const VT_BYREF = &H4000&  ' Tests whether the InitedArray routine was passed a Variant that contains an array, rather than directly an array in the former case ptr already points to the SA structure. Thanks to Monte Hansen for this fix
  181. Private Const FADF_NO_REDIM = FADF_AUTO Or FADF_FIXEDSIZE
  182.  
  183. Private Type SAFEARRAY
  184.     cDims       As Integer        ' Count of dimensions in this array
  185.     fFeatures   As Integer        ' Bitfield flags indicating attributes of a particular array
  186.     cbElements  As Long           ' Byte size of each element of the array
  187.     cLocks      As Long           ' Number of times the array has been locked without corresponding unlock
  188.     pvData      As Long           ' Pointer to the start of the array data (use only if cLocks > 0)
  189.     cElements   As Long           ' Count of elements in this dimension
  190.     lLBound     As Long           ' The lower-bounding index of this dimension
  191.     lUBound     As Long           ' The upper-bounding index of this dimension
  192. End Type
  193.  
  194. Private StringPtrs_Header As SAFEARRAY
  195. Private StringPtrs() As Long
  196.  
  197. Private ssLb() As Long, ssUb() As Long, ssMax As Long  ' Avalanche pending boundary stacks
  198. Private psLb() As Long, psUb() As Long, psMax As Long  ' Stable presorter boundary stacks
  199. Private lA_1() As Long, lA_2() As Long, ssBuf As Long  ' Stable quicksort working buffers
  200. Private twLb() As Long, twUb() As Long, twMax As Long  ' Twister runner stacks
  201. Private twBuf() As Long, twBufMax As Long              ' Twister copymemory buffer
  202.  
  203. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  204.  
  205. Public Enum eSortOrder
  206.     Descending = -1&
  207.     Default = 0&
  208.     Ascending = 1&
  209.     #If False Then
  210.         Dim Descending, Default, Ascending
  211.     #End If
  212. End Enum
  213.  
  214. Public Enum eCompareMethod
  215.     BinaryCompare = &H0
  216.     TextCompare = &H1
  217.     #If False Then
  218.         Dim BinaryCompare, TextCompare
  219.     #End If
  220. End Enum
  221.  
  222. Public Enum eCompareResult
  223.     Lesser = -1&
  224.     Equal = 0&
  225.     Greater = 1&
  226.     #If False Then
  227.         Dim Lesser, Equal, Greater
  228.     #End If
  229. End Enum
  230.  
  231. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  232.  
  233. Private Const Default_Order As Long = Ascending
  234. Private mMethod As eCompareMethod
  235. Private mOrder As eSortOrder
  236.  
  237. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  238.  
  239. ' The following properties should be set before sorting.
  240.  
  241. Property Get SortOrder() As eSortOrder
  242.     If mOrder = Default Then mOrder = Default_Order
  243.     SortOrder = mOrder
  244. End Property
  245.  
  246. Property Let SortOrder(ByVal eNewOrder As eSortOrder)
  247.     If eNewOrder = Default Then
  248.         If mOrder = Default Then mOrder = Default_Order
  249.     Else
  250.         mOrder = eNewOrder
  251.     End If
  252. End Property
  253.  
  254. Property Get SortMethod() As eCompareMethod
  255.     SortMethod = mMethod
  256. End Property
  257.  
  258. Property Let SortMethod(ByVal eNewMethod As eCompareMethod)
  259.     mMethod = eNewMethod
  260. End Property
  261.  
  262. ' + Stable QuickSort v2.3 +++++++++++++++++++++++++++++++++++++++++++++
  263.  
  264. ' This is a non-recursive quicksort based algorithm that has been written from
  265. ' the ground up as a stable alternative to the blindingly fast quicksort.
  266.  
  267. Sub strStableSort2(sA() As String, ByVal lbA As Long, ByVal ubA As Long)
  268.     ' This is an even faster stable non-recursive quicksort
  269.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  270.     Dim item As String, lpStr As Long, lpS As Long
  271.     Dim ptr1 As Long, ptr2 As Long, cnt As Long
  272.     Dim idx As Long, opt As Long, pvt As Long
  273.     Dim walk As Long, find As Long, midd As Long
  274.     Dim base As Long, run As Long, cast As Long
  275.     Dim ceil As Long, mezz As Long
  276.     Dim inter1 As Long, inter2 As Long
  277.     Dim lpL_1 As Long, lpL_2 As Long
  278.     Dim lItem As Long, lpSA As Long
  279.     Dim eComp As eSortOrder
  280.  
  281.     cnt = ubA - lbA + n1              ' Grab array item count
  282.     If (cnt < n2) Then Exit Sub       ' If nothing to do then exit
  283.     eComp = SortOrder                 ' Initialize compare variable
  284.     pvt = (cnt \ n64) + n32           ' Allow for worst case senario + some
  285.  
  286.     lpSA = SubstArrayHeader(sA, lbA, ubA)
  287.     InitializeStacks ssLb, ssUb, ssMax, pvt  ' Initialize pending boundary stacks
  288.     InitializeStacks twLb, twUb, twMax, pvt  ' Initialize pending runner stacks
  289.     InitializeStacks lA_1, lA_2, ssBuf, cnt  ' Initialize working buffers
  290.  
  291.     lpL_1 = VarPtr(lA_1(n0))                 ' Cache pointer to lower buffer
  292.     lpL_2 = VarPtr(lA_2(n0))                 ' Cache pointer to upper buffer
  293.     lpStr = VarPtr(item)                     ' Cache pointer to the string variable
  294.     lpS = Sum(VarPtr(sA(lbA)), -(lbA * n4))  ' Cache pointer to the string array
  295.  
  296.     cnt = n0
  297.     Do: ptr1 = n0: ptr2 = n0
  298.         pvt = ((ubA - lbA) \ n2) + lbA       ' Get pivot index position
  299.         lItem = StringPtrs(pvt)              ' Grab current value into item
  300.         CopyMemByR ByVal lpStr, lItem, n4
  301.  
  302.         For idx = lbA To pvt - n1
  303.             If (StrComp(sA(idx), item, mMethod) = eComp) Then ' (idx > item)
  304.                 lA_2(ptr2) = StringPtrs(idx) ' 3
  305.                 ptr2 = ptr2 + n1
  306.             Else
  307.                 lA_1(ptr1) = StringPtrs(idx) ' 1
  308.                 ptr1 = ptr1 + n1
  309.             End If
  310.         Next
  311.         inter1 = ptr1: inter2 = ptr2
  312.         For idx = pvt + n1 To ubA
  313.             If (StrComp(item, sA(idx), mMethod) = eComp) Then ' (idx < item)
  314.                 lA_1(ptr1) = StringPtrs(idx) ' 2
  315.                 ptr1 = ptr1 + n1
  316.             Else
  317.                 lA_2(ptr2) = StringPtrs(idx) ' 4
  318.                 ptr2 = ptr2 + n1
  319.             End If
  320.         Next '-Avalanche v2 ⌐Rd-
  321.         CopyMemByV Sum(lpS, lbA * n4), lpL_1, ptr1 * n4 ' 1 2 item 3 4
  322.         StringPtrs(lbA + ptr1) = lItem       ' Re-assign current item
  323.         CopyMemByV Sum(lpS, (lbA + ptr1 + n1) * n4), lpL_2, ptr2 * n4
  324.  
  325.         If (ubA - lbA < n64) Then            ' Ignore false indicators
  326.             If (inter1 = n0) Then            ' Reverse indicator
  327.             ElseIf (ubA - lbA < n3) Then     ' Delegate to built-in Repeater on tiny chuncks
  328.                 walk = lbA
  329.                 Do Until walk = ubA
  330.                     walk = walk + n1
  331.                     CopyMemByV lpStr, Sum(lpS, walk * n4), n4 ' item = sA(walk)
  332.                     find = walk
  333.                     Do While StrComp(sA(find - n1), item, mMethod) = eComp
  334.                         find = find - n1
  335.                         If (find = lbA) Then Exit Do
  336.                     Loop '-Repeater v45c ⌐Rd-
  337.                     If (find < walk) Then
  338.                         CopyMemByV Sum(lpS, (find + n1) * n4), Sum(lpS, find * n4), (walk - find) * n4
  339.                         CopyMemByV Sum(lpS, find * n4), lpStr, n4 ' Move items up 1, sA(find) = item
  340.                 End If: Loop
  341.                 ptr1 = n0: ptr2 = n0
  342.             End If
  343.         ElseIf (inter1 = n0) Then
  344.             If (inter2 = ptr2) Then          ' Reverse
  345.             ElseIf (ptr1 = n0) Then          ' Reverse Pretty
  346.                 If (ptr1 > inter1) And (inter1 < n50K) Then                    ' Runners dislike super large ranges
  347.                     CopyMemByR ByVal lpStr, StringPtrs(lbA + ptr1 - n1), n4
  348.                     opt = lbA + (inter1 \ n2)
  349.                     run = lbA + inter1
  350.                     Do While run > opt                                         ' Runner do loop
  351.                         If Not (StrComp(sA(run - n1), item, mMethod) = eComp) Then Exit Do
  352.                         run = run - n1
  353.                     Loop: cast = lbA + inter1 - run
  354.                     If cast Then
  355.                         CopyMemByV lpL_1, Sum(lpS, run * n4), cast * n4        ' Grab items that stayed below current that should also be above items that have moved down below current
  356.                         CopyMemByV Sum(lpS, run * n4), Sum(lpS, (lbA + inter1) * n4), (ptr1 - inter1) * n4 ' Move down items
  357.                         CopyMemByV Sum(lpS, (lbA + ptr1 - cast - n1) * n4), lpL_1, cast * n4 ' Re-assign items into position immediately below current item
  358.                     End If
  359.                 End If ' 1 2 1r item 4r 3 4
  360.                 If (inter2) And (ptr2 - inter2 < n50K) Then
  361.                     base = lbA + ptr1 + n1
  362.                     CopyMemByR ByVal lpStr, StringPtrs(base), n4
  363.                     pvt = lbA + ptr1 + inter2
  364.                     opt = pvt + ((ptr2 - inter2) \ n2)
  365.                     run = pvt
  366.                     Do While run < opt                                         ' Runner do loop
  367.                         If Not (StrComp(sA(run + n1), item, mMethod) = eComp) Then Exit Do
  368.                         run = run + n1
  369.                     Loop: cast = run - pvt
  370.                     If cast Then
  371.                         CopyMemByV lpL_1, Sum(lpS, (pvt + n1) * n4), cast * n4 ' Grab items that stayed above current that should also be below items that have moved up above current
  372.                         CopyMemByV Sum(lpS, (base + cast) * n4), Sum(lpS, base * n4), inter2 * n4 ' Move up items
  373.                         CopyMemByV Sum(lpS, base * n4), lpL_1, cast * n4       ' Re-assign items into position immediately above current item
  374.             End If: End If: End If
  375.         ElseIf (inter2 = n0) Then
  376.             If (inter1 = ptr1) Then          ' Refresh
  377.                 ' Delegate to built-in Insert/Binary hybrid on ideal data state
  378.                 walk = lbA: mezz = ubA: idx = n0                              ' Initialize our walker variables
  379.                 opt = GetOptimalRange(ubA - lbA + n1)                         ' Get runners optimal range
  380.                 If opt > twMax Then InitializeStacks twLb, twUb, twMax, opt   ' Ensure enough stack space
  381.                 Do While walk < mezz ' ----==============================---- ' Do the twist while there's more items
  382.                     walk = walk + n1                                          ' Walk up the array and use binary search to insert each item down into the sorted lower array
  383.                     CopyMemByV lpStr, Sum(lpS, walk * n4), n4                 ' Grab current value into item
  384.                     find = walk                                               ' Default to current position
  385.                     ceil = walk - n1                                          ' Set ceiling to current position - 1
  386.                     base = lbA                                                ' Set base to lower bound
  387.                     Do While StrComp(sA(ceil), item, mMethod) = eComp   '  .  ' While current item must move down
  388.                         midd = (base + ceil) \ n2                             ' Find mid point
  389.                         Do Until StrComp(sA(midd), item, mMethod) = eComp     ' Step back up if below
  390.                             base = midd + n1                                  ' Bring up the base
  391.                             midd = (base + ceil) \ n2                         ' Find mid point
  392.                             If midd = ceil Then Exit Do                       ' If we're up to ceiling
  393.                         Loop                                                  ' Out of loop >= target pos
  394.                         find = midd                                           ' Set provisional to new ceiling
  395.                         If find = base Then Exit Do                           ' If we're down to base
  396.                         ceil = midd - n1                                      ' Bring down the ceiling
  397.                     Loop '-Twister v4 ⌐Rd-     .      . ...  .             .  ' Out of binary search loops
  398.                     If (find < walk) Then                                     ' If current item needs to move down
  399.                         CopyMemByV lpStr, Sum(lpS, find * n4), n4
  400.                         run = walk + n1
  401.                         Do Until run > mezz Or run - walk > opt               ' Runner do loop
  402.                             If Not (StrComp(item, sA(run), mMethod) = eComp) Then Exit Do
  403.                             run = run + 1
  404.                         Loop: cast = (run - walk)
  405.                         CopyMemByV lpL_2, Sum(lpS, walk * n4), cast * n4      ' Grab current value(s)
  406.                         CopyMemByV Sum(lpS, (find + cast) * n4), Sum(lpS, find * n4), (walk - find) * n4 ' Move up items
  407.                         CopyMemByV Sum(lpS, find * n4), lpL_2, cast * n4      ' Re-assign current value(s) into found pos
  408.                         If cast > n1 Then
  409.                             If Not run > mezz Then
  410.                                 idx = idx + n1
  411.                                 twLb(idx) = run - n1
  412.                                 twUb(idx) = mezz
  413.                             End If
  414.                             walk = find
  415.                             mezz = find + cast - n1
  416.                     End If: End If
  417.                     If walk = mezz Then
  418.                         If idx Then
  419.                             walk = twLb(idx)
  420.                             mezz = twUb(idx)
  421.                             idx = idx - n1
  422.                 End If: End If: Loop     ' Out of walker do loop
  423.                 ' ----==========----
  424.                 ptr1 = n0: ptr2 = n0
  425.             ElseIf (ptr2 = n0) Then      ' Pretty
  426.                 If (ptr1 > inter1) And (inter1 < n50K) Then                    ' Runners dislike super large ranges
  427.                     CopyMemByR ByVal lpStr, StringPtrs(lbA + ptr1 - n1), n4
  428.                     opt = lbA + (inter1 \ n2)
  429.                     run = lbA + inter1
  430.                     Do While run > opt                                         ' Runner do loop
  431.                         If Not (StrComp(sA(run - n1), item, mMethod) = eComp) Then Exit Do
  432.                         run = run - n1
  433.                     Loop: cast = lbA + inter1 - run
  434.                     If cast Then
  435.                         CopyMemByV lpL_1, Sum(lpS, run * n4), cast * n4        ' Grab items that stayed below current that should also be above items that have moved down below current
  436.                         CopyMemByV Sum(lpS, run * n4), Sum(lpS, (lbA + inter1) * n4), (ptr1 - inter1) * n4 ' Move down items
  437.                         CopyMemByV Sum(lpS, (lbA + ptr1 - cast - n1) * n4), lpL_1, cast * n4 ' Re-assign items into position immediately below current item
  438.                     End If
  439.                 End If ' 1 2 1r item 4r 3 4
  440.                 If (inter2) And (ptr2 - inter2 < n50K) Then
  441.                     base = lbA + ptr1 + n1
  442.                     CopyMemByR ByVal lpStr, StringPtrs(base), n4
  443.                     pvt = lbA + ptr1 + inter2
  444.                     opt = pvt + ((ptr2 - inter2) \ n2)
  445.                     run = pvt
  446.                     Do While run < opt                                         ' Runner do loop
  447.                         If Not (StrComp(sA(run + n1), item, mMethod) = eComp) Then Exit Do
  448.                         run = run + n1
  449.                     Loop: cast = run - pvt
  450.                     If cast Then
  451.                         CopyMemByV lpL_1, Sum(lpS, (pvt + n1) * n4), cast * n4 ' Grab items that stayed above current that should also be below items that have moved up above current
  452.                         CopyMemByV Sum(lpS, (base + cast) * n4), Sum(lpS, base * n4), inter2 * n4 ' Move up items
  453.                         CopyMemByV Sum(lpS, base * n4), lpL_1, cast * n4       ' Re-assign items into position immediately above current item
  454.         End If: End If: End If: End If
  455.  
  456.         If (ptr1 > n1) Then
  457.             If (ptr2 > n1) Then cnt = cnt + n1: ssLb(cnt) = lbA + ptr1 + n1: ssUb(cnt) = ubA
  458.             ubA = lbA + ptr1 - n1
  459.         ElseIf (ptr2 > n1) Then
  460.             lbA = lbA + ptr1 + n1
  461.         Else
  462.             If cnt = n0 Then Exit Do
  463.             lbA = ssLb(cnt): ubA = ssUb(cnt): cnt = cnt - n1
  464.         End If
  465.     Loop
  466.     CopyMemByR ByVal lpSA, 0&, n4  ' De-reference our pointer to safearray header
  467.     CopyMemByR ByVal lpStr, 0&, n4 ' De-reference our pointer to item variable
  468. End Sub
  469.  
  470. ' + Stable QuickSort v2.3 Indexed +++++++++++++++++++
  471.  
  472. ' This is an indexed stable non-recursive quicksort.
  473.  
  474. ' It uses a long array that holds references to the string arrays
  475. ' indices. This is known as an indexed sort. No changes are made
  476. ' to the source string array. This also allows the index array to
  477. ' be passed on to other sort processes to be further manipulated.
  478.  
  479. ' After a sort procedure is run the long array is ready as a sorted
  480. ' index (lookup table) to the string array items.
  481.  
  482. ' E.G sA(idxA(lo)) returns the lo item in the string array whose
  483. ' index may be anywhere in the string array.
  484.  
  485. Sub strStableSort2Indexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long)
  486.     ' This is my indexed stable non-recursive quicksort
  487.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  488.     Dim item As String, lpStr As Long, lpS As Long
  489.     Dim walk As Long, find As Long, midd As Long
  490.     Dim base As Long, run As Long, cast As Long
  491.     Dim idx As Long, opt As Long, pvt As Long
  492.     Dim ptr1 As Long, ptr2 As Long, cnt As Long
  493.     Dim ceil As Long, mezz As Long, lpB As Long
  494.     Dim inter1 As Long, inter2 As Long
  495.     Dim lpL_1 As Long, lpL_2 As Long
  496.     Dim idxItem As Long, lpI As Long
  497.     Dim eComp As eSortOrder
  498.  
  499.     cnt = ubA - lbA + n1                ' Grab array item count
  500.     If (cnt < n2) Then Exit Sub         ' If nothing to do then exit
  501.     eComp = SortOrder                   ' Initialize compare variable
  502.     pvt = (cnt \ n64) + n32             ' Allow for worst case senario + some
  503.  
  504.     ValidateIndexArray idxA, lbA, ubA         ' Validate the index array
  505.     InitializeStacks ssLb, ssUb, ssMax, pvt   ' Initialize pending boundary stacks
  506.     InitializeStacks twLb, twUb, twMax, pvt   ' Initialize pending runner stacks
  507.     InitializeStacks lA_1, lA_2, ssBuf, cnt   ' Initialize working buffers
  508.  
  509.     lpL_1 = VarPtr(lA_1(n0))                  ' Cache pointer to lower buffer
  510.     lpL_2 = VarPtr(lA_2(n0))                  ' Cache pointer to upper buffer
  511.     lpStr = VarPtr(item)                      ' Cache pointer to the string variable
  512.     lpS = Sum(VarPtr(sA(lbA)), -(lbA * n4))   ' Cache pointer to the string array
  513.     lpI = Sum(VarPtr(idxA(lbA)), -(lbA * n4)) ' Cache pointer to the index array
  514.  
  515.     cnt = n0
  516.     Do: ptr1 = n0: ptr2 = n0
  517.         pvt = ((ubA - lbA) \ n2) + lbA   ' Get pivot index position
  518.         idxItem = idxA(pvt)              ' Grab current value into item
  519.         CopyMemByV lpStr, Sum(lpS, idxItem * n4), n4
  520.  
  521.         For idx = lbA To pvt - n1
  522.             If (StrComp(sA(idxA(idx)), item, mMethod) = eComp) Then ' (idx > item)
  523.                 lA_2(ptr2) = idxA(idx)   ' 3
  524.                 ptr2 = ptr2 + n1
  525.             Else
  526.                 lA_1(ptr1) = idxA(idx)   ' 1
  527.                 ptr1 = ptr1 + n1
  528.             End If
  529.         Next
  530.         inter1 = ptr1: inter2 = ptr2
  531.         For idx = pvt + n1 To ubA
  532.             If (StrComp(item, sA(idxA(idx)), mMethod) = eComp) Then ' (idx < item)
  533.                 lA_1(ptr1) = idxA(idx)   ' 2
  534.                 ptr1 = ptr1 + n1
  535.             Else
  536.                 lA_2(ptr2) = idxA(idx)   ' 4
  537.                 ptr2 = ptr2 + n1
  538.             End If
  539.         Next '-Avalanche v2i ⌐Rd-
  540.         lpB = VarPtr(idxA(lbA))          ' Cache pointer to current lb
  541.         CopyMemByV lpB, lpL_1, ptr1 * n4
  542.         idxA(lbA + ptr1) = idxItem       ' 1 2 item 3 4
  543.         CopyMemByV Sum(lpB, (ptr1 + n1) * n4), lpL_2, ptr2 * n4
  544.  
  545.         If (ubA - lbA < n64) Then        ' Ignore false indicators
  546.             If (inter2 = ptr2) Then      ' Reverse indicator
  547.             ElseIf (ubA - lbA < n3) Then ' Delegate to built-in Repeater on tiny chunks
  548.                 For walk = lbA + n1 To ubA
  549.                    idxItem = idxA(walk)  ' Grab current value
  550.                    CopyMemByV lpStr, Sum(lpS, idxItem * n4), n4 ' item = sA(walk)
  551.                    find = walk
  552.                    Do While StrComp(sA(idxA(find - n1)), item, mMethod) = eComp
  553.                        find = find - n1
  554.                        If (find = lbA) Then Exit Do
  555.                    Loop '-Repeater v45i ⌐Rd-
  556.                    If (find < walk) Then    ' Move items up 1, sA(find) = item
  557.                        CopyMemByV Sum(lpI, (find + n1) * n4), Sum(lpI, find * n4), (walk - find) * n4
  558.                        idxA(find) = idxItem ' Re-assign current item index into found pos
  559.                 End If: Next
  560.                 ptr1 = n0: ptr2 = n0
  561.             End If
  562.         ElseIf (inter1 = n0) Then
  563.             If (inter2 = ptr2) Then      ' Reverse
  564.             ElseIf (ptr1 = n0) Then      ' Reverse Pretty
  565.                 If (ptr1 > inter1) And (inter1 < n50K) Then                  ' Runners dislike super large ranges
  566.                     CopyMemByV lpStr, Sum(lpS, idxA(lbA + ptr1 - n1) * n4), n4
  567.                     opt = lbA + (inter1 \ n2)
  568.                     run = lbA + inter1
  569.                     Do While run > opt                                       ' Runner do loop
  570.                         If Not (StrComp(sA(idxA(run - n1)), item, mMethod) = eComp) Then Exit Do
  571.                         run = run - n1
  572.                     Loop: cast = lbA + inter1 - run
  573.                     If cast Then
  574.                         CopyMemByV lpL_1, Sum(lpI, run * n4), cast * n4      ' Grab items that stayed below current that should also be above items that have moved down below current
  575.                         CopyMemByV Sum(lpI, run * n4), Sum(lpI, (lbA + inter1) * n4), (ptr1 - inter1) * n4 ' Move down items
  576.                         CopyMemByV Sum(lpI, (lbA + ptr1 - cast - n1) * n4), lpL_1, cast * n4 ' Re-assign items into position immediately below current item
  577.                     End If
  578.                 End If ' 1 2 1r item 4r 3 4
  579.                 If (inter2) And (ptr2 - inter2 < n50K) Then
  580.                     base = lbA + ptr1 + n1
  581.                     CopyMemByV lpStr, Sum(lpS, idxA(base) * n4), n4
  582.                     pvt = lbA + ptr1 + inter2
  583.                     opt = pvt + ((ptr2 - inter2) \ n2)
  584.                     run = pvt
  585.                     Do While run < opt                                       ' Runner do loop
  586.                         If Not (StrComp(sA(idxA(run + n1)), item, mMethod) = eComp) Then Exit Do
  587.                         run = run + n1
  588.                     Loop: cast = run - pvt
  589.                     If cast Then
  590.                         CopyMemByV lpL_1, Sum(lpI, (pvt + n1) * n4), cast * n4 ' Grab items that stayed above current that should also be below items that have moved up above current
  591.                         CopyMemByV Sum(lpI, (base + cast) * n4), Sum(lpI, base * n4), inter2 * n4 ' Move up items
  592.                         CopyMemByV Sum(lpI, base * n4), lpL_1, cast * n4     ' Re-assign items into position immediately above current item
  593.             End If: End If: End If
  594.         ElseIf (inter2 = n0) Then
  595.             If (inter1 = ptr1) Then      ' Refresh
  596.                 ' Delegate to built-in Insert/Binary hybrid on ideal data state
  597.                 walk = lbA: mezz = ubA: idx = n0                                  ' Initialize our walker variables
  598.                 opt = GetOptimalRange(ubA - lbA + n1)                             ' Get runners optimal range
  599.                 If opt > twMax Then InitializeStacks twLb, twUb, twMax, opt       ' Ensure enough stack space
  600.                 Do While walk < mezz ' ----==================================---- ' Do the twist while there's more items
  601.                     walk = walk + n1                                              ' Walk up the array and use binary search to insert each item down into the sorted lower array
  602.                     CopyMemByV lpStr, Sum(lpS, idxA(walk) * n4), n4               ' Grab current value into item
  603.                     find = walk                                                   ' Default to current position
  604.                     ceil = walk - n1                                              ' Set ceiling to current position - 1
  605.                     base = lbA                                                    ' Set base to lower bound
  606.                     Do While StrComp(sA(idxA(ceil)), item, mMethod) = eComp '  .  ' While current item must move down
  607.                         midd = (base + ceil) \ n2                                 ' Find mid point
  608.                         Do Until StrComp(sA(idxA(midd)), item, mMethod) = eComp   ' Step back up if below
  609.                             base = midd + n1                                      ' Bring up the base
  610.                             midd = (base + ceil) \ n2                             ' Find mid point
  611.                             If midd = ceil Then Exit Do                           ' If we're up to ceiling
  612.                         Loop                                                      ' Out of loop >= target pos
  613.                         find = midd                                               ' Set provisional to new ceiling
  614.                         If find = base Then Exit Do                               ' If we're down to base
  615.                         ceil = midd - n1                                          ' Bring down the ceiling
  616.                     Loop '-Twister v4i ⌐Rd-    .       . ...   .               .  ' Out of binary search loops
  617.                     If (find < walk) Then                                         ' If current item needs to move down
  618.                         CopyMemByV lpStr, Sum(lpS, idxA(find) * n4), n4
  619.                         run = walk + n1
  620.                         Do Until run > mezz Or run - walk > opt                   ' Runner do loop
  621.                             If Not (StrComp(item, sA(idxA(run)), mMethod) = eComp) Then Exit Do
  622.                             run = run + 1
  623.                         Loop: cast = (run - walk)
  624.                         CopyMemByV lpL_2, Sum(lpI, walk * n4), cast * n4          ' Grab current value(s)
  625.                         CopyMemByV Sum(lpI, (find + cast) * n4), Sum(lpI, find * n4), (walk - find) * n4 ' Move up items
  626.                         CopyMemByV Sum(lpI, find * n4), lpL_2, cast * n4          ' Re-assign current value(s) into found pos
  627.                         If cast > n1 Then
  628.                             If Not run > mezz Then
  629.                                 idx = idx + n1
  630.                                 twLb(idx) = run - n1
  631.                                 twUb(idx) = mezz
  632.                             End If
  633.                             walk = find
  634.                             mezz = find + cast - n1
  635.                     End If: End If
  636.                     If walk = mezz Then
  637.                         If idx Then
  638.                             walk = twLb(idx)
  639.                             mezz = twUb(idx)
  640.                             idx = idx - n1
  641.                 End If: End If: Loop     ' Out of walker do loop
  642.                 ' ----=================----
  643.                 ptr1 = n0: ptr2 = n0
  644.             ElseIf (ptr2 = n0) Then      ' Pretty
  645.                 If (ptr1 > inter1) And (inter1 < n50K) Then                  ' Runners dislike super large ranges
  646.                     CopyMemByV lpStr, Sum(lpS, idxA(lbA + ptr1 - n1) * n4), n4
  647.                     opt = lbA + (inter1 \ n2)
  648.                     run = lbA + inter1
  649.                     Do While run > opt                                       ' Runner do loop
  650.                         If Not (StrComp(sA(idxA(run - n1)), item, mMethod) = eComp) Then Exit Do
  651.                         run = run - n1
  652.                     Loop: cast = lbA + inter1 - run
  653.                     If cast Then
  654.                         CopyMemByV lpL_1, Sum(lpI, run * n4), cast * n4      ' Grab items that stayed below current that should also be above items that have moved down below current
  655.                         CopyMemByV Sum(lpI, run * n4), Sum(lpI, (lbA + inter1) * n4), (ptr1 - inter1) * n4 ' Move down items
  656.                         CopyMemByV Sum(lpI, (lbA + ptr1 - cast - n1) * n4), lpL_1, cast * n4 ' Re-assign items into position immediately below current item
  657.                     End If
  658.                 End If ' 1 2 1r item 4r 3 4
  659.                 If (inter2) And (ptr2 - inter2 < n50K) Then
  660.                     base = lbA + ptr1 + n1
  661.                     CopyMemByV lpStr, Sum(lpS, idxA(base) * n4), n4
  662.                     pvt = lbA + ptr1 + inter2
  663.                     opt = pvt + ((ptr2 - inter2) \ n2)
  664.                     run = pvt
  665.                     Do While run < opt                                       ' Runner do loop
  666.                         If Not (StrComp(sA(idxA(run + n1)), item, mMethod) = eComp) Then Exit Do
  667.                         run = run + n1
  668.                     Loop: cast = run - pvt
  669.                     If cast Then
  670.                         CopyMemByV lpL_1, Sum(lpI, (pvt + n1) * n4), cast * n4 ' Grab items that stayed above current that should also be below items that have moved up above current
  671.                         CopyMemByV Sum(lpI, (base + cast) * n4), Sum(lpI, base * n4), inter2 * n4 ' Move up items
  672.                         CopyMemByV Sum(lpI, base * n4), lpL_1, cast * n4     ' Re-assign items into position immediately above current item
  673.         End If: End If: End If: End If
  674.  
  675.         If (ptr1 > n1) Then
  676.             If (ptr2 > n1) Then cnt = cnt + n1: ssLb(cnt) = lbA + ptr1 + n1: ssUb(cnt) = ubA
  677.             ubA = lbA + ptr1 - n1
  678.         ElseIf (ptr2 > n1) Then
  679.             lbA = lbA + ptr1 + n1
  680.         Else
  681.             If (cnt = n0) Then Exit Do
  682.             lbA = ssLb(cnt): ubA = ssUb(cnt): cnt = cnt - n1
  683.         End If
  684.     Loop: CopyMemByR ByVal lpStr, 0&, n4 ' De-reference pointer to item variable
  685. End Sub
  686.  
  687. ' + ArrayPtr +++++++++++++++++++++++++++++++++++++++++++++++++
  688.  
  689. ' This function returns a pointer to the SAFEARRAY header of
  690. ' any Visual Basic array, including a Visual Basic string array.
  691.  
  692. ' Substitutes both ArrPtr and StrArrPtr.
  693.  
  694. ' This function will work with vb5 or vb6 without modification.
  695.  
  696. Function ArrayPtr(Arr) As Long
  697.     Dim iDataType As Integer
  698.     On Error GoTo UnInit
  699.     CopyMemByR iDataType, Arr, n2                           ' get the real VarType of the argument, this is similar to VarType(), but returns also the VT_BYREF bit
  700.     If (iDataType And vbArray) = vbArray Then               ' if a valid array was passed
  701.         CopyMemByR ArrayPtr, ByVal Sum(VarPtr(Arr), n8), n4 ' get the address of the SAFEARRAY descriptor stored in the second half of the Variant parameter that has received the array. Thanks to Francesco Balena.
  702.     End If
  703. UnInit:
  704. End Function
  705.  
  706. ' + Validate Index Array +++++++++++++++++++++++++++++++++++++
  707.  
  708. ' This will prepare the passed index array if it is not already.
  709.  
  710. ' This sub-routine determines if the index array passed is either:
  711. ' [A] uninitialized or Erased
  712. '     initialized to invalid boundaries
  713. '     initialized to valid boundaries but not prepared
  714. ' [B] initialized to extended boundaries and not fully prepared
  715. ' [C] prepared for the sort process by the For loop
  716. '     has been modified by a previous sort process
  717.  
  718. ' If the condition is determined to be [A] then it is prepared by
  719. ' executing the For loop code, if the condition is determined to
  720. ' be [B] then it is prepared only from the old ub to the new ub,
  721. ' otherwise if [C] nothing is done
  722. ' This will Threparfreptm th  idxA(l' othf the SAFEARRAY descriptor stox ar
  723.  
  724. ' This su nd n ar
  725.  
  726. ' o + ptr1 + inter2down belotrb<ku thoy headuntmsub- ElseIf (ptrDrt Do                           ' It     ' Wa    Copy         Rut Valida' Iries but not prepared
  727. ' uthis of  current     ' It     ' Wa    CopySid ao + pt         Rss of the SAFEARRAY descriptor stored in the second half of thuni(d n ar
  728. Inited in the secr                  ltor st1) * n4), lpL_1A(idx)   ' 1
  729.                 ptr1 = ptr1 + n1
  730.             End If
  731.         Next
  732.         inter1 = ptr1: inter2 = ptr2
  733.         For idx = pvt + n1 To ubA
  734.             If (StrComp(item, sA(idxA(idx)), mMethod) = eComp) Then ' (idx < item)
  735.                 lA_ sA(iUnIni        lA_ sA(iUf.         pvt)
  736.  n1
  737.             End If
  738.         Next
  739.         inter1 = ptr1: inter2 = ptr2
  740.         For idx = pvt + n1 To ubA
  741.             If (StrComp(item, sA(idxA(idx)), mMethod) = eComp) Then ' (idx < item)
  742.                 lA_ sA(iUnIni        lA_ sA(iUf.         pvt)
  743. ,f
  744.   =e   a             If (StrComp(itemP  If (StrComp(i+ n1r = -1&
  745.     Equal = 0&
  746.   mezz Or rst * n4                    C + iurrComp(i+ n1o         If (StrComp(il'rot (mbAIf hat stayt stayt stayt stayt statrComp(Re-assign items into p thun  ' IzIcl tem
  747.                     find = walk                                      izer1      lz0K) Th it trCo0         tem, sA(idxA(dariesate        m0         tem =en1) * n4), lpL_1, cast
  748. e    IfdxA(t* nameter tha(Stetee    a(dariee      CopyMemByV a      aassign items.=cast
  749. e    IfdxA(t* n r Gourrenttttt, lpems     i     g, lA_2unction
  750.  
  751. ' + (t*yt st  ptr1 = ptr1 + nEWnction
  752. ign items into p thun  ' IzIcl tem
  753.                     find = walk
  754.      lourrentttiS  pvt)
  755.  n1
  756.             End If
  757.         Next
  758.         inter1 = ptr1: inter2 = ptr2
  759.         For idx = pvt + n1 uhtk
  760.     d
  761.      xd    2) Thc++++ IzI  'myffffttw Au2ttttt,             find = walk
  762.      lourrentttiS  pvt)
  763.  n1
  764.             End If
  765.         Next
  766.         inter1 = ptr1: ia   Next
  767.  
  768. ' + (t*yt slS  pvn1) Then cnt = cnt + n17a) = eCocter2= ptr1butction
  769. ign items int2òlA_2unct. ne    CoTn itfms ipared
  770. ' b, ng
  771.     Dim walk 2= ptr1butction
  772. ign itbut + nEWnnnnnnnosign     ni        lA_ sA(inEOyMemByV lpStTiwMax, opt       '*yt t)
  773. nptr1bustayt stayt statr't     lA_ sAt)
  774. np      N>oion
  775. ign e, opt       '*y      ' Gred
  776. ' b, n aEnd tr,Ib, n aEA_ sAt)*yt slS  pvn1) TheslS y    bc(Rei   C + iu         t (StrComp(item, s'*y ' b, ng idxA(id     pu(ubompnnnnA_2unter1 = ptr1: ia   Next
  777.  
  778. ' + (t*yt slS  pvn1) Then cnt = cnt + n17a) = eCocter2= ptr1butction
  779. ign items     '*bc( Then'CpV lpl    Nn iteitem, s'*y ' b, ng idxA(id  in them, s'*y  idxm =en13 4
  780.         A_2unter1 = ptr1: ia   Next
  781.  
  782. ' + (t*yt slS  pvn1) Then cnt = cnt + n17a) = eCe
  783.      louF  bc(                       s ' 1 2 item 3 4
  784.    e,o iteitem, sn> This will p  ize    esys ip1 2 itemtrCompre,o iteitem, sn> This will p  TuIt
  785. hen  ter)         If (StrComp(item, sA(idxA(urn
  786. zhis will 
  787. '  ptr1: ia   Nexrrent itemn1) T)t p  atem, sype As ,em, ATiwMax, esys ip1tn cnt = cnt + nproces                 opt =o               r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o     F  bG slS      A_2unp
  788. '     has been modified by a previous sort process
  789.  
  790. ' If the conditDhas been t =oo      inter1 = ptr1: interMt = cnt + n17a) = eCe
  791.     vo        ' Rfo"tion
  792.  ptterMr2o      bG slStionhen   bGSB  rktdx = pvt + no + n1 T       arrl=Dteue be [ABEqual- n1Arm n16   bG slStionhen   b    uB   sMemByV lbG pt =o Rut copyMr do         opt =o               r do         opt =o               r do         opt =o                n1A    ndit, s'E slpems ule
  793. Envious s-_1, .t,eCe
  794.  : interMt gex A      r do         VD           End If
  795.         d Io VD: interMt gex A8 t gex a
  796.    x = pvt + no + n1 T       arrl=Dteue be [ABEqusG slSttdx = pvt + no + n1 T       arrl=Dteue be [ABEqual- n1Arm n16   bG slStionhen   b    uB   sMemByV lbG pt =o Rut                  Sube [E slSahen_hen ized               dinteb      r do h  dinteb      r do h  dinteb      r do h  dio be [  n1    sMax,     opt =o lt" ay if it is  r bG pt =o Rut  un -        n1A    ndit, s'E slpems ule
  797. Envious s-_1, .t,eCe
  798.  : interMt gex A      r do         VD     4RAY d     Sube [E slSahen d    r t     4RAY, n4               ' Grab current value into item
  799.                     find = walk      .       . ...   .     b paredt gexb paredt mByV l If  on
  800. j   If rrentttiS ,   s 'pie
  801.  ew. ...   .  . ... aredtd    End If)c [  n1   e bG slStl        opt =o     d If
  802.  o"l ip1 2 itemtrCompre,o iteo    btLlpemD  izer1bre1I, (pvterzer1ben    o         r do         opt =o               r do         opt =o               r do         opt =o               r do         opt =o     F       ò11 T nt + n1: ssLb(cnt) = lbAoD     opt =w  opt =o  ++ IzI  'my              find = walk      .       . ...   .   ,      lue inti  has been modisax,     o F  bG slS      A_2unp
  803. '     has been modified by a previous sort process
  804. teb  into nC     nlpemfa      ab  intdt i7     E!ameterA  a po     F       òw   otTe"Rn ' Grab currenles
  805.  Z    opt =o               r do   . erA  a (lpIey1di                          run = run + 1
  806.                         Loop: cast = (run - walk)
  807.         ;e         lj n               ' Runner do loop
  808.                       ,   eiiiiiic         bo Rutgn    btLl  sMem    Enedtd    End If)c [  n1   e bG slStl        opt =o     d If
  809.  o"l ip1 2 itemtrCompre,o iteo    btLlpemD  izeeI   d IdtbG slStllStl        opt =o     c ar'   rei  l           runAfeg          r do         opt =o               r do     VD     4RAY d     Sube [Edit, s'E slp n4),ti  htebent value into it    rdt i7Rf> tw + inter1 - r- pvt
  810.     e                             ' Defaul           v3pemfaax,     o F  bG slS    oD     opt =w  opt =o  ++    d Idd     lS  y              findeT. ...   .t abo Rut   pt =w  opt =o  ++    d IdbindeT. ..           find ==o  ++    d Idbin'lv fint =w       bG     s        e iteo     f  e suni: int-,Nexube [Ee below current
  811.                xlue into iiteoP-InAY d       f  e ++  into iRutgit, s   S    f ...A    ndit, s'E slpems ule
  812. Envious s-_1, .t,eCe
  813.  : interMt gex A      r do         VD     4RA
  814.      oooooooooooooooor"Rn ' GrabbG  b  If r dpt =o  ++   )r dpt ,    1    opt =o               r do         opt =o               r  xlue into iiteo find = walk
  815. l bG     sTi d Idtgn      n )r    tLl     rdt iinter++    d IdbindeT. ..   bNexu  bs    m0         tem =en1) * n4), lpL_1, c   *yt o Bal             midd = (base + ceil) \ n2                             '  iiteo find = walk
  816. l em =en1intoe   f  e ++   iiteo eile, s'*y21 slS      I'CS' Defaul++ d Idbind = pvt + 
  817.  
  818. ' + Arraexu  bunA,1, c  unne e     ' Set base t'*y21     CopyMemByVUUUUUUUfet base t'*y21  a(ito iiteomiddeT.nlpI,'      f  tLlUp    t      eT. ..           find ==o it bat =o        1eComp) Then ' (idx < iteen c  b  If rerMr2oA8 t t      If rerMriDatMemByV a t r- pvt
  819.     e e=en1intoe   f  et
  820.     e e=en1intoe   f  et
  821.     e e=en               ' Defaul     rrlscint'   reo   =en1intoe  c  b 'ae e=en  d)unner     Ifen1iu n1  lm =en1int ind = walk
  822. l (CS' Defauntoe  se t'*y