home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 5_2007-2008.ISO / data / Zips / Proper_Lis2102272182008.psc / PrettySort.bas < prev   
BASIC Source File  |  2008-02-16  |  57KB  |  1,275 lines

  1. Attribute VB_Name = "mPrettySort"
  2. Option Explicit                       ' -⌐Rd 04/08-
  3.  
  4. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  5. '
  6. ' Pretty Sort string array sorting algorithms.
  7. '
  8. ' Intuitive/natural sorting and comparison functions.
  9. '
  10. ' The following are intended for sorting filenames that
  11. ' may contain numbers in a more intuitive order:
  12. '
  13. '  strPrettyFileNames
  14. '  strPrettyFileNamesIndexed
  15. '  StrCompFileNames
  16. '
  17. ' The following are intended for sorting strings that
  18. ' may contain numbers in a more intuitive order:
  19. '
  20. '  strPrettyNumSort
  21. '  strPrettyNumSortIndexed
  22. '  StrCompNumbers
  23. '
  24. ' The following are intended for sorting strings that
  25. ' do not contain numbers in a more intuitive order:
  26. '
  27. '  strPrettySort
  28. '  strPrettySortIndexed
  29. '
  30. ' You are free to use any part or all of this code even for
  31. ' commercial purposes in any way you wish under the one condition
  32. ' that no copyright notice is moved or removed from where it is.
  33. '
  34. ' For comments, suggestions or bug reports you can contact me at:
  35. ' rdòedwardsòbigpondòcom.
  36. '
  37. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  38.  
  39. ' This constant defines the maximum allowed occurence of numeric character
  40. ' groups (1 or more) within any string item being passed to these routines:
  41. Private Const MAX_DISCRETE_OCCUR_NUMS As Long = 256&
  42.  
  43. ' For example, this string has 4 occurences: a6string 07with 89 four3occurs
  44.  
  45. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  46.  
  47. ' Declare some CopyMemory Alias's (thanks Bruce :)
  48. Private Declare Sub CopyMemByV Lib "kernel32" Alias "RtlMoveMemory" (ByVal lpDest As Long, ByVal lpSrc As Long, ByVal lLenB As Long)
  49. Private Declare Sub CopyMemByR Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal lLenB As Long)
  50. Private Declare Function AllocStrB Lib "oleaut32" Alias "SysAllocStringByteLen" (ByVal lpszStr As Long, ByVal lLenB As Long) As Long
  51.  
  52. ' More efficient repeated use of numeric literals
  53. Private Const n0 = 0&, n1 = 1&, n2 = 2&, n3 = 3&, n4 = 4&, n5 = 5&, n6 = 6&
  54. Private Const n7 = 7&, n8 = 8&, n12 = 12&, n16 = 16&, n32 = 32&, n64 = 64&
  55. Private Const nKB As Long = 1024&
  56.  
  57. Private Enum SAFEATURES
  58.     FADF_AUTO = &H1              ' Array is allocated on the stack
  59.     FADF_STATIC = &H2            ' Array is statically allocated
  60.     FADF_EMBEDDED = &H4          ' Array is embedded in a structure
  61.     FADF_FIXEDSIZE = &H10        ' Array may not be resized or reallocated
  62.     FADF_BSTR = &H100            ' An array of BSTRs
  63.     FADF_UNKNOWN = &H200         ' An array of IUnknown*
  64.     FADF_DISPATCH = &H400        ' An array of IDispatch*
  65.     FADF_VARIANT = &H800         ' An array of VARIANTs
  66.     FADF_RESERVED = &HFFFFF0E8   ' Bits reserved for future use
  67.     #If False Then
  68.         Dim FADF_AUTO, FADF_STATIC, FADF_EMBEDDED, FADF_FIXEDSIZE, FADF_BSTR, FADF_UNKNOWN, FADF_DISPATCH, FADF_VARIANT, FADF_RESERVED
  69.     #End If
  70. End Enum
  71. 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
  72.  
  73. Private Type SAFEARRAY
  74.     cDims       As Integer       ' Count of dimensions in this array (only 1 supported)
  75.     fFeatures   As Integer       ' Bitfield flags indicating attributes of a particular array
  76.     cbElements  As Long          ' Byte size of each element of the array
  77.     cLocks      As Long          ' Number of times the array has been locked without corresponding unlock
  78.     pvData      As Long          ' Pointer to the start of the array data (use only if cLocks > 0)
  79. End Type
  80. Private Type SABOUNDS            ' This module supports single dimension arrays only
  81.     cElements As Long            ' Count of elements in this dimension
  82.     lLBound   As Long            ' The lower-bounding index of this dimension
  83.     lUBound   As Long            ' The upper-bounding index of this dimension
  84. End Type
  85.  
  86. Private qs4Lb() As Long, qs4Ub() As Long ' Non-stable non-recursive quicksort stacks
  87. Private ss2Lb() As Long, ss2Ub() As Long ' Stable non-recursive quicksort stacks
  88. Private tw4Lb() As Long, tw4Ub() As Long ' Stable insert/binary runner stacks
  89. Private lA_1() As Long, lA_2() As Long   ' Stable quicksort working buffers
  90. Private qs4Max As Long, ss2Max As Long
  91. Private tw4Max As Long, bufMax As Long
  92.  
  93. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  94.  
  95. Private Type tPrettySort
  96.     occurs As Long
  97.     idxn(1 To MAX_DISCRETE_OCCUR_NUMS) As Long
  98.     cnums(1 To MAX_DISCRETE_OCCUR_NUMS) As Long
  99.     cpads(1 To MAX_DISCRETE_OCCUR_NUMS) As Long
  100. End Type
  101.  
  102. Public Enum ePrettyFiles
  103.     GroupByExtension = 0&
  104.     GroupByFolder = 1&
  105.     #If False Then
  106.         Dim GroupByExtension, GroupByFolder
  107.     #End If
  108. End Enum
  109.  
  110. Public Enum eCompare
  111.     Lesser = -1&
  112.     Equal = 0&
  113.     Greater = 1&
  114.     #If False Then
  115.         Dim Lesser, Equal, Greater
  116.     #End If
  117. End Enum
  118.  
  119. Public Enum eSortOrder
  120.     Descending = -1&
  121.     Default = 0&
  122.     Ascending = 1&
  123.     #If False Then
  124.         Dim Descending, Default, Ascending
  125.     #End If
  126. End Enum
  127.  
  128. ' +++++++++++++++++++++++++++++++++++++++++++++++++++++++
  129.  
  130. Private Const Default_Direction As Long = Ascending
  131.  
  132. Private mComp As eCompare
  133. Private mCriteria As VbCompareMethod
  134. Private mSortOrder As eSortOrder
  135.  
  136. Private padZs As String
  137.  
  138. ' +++++++++++++++++++++++++++++++++++++++++++++++++++++++
  139.  
  140. ' The following properties should be set before sorting.
  141.  
  142. Property Get SortMethod() As VbCompareMethod
  143.     SortMethod = mCriteria
  144. End Property
  145.  
  146. Property Let SortMethod(ByVal NewMethod As VbCompareMethod)
  147.     mCriteria = NewMethod
  148. End Property
  149.  
  150. Property Get SortOrder() As eSortOrder
  151.     If mSortOrder = Default Then mSortOrder = Default_Direction
  152.     SortOrder = mSortOrder
  153. End Property
  154.  
  155. Property Let SortOrder(ByVal NewDirection As eSortOrder)
  156.     If NewDirection = Default Then
  157.         If mSortOrder = Default Then mSortOrder = Default_Direction
  158.     Else
  159.         mSortOrder = NewDirection
  160.     End If
  161. End Property
  162.  
  163. ' + Pretty File Names Sorter +++++++++++++++++++++++++++
  164.  
  165. ' This sub will sort string array items containing numeric
  166. ' characters in a more intuitive order. It will take into
  167. ' account all occurences of numbers in the string item.
  168.  
  169. ' It is intended for sorting filenames, and will apply the
  170. ' same intuitive order for folders in the file path if they
  171. ' contain numeric characters.
  172.  
  173. ' Specifying a path is not required, neither do they need to
  174. ' have extensions, in fact, they do not need to be filenames;
  175. ' just strings that may contain numbers grouped together
  176. ' within the string text.
  177.  
  178. ' In other words, it can be used for normal pretty sorting
  179. ' operations. But please note, this is intended for sorting
  180. ' filenames and is a little slower than the strPrettyNumSort
  181. ' sub below because of extra code to handle the extensions.
  182.  
  183. ' If GroupByExtension is specified it will order the items by
  184. ' extension, whilst still producing an intuitive order of the
  185. ' file path and names that may have numbers within them and
  186. ' that have the same extension.
  187.  
  188. ' Note: this will also sort pure numbers, which will group
  189. ' positive and negative numbers together from small values
  190. ' to large values (or reversed) irrespective of their sign.
  191.  
  192. ' This is version two of the Pretty File Names routine which
  193. ' also handles numbers in the extension.
  194.  
  195. Sub strPrettyFileNames(sA() As String, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True, Optional ByVal Group As ePrettyFiles = GroupByExtension) '-⌐Rd-
  196.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  197.     Dim aIdx() As Long, lAbuf() As Long
  198.     Dim lpS As Long, lpL As Long
  199.     Dim walk As Long, cnt As Long
  200.  
  201.     cnt = ubA - lbA + n1            ' Grab array item count
  202.     If (cnt < n1) Then Exit Sub     ' If nothing to do then exit
  203.  
  204.     strPrettyFileNamesIndexed sA, aIdx, lbA, ubA, CapsFirst, Group
  205.  
  206.     ' Now use copymemory to copy the string pointers to a long array buffer for later
  207.     ' reference to the original strings. We need this to re-order the strings in the
  208.     ' last step that would over-write needed items if the buffer was not used.
  209.  
  210.     ReDim lAbuf(lbA To ubA) As Long
  211.     lpS = VarPtr(sA(lbA))
  212.     lpL = VarPtr(lAbuf(lbA))
  213.     CopyMemByV lpL, lpS, cnt * n4
  214.  
  215.     ' Next we do the actual re-ordering of the array items by referencing the string
  216.     ' pointers with the index array, and assigning back into the index array ready to
  217.     ' be copied across to the string array in one copy process.
  218.  
  219.     For walk = lbA To ubA
  220.         aIdx(walk) = lAbuf(aIdx(walk))
  221.     Next
  222.  
  223.     ' The last step assigns the string pointers back into the original array
  224.     ' from the pointer array buffer.
  225.  
  226.     lpL = VarPtr(aIdx(lbA))
  227.     CopyMemByV lpS, lpL, cnt * n4
  228. End Sub
  229.  
  230. ' + Indexed Pretty File Names Sorter ++++++++++++++++++++++
  231.  
  232. Sub strPrettyFileNamesIndexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True, Optional ByVal Group As ePrettyFiles = GroupByExtension) '-⌐Rd-
  233.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  234.     Dim numsN() As Long, numsE() As Long
  235.     Dim sAtemp() As String, lpS As Long
  236.     Dim atFN() As tPrettySort
  237.     Dim atEXT() As tPrettySort
  238.     Dim period() As Long, extens() As Long
  239.     Dim filenmP() As Long, extensP() As Long
  240.     Dim prevMethod As eCompare
  241.     Dim prevOrder As eSortOrder
  242.     Dim lenP() As Long, lpads As Long
  243.     Dim walk As Long, lpad As Long
  244.     Dim lPos As Long, clen As Long
  245.     Dim lenFN As Long, lenExt As Long
  246.     Dim item As String, lpStr As Long
  247.  
  248.     If (ubA - lbA < n1) Then Exit Sub           ' If nothing to do then exit
  249.  
  250.     lpStr = VarPtr(item)                        ' Cache pointer to the string variable
  251.     lpS = VarPtr(sA(lbA)) - (lbA * n4)          ' Cache pointer to the string array
  252.  
  253.     ReDim sAtemp(lbA To ubA) As String          ' ReDim buffers
  254.     ReDim atFN(lbA To ubA) As tPrettySort
  255.     ReDim atEXT(lbA To ubA) As tPrettySort
  256.     ReDim period(lbA To ubA) As Long
  257.     ReDim extens(lbA To ubA) As Long
  258.     ReDim lenP(lbA To ubA) As Long
  259.     ReDim filenmP(lbA To ubA) As Long
  260.     ReDim extensP(lbA To ubA) As Long
  261.  
  262.     ' First build an array of data about the filenames to use for the padding process.
  263.  
  264.     ' Because filename and extension are distinctive entities in the comparison process
  265.     ' it is neccessary to do a good deal of preperation to produce the resulting padded
  266.     ' items for comparison. Pre-calculating the padding data is relatively fast compared
  267.     ' to string manipulation.
  268.  
  269.     For walk = lbA To ubA    ' Loop thru the array items one by one
  270.  
  271.         CopyMemByV lpStr, lpS + (walk * n4), n4   ' Cache current item
  272.         clen = Len(item)                          ' Cache the items length
  273.  
  274.         lPos = InStrRev(item, "\")                    ' Determine position of the last backslash instance
  275.         If lPos = n0 Then lPos = InStrRev(item, "/")  ' If no backslash then maybe it's a forward slash?
  276.         period(walk) = InStrRev(item, ".")            ' Determine position of the last period character
  277.  
  278.         If period(walk) = n0 Or period(walk) < lPos Then   ' If no period or it's before the last slash
  279.             period(walk) = clen + n1                       ' Set to item length instead, + phantom period pos
  280.             extens(walk) = n0
  281.         Else                                          ' Record length of this items extension
  282.             extens(walk) = clen - period(walk)
  283.             If extens(walk) > lenExt Then lenExt = extens(walk)
  284.         End If
  285.  
  286.         If period(walk) - n1 > lenFN Then lenFN = period(walk) - n1
  287.     Next
  288.     CopyMemByR ByVal lpStr, 0&, n4   ' De-reference pointer to item variable
  289.  
  290.     ' Set nums to the maximum position that numbers can occur in the strings.
  291.  
  292.     ReDim numsN(n0 To lenFN) As Long
  293.     ReDim numsE(n0 To lenExt) As Long
  294.  
  295.     ' Find all occurences of numeric chars in the filename portion of the items.
  296.  
  297.     For walk = lbA To ubA
  298.         GoNumLoop atFN(walk), sA(walk), numsN, period(walk)
  299.     Next
  300.  
  301.     ' Next calculate the padding length for all num inst's in each filename, and
  302.     ' add them together to determine the total padding needed for each filename.
  303.  
  304.     ' The total lengths are compared to identify the longest length that will
  305.     ' be used to pre-allocate the string lengths for faster string operations.
  306.  
  307.     For walk = lbA To ubA
  308.         lpad = GoPadLoop(atFN(walk), numsN)   ' Calc the padding length for this filename
  309.         If lpad > lpads Then lpads = lpad     ' Set lpads to longest padding length
  310.         clen = period(walk) - n1 + lpad       ' Calc the length of this filename when padded
  311.         If clen > lenFN Then lenFN = clen     ' Set lenFN to longest filename length
  312.  
  313.         filenmP(walk) = clen    ' Record the new length of this filename when padded
  314.     Next
  315.  
  316.     ' Find all occurences of numeric chars in the extension portion of the items.
  317.  
  318.     For walk = lbA To ubA
  319.         GoNumLoop atEXT(walk), Mid$(sA(walk), period(walk) + n1), numsE, extens(walk) + n1
  320.     Next
  321.  
  322.     ' Next calculate the padding length for all num inst's in each extension, and
  323.     ' add them together to determine the total padding needed for each extension.
  324.  
  325.     ' The total lengths are compared to identify the longest length that will
  326.     ' be used to pre-allocate the string lengths for faster string operations.
  327.  
  328.     For walk = lbA To ubA
  329.         lpad = GoPadLoop(atEXT(walk), numsE)  ' Calc the padding length for this extension
  330.         If lpad > lpads Then lpads = lpad     ' Set lpads to longest padding length
  331.         clen = extens(walk) + lpad            ' Calc the length of this extension when padded
  332.         If clen > lenExt Then lenExt = clen   ' Set lenExt to longest extension length
  333.  
  334.         extensP(walk) = clen    ' Record the new length of this extension when padded
  335.     Next
  336.  
  337.     ' Pre-allocate the buffer array strings without assigning any data into them.
  338.  
  339.     If Group = GroupByExtension Then
  340.         For walk = lbA To ubA
  341.             clen = lenExt + filenmP(walk)
  342.             CopyMemByV VarPtr(sAtemp(walk)), VarPtr(AllocStrB(n0, clen + clen)), n4
  343.             lenP(walk) = clen   ' Record total length of this item
  344.         Next
  345.     Else ' Group = GroupByFolder
  346.         For walk = lbA To ubA
  347.             clen = lenFN + extensP(walk)
  348.             CopyMemByV VarPtr(sAtemp(walk)), VarPtr(AllocStrB(n0, clen + clen)), n4
  349.             lenP(walk) = clen   ' Record total length of this item
  350.         Next
  351.     End If
  352.  
  353.     padZs = String$(lpads, "0")  ' Create pad to longest padding length
  354.  
  355.     ' Next, pad all filenames containing numeric characters, based on the
  356.     ' longest number for that position, into the temp string array.
  357.  
  358.     ' Step through each item building the temp string with padded numeric
  359.     ' chars using recorded info in atFN and atEXT.
  360.  
  361.     ' If GroupByExtension is specified then pad the extensions and prefix
  362.     ' them to the temp array filenames, else pad the filenames and append
  363.     ' the extensions.
  364.  
  365.     If Group = GroupByExtension Then
  366.         For walk = lbA To ubA     ' Loop thru the array items one by one
  367.             GoBufLoop atEXT(walk), Mid$(sA(walk), period(walk) + n1), sAtemp(walk), n1, lenP(walk)
  368.             clen = extensP(walk)
  369.             lpad = lenExt - clen  ' Write padding before filename
  370.             If lpad > n0 Then Mid$(sAtemp(walk), clen + n1) = Space$(lpad)
  371.             If period(walk) > n1 Then
  372.                 GoBufLoop atFN(walk), Left$(sA(walk), period(walk) - n1), sAtemp(walk), lenExt + n1, lenP(walk)
  373.             End If
  374.         Next
  375.     Else ' Group = GroupByFolder
  376.         For walk = lbA To ubA     ' Loop thru the array items one by one
  377.             GoBufLoop atFN(walk), Left$(sA(walk), period(walk) - n1), sAtemp(walk), n1, lenP(walk)
  378.             clen = filenmP(walk)
  379.             lpad = lenFN - clen   ' Write padding before extension
  380.             If lpad > n0 Then Mid$(sAtemp(walk), clen + n1) = Space$(lpad)
  381.             If extens(walk) > n0 Then
  382.                 GoBufLoop atEXT(walk), Mid$(sA(walk), period(walk) + n1), sAtemp(walk), lenFN + n1, lenP(walk)
  383.             End If
  384.         Next
  385.     End If
  386.  
  387.     ' To sort numeric values in a more intuitive order we sort with an indexing
  388.     ' sorter to index the string array which will provide sorted indicies into all
  389.     ' the working arrays.
  390.  
  391.     prevOrder = SortOrder        ' Cache SortOrder property
  392.     prevMethod = SortMethod      ' Cache SortMethod property
  393.  
  394.     ' First sort with binary comparison to seperate upper and lower case letters
  395.     ' in the order specified by CapsFirst.
  396.  
  397.     'CapsFirst: False(0) >> Descending(-1) : True(-1) >> Ascending(1)
  398.     SortOrder = (CapsFirst * -2&) - n1
  399.     SortMethod = vbBinaryCompare
  400.     strSwapSort4Indexed sAtemp, idxA, lbA, ubA
  401.  
  402.     ' Next sort in the desired direction with case-insensitive comparison to group
  403.     ' upper and lower case letters together, but with a stable sorter to preserve the
  404.     ' requested caps-first or lower-first order.
  405.     
  406.     ' Notice we pass on the indexed array in its pre-sorted state to be further modified.
  407.     
  408.     ' Notice also we are comparing the padded items in the temp string array whose items
  409.     ' are still in their original positions, which of course corresponds to the indices
  410.     ' of the source string array.
  411.  
  412.     SortOrder = prevOrder        ' Reset SortOrder property
  413.     SortMethod = vbTextCompare
  414.     strStableSort2Indexed sAtemp, idxA, lbA, ubA
  415.  
  416.     SortMethod = prevMethod      ' Reset SortMethod property
  417. End Sub
  418.  
  419. ' + Pretty File Names Compare Function ++++++++++++++++++++++++++++++
  420.  
  421. ' This function will compare two string items containing numeric characters in
  422. ' a more intuitive order. It will take into account all occurences of numbers in
  423. ' the string items including in their extensions.
  424.  
  425. ' It is intended for comparing filenames, and will apply the same intuitive order
  426. ' for folders in the file path if they contain numeric characters.
  427.  
  428. ' Specifying a path is not required, neither do they need to have extensions, in
  429. ' fact, they do not need to be filenames; just strings that may contain numbers
  430. ' grouped together within the string text.
  431.  
  432. ' In other words, it can be used for normal pretty sorting comparisons.
  433. ' But please note, this is intended for comparing filenames and is a little
  434. ' slower than the StrCompNumbers compare function below because of extra code
  435. ' to handle the extensions.
  436.  
  437. ' If SortByExtension is specified it will compare the items by extension, whilst
  438. ' still producing an intuitive order of the file path and names that may have
  439. ' numbers within them and that have the same extension.
  440.  
  441. ' Note: this will also sort pure numbers, which will group positive and negative
  442. ' numbers together from small values to large values (or reversed) irrespective
  443. ' of their sign.
  444.  
  445. ' This is version two of this function which also handles numbers in the extension.
  446.  
  447. Public Function StrCompFileNames(sThis As String, sThan As String, Optional ByVal CapsFirst As Boolean, Optional ByVal Group As ePrettyFiles = GroupByFolder) As eCompare '-⌐Rd-
  448.     Dim periodThis As Long, extensThis As Long
  449.     Dim periodThan As Long, extensThan As Long
  450.     Dim tFNthis As tPrettySort, tEXTthis As tPrettySort
  451.     Dim tFNthan As tPrettySort, tEXTthan As tPrettySort
  452.     Dim numsN() As Long, numsE() As Long
  453.     Dim lpad As Long, lpads As Long, clen As Long
  454.     Dim filenmPthis As Long, extensPthis As Long
  455.     Dim filenmPthan As Long, extensPthan As Long
  456.     Dim lenPthis As Long, lenPthan As Long
  457.     Dim sTempThis As String, sTempThan As String
  458.     Dim eComp As eCompare, lPos As Long
  459.     Dim lenFN As Long, lenExt As Long
  460.  
  461.     ' First, we gather information about our filenames to use for the
  462.     ' padding process and to access the extensions as needed later.
  463.  
  464.     clen = Len(sThis)                              ' Cache the items length
  465.     lPos = InStrRev(sThis, "\")                    ' Determine position of the last backslash instance
  466.     If lPos = n0 Then lPos = InStrRev(sThis, "/")  ' If no backslash then maybe it's a forward slash?
  467.     periodThis = InStrRev(sThis, ".")              ' Determine position of the last period character
  468.  
  469.     If periodThis = n0 Or periodThis < lPos Then   ' If no period or it's before the last slash
  470.         periodThis = clen + n1                     ' Set to item length instead, + phantom period pos
  471.         extensThis = n0
  472.     Else                                           ' Record length of this items extension
  473.         extensThis = clen - periodThis
  474.         lenExt = extensThis
  475.     End If
  476.     lenFN = periodThis - n1
  477.  
  478.     clen = Len(sThan)                              ' Cache the items length
  479.     lPos = InStrRev(sThan, "\")                    ' Determine position of the last backslash instance
  480.     If lPos = n0 Then lPos = InStrRev(sThan, "/")  ' If no backslash then maybe it's a forward slash?
  481.     periodThan = InStrRev(sThan, ".")              ' Determine position of the last period character
  482.  
  483.     If periodThan = n0 Or periodThan < lPos Then   ' If no period or it's before the last slash
  484.         periodThan = clen + n1                     ' Set to item length instead, + phantom period pos
  485.         extensThan = n0
  486.     Else                                           ' Record length of than items extension
  487.         extensThan = clen - periodThan
  488.         If extensThan > lenExt Then lenExt = extensThan
  489.     End If
  490.     If periodThan - n1 > lenFN Then lenFN = periodThan - n1
  491.  
  492.     ' Set nums to the maximum position that numbers can occur in the strings.
  493.  
  494.     ReDim numsN(n0 To lenFN) As Long
  495.     ReDim numsE(n0 To lenExt) As Long
  496.  
  497.     ' Find all occurences of numeric chars in the filename portion of the items.
  498.  
  499.     GoNumLoop tFNthis, sThis, numsN, periodThis
  500.     GoNumLoop tFNthan, sThan, numsN, periodThan
  501.  
  502.     ' Next calculate the padding length for all num inst's in each filename, and
  503.     ' add them together to determine the total padding needed for each filename.
  504.  
  505.     ' The total lengths are compared to identify which is longest, and that will
  506.     ' be used to pre-allocate the string lengths for faster string operations.
  507.  
  508.     lpad = GoPadLoop(tFNthis, numsN)    ' Calc the padding length for this filename
  509.     lpads = lpad                        ' Set lpads to this padding length
  510.     clen = periodThis - n1 + lpad       ' Calc the length of this filename when padded
  511.     lenFN = clen                        ' Set lenFN to this filename length
  512.  
  513.     filenmPthis = clen    ' Record the new length of this filename when padded
  514.  
  515.     lpad = GoPadLoop(tFNthan, numsN)    ' Calc the padding length for than filename
  516.     If lpad > lpads Then lpads = lpad   ' Set lpads to longest padding length
  517.     clen = periodThan - n1 + lpad       ' Calc the length of than filename when padded
  518.     If clen > lenFN Then lenFN = clen   ' Set lenFN to longest filename length
  519.  
  520.     filenmPthan = clen    ' Record the new length of than filename when padded
  521.  
  522.     ' Find all occurences of numeric chars in the extension portion of the items.
  523.  
  524.     GoNumLoop tEXTthis, Mid$(sThis, periodThis + n1), numsE, extensThis + n1
  525.     GoNumLoop tEXTthan, Mid$(sThan, periodThan + n1), numsE, extensThan + n1
  526.  
  527.     ' Next calculate the padding length for all num inst's in each extension, and
  528.     ' add them together to determine the total padding needed for each extension.
  529.  
  530.     ' The total lengths are compared to identify the longest length that will
  531.     ' be used to pre-allocate the string lengths for faster string operations.
  532.  
  533.     lpad = GoPadLoop(tEXTthis, numsE)   ' Calc the padding length for this extension
  534.     If lpad > lpads Then lpads = lpad   ' Set lpads to longest padding length
  535.     clen = extensThis + lpad            ' Calc the length of this extension when padded
  536.     lenExt = clen                       ' Set lenExt to this extension length
  537.  
  538.     extensPthis = clen    ' Record the new length of this extension when padded
  539.  
  540.     lpad = GoPadLoop(tEXTthan, numsE)   ' Calc the padding length for than extension
  541.     If lpad > lpads Then lpads = lpad   ' Set lpads to longest padding length
  542.     clen = extensThan + lpad            ' Calc the length of than extension when padded
  543.     If clen > lenExt Then lenExt = clen ' Set lenExt to longest extension length
  544.  
  545.     extensPthan = clen    ' Record the new length of than extension when padded
  546.  
  547.     ' Pre-allocate the buffer strings for faster string building operations.
  548.  
  549.     If Group = GroupByExtension Then
  550.         clen = lenExt + filenmPthis
  551.     Else 'Group = GroupByFolder
  552.         clen = lenFN + extensPthis
  553.     End If
  554.  
  555.     sTempThis = Space$(clen)
  556.     lenPthis = clen   ' Record total length of this item
  557.  
  558.     If Group = GroupByExtension Then
  559.         clen = lenExt + filenmPthan
  560.     Else 'Group = GroupByFolder
  561.         clen = lenFN + extensPthan
  562.     End If
  563.  
  564.     sTempThan = Space$(clen)
  565.     lenPthan = clen   ' Record total length of than item
  566.  
  567.     padZs = String$(lpads, "0")  ' Create pad to longest padding length
  568.  
  569.     ' Next, pad the filenames containing numeric characters, based on the
  570.     ' longest number for that position, into the temp string variables.
  571.  
  572.     ' Step through both items building the temp string with padded numeric
  573.     ' chars using recorded info in tFN and tEXT.
  574.  
  575.     ' If GroupByExtension is specified then pad the extensions and prefix them
  576.     ' to the temp filenames, else pad the filenames and append the extensions.
  577.  
  578.     If Group = GroupByExtension Then
  579.  
  580.         GoBufLoop tEXTthis, Mid$(sThis, periodThis + n1), sTempThis, n1, lenPthis
  581.         lpad = lenExt - extensPthis  ' Write padding before filename
  582.         If lpad > n0 Then Mid$(sTempThis, extensPthis + n1) = Space$(lpad)
  583.         If periodThis > n1 Then
  584.             GoBufLoop tFNthis, Left$(sThis, periodThis - n1), sTempThis, lenExt + n1, lenPthis
  585.         End If
  586.  
  587.         GoBufLoop tEXTthan, Mid$(sThan, periodThan + n1), sTempThan, n1, lenPthan
  588.         lpad = lenExt - extensPthan  ' Write padding before filename
  589.         If lpad > n0 Then Mid$(sTempThan, extensPthan + n1) = Space$(lpad)
  590.         If periodThan > n1 Then
  591.             GoBufLoop tFNthan, Left$(sThan, periodThan - n1), sTempThan, lenExt + n1, lenPthan
  592.         End If
  593.  
  594.     Else 'Group = GroupByFolder
  595.  
  596.         GoBufLoop tFNthis, Left$(sThis, periodThis - n1), sTempThis, n1, lenPthis
  597.         lpad = lenFN - filenmPthis   ' Write padding before extension
  598.         If lpad > n0 Then Mid$(sTempThis, filenmPthis + n1) = Space$(lpad)
  599.         If extensThis > n0 Then
  600.             GoBufLoop tEXTthis, Mid$(sThis, periodThis + n1), sTempThis, lenFN + n1, lenPthis
  601.         End If
  602.  
  603.         GoBufLoop tFNthan, Left$(sThan, periodThan - n1), sTempThan, n1, lenPthan
  604.         lpad = lenFN - filenmPthan   ' Write padding before extension
  605.         If lpad > n0 Then Mid$(sTempThan, filenmPthan + n1) = Space$(lpad)
  606.         If extensThan > n0 Then
  607.             GoBufLoop tEXTthan, Mid$(sThan, periodThan + n1), sTempThan, lenFN + n1, lenPthan
  608.         End If
  609.  
  610.     End If
  611.  
  612.     ' Next, we compare the padded items with case-insensitive comparison.
  613.  
  614.     eComp = StrComp(sTempThis, sTempThan, vbTextCompare)
  615.  
  616.     ' If the items are equal with case-insensitive comparison we return the
  617.     ' order specified by CapsFirst, else we return the case-insensitive result.
  618.  
  619.     If eComp = Equal Then
  620.  
  621.         ' To order items that are spelled the same in a more consistent order we
  622.         ' compare with binary comparison to seperate upper and lower case letters
  623.         ' in the order specified by CapsFirst.
  624.  
  625.         'CapsFirst: False(0) >> Descending(-1) : True(-1) >> Ascending(1)
  626.         lPos = (CapsFirst * -2&) - n1
  627.         StrCompFileNames = StrComp(sTempThis, sTempThan, vbBinaryCompare) * lPos
  628.  
  629.     Else
  630.         StrCompFileNames = eComp
  631.     End If
  632.  
  633. End Function
  634.  
  635. ' + Pretty Number Sorter ++++++++++++++++++++++++++++++++++++++++++++
  636.  
  637. ' This routine will sort string array items containing numeric characters
  638. ' in a more intuitive order. It will take into account all occurences of
  639. ' numbers in string items of any length.
  640.  
  641. ' It first sorts with binary comparison to seperate upper and lower case
  642. ' letters in the order specified by CapsFirst.
  643.  
  644. ' It then sorts in the desired direction with case-insensitive comparison
  645. ' to group upper and lower case letters together, but with a stable sorter
  646. ' to preserve the requested caps-first or lower-first order.
  647.  
  648. Sub strPrettyNumSort(sA() As String, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True) '-⌐Rd-
  649.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  650.     Dim aIdx() As Long, lAbuf() As Long
  651.     Dim lpS As Long, lpL As Long
  652.     Dim walk As Long, cnt As Long
  653.  
  654.     cnt = ubA - lbA + n1            ' Grab array item count
  655.     If (cnt < n1) Then Exit Sub     ' If nothing to do then exit
  656.  
  657.     strPrettyNumSortIndexed sA, aIdx, lbA, ubA, CapsFirst
  658.  
  659.     ' Now use copymemory to copy the string pointers to a long array buffer for later
  660.     ' reference to the original strings. We need this to re-order the strings in the
  661.     ' last step that would over-write needed items if the buffer was not used.
  662.  
  663.     ReDim lAbuf(lbA To ubA) As Long
  664.     lpS = VarPtr(sA(lbA))
  665.     lpL = VarPtr(lAbuf(lbA))
  666.     CopyMemByV lpL, lpS, cnt * n4
  667.  
  668.     ' Next we do the actual re-ordering of the array items by referencing the string
  669.     ' pointers with the index array, and assigning back into the index array ready to
  670.     ' be copied across to the string array in one copy process.
  671.  
  672.     For walk = lbA To ubA
  673.         aIdx(walk) = lAbuf(aIdx(walk))
  674.     Next
  675.  
  676.     ' The last step assigns the string pointers back into the original array
  677.     ' from the pointer array buffer.
  678.  
  679.     lpL = VarPtr(aIdx(lbA))
  680.     CopyMemByV lpS, lpL, cnt * n4
  681. End Sub
  682.  
  683. ' + Indexed Pretty Number Sorter ++++++++++++++++++++++++++++++++++
  684.  
  685. Sub strPrettyNumSortIndexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True) '-⌐Rd-
  686.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  687.     Dim sAtemp() As String, lpS As Long
  688.     Dim nums() As Long, citems() As Long
  689.     Dim atPS() As tPrettySort
  690.     Dim prevMethod As eCompare
  691.     Dim prevOrder As eSortOrder
  692.     Dim walk As Long, clen As Long
  693.     Dim lpad As Long, lpads As Long
  694.  
  695.     If (ubA - lbA < n1) Then Exit Sub           ' If nothing to do then exit
  696.  
  697.     ReDim sAtemp(lbA To ubA) As String          ' ReDim buffers
  698.     ReDim atPS(lbA To ubA) As tPrettySort
  699.     ReDim citems(lbA To ubA) As Long
  700.  
  701.     lpS = VarPtr(sAtemp(lbA)) - (lbA * n4)      ' Cache pointer to the temp array
  702.  
  703.     ' First build an array of data about the items to use for the padding process. The
  704.     ' resulting padded items will be used in the sorting process in place of the src array.
  705.     ' Pre-calculating the padding data is relatively fast compared to string manipulation.
  706.  
  707.     For walk = lbA To ubA
  708.         clen = Len(sA(walk))
  709.         If clen > lpad Then lpad = clen     ' Set lmax to longest item length
  710.         citems(walk) = clen                 ' Cache items length
  711.     Next
  712.  
  713.     ' Set nums to the maximum position that numbers can occur in the strings.
  714.  
  715.     ReDim nums(n0 To lpad) As Long
  716.  
  717.     ' Find all occurences of numeric chars in the string array items.
  718.  
  719.     For walk = lbA To ubA
  720.         GoNumLoop atPS(walk), sA(walk), nums, citems(walk) + n1
  721.     Next
  722.  
  723.     ' Next calculate the padding length for all num inst's in each item, and
  724.     ' add them together to determine the total padding needed for each item.
  725.  
  726.     ' The total lengths are compared to identify the longest length that will
  727.     ' be used to pre-allocate the string lengths for faster string operations.
  728.  
  729.     For walk = lbA To ubA
  730.         lpad = GoPadLoop(atPS(walk), nums)  ' Calc the padding length for this item
  731.         If lpad > lpads Then lpads = lpad   ' Set lpads to longest padding length
  732.         clen = citems(walk) + lpad          ' Calc the length of this item when padded
  733.         citems(walk) = clen                 ' Record the new length of this item when padded
  734.     Next
  735.  
  736.     ' Pre-allocate the buffer array strings without assigning any data into them.
  737.  
  738.     For walk = lbA To ubA
  739.         clen = citems(walk)
  740.         CopyMemByV lpS + (walk * n4), VarPtr(AllocStrB(n0, clen + clen)), n4
  741.     Next
  742.  
  743.     padZs = String$(lpads, "0")  ' Create pad to longest padding length
  744.  
  745.     ' Next, pad all string items containing numeric characters, based on
  746.     ' the longest number for that position, into the temp string array.
  747.  
  748.     ' Step through each item building the temp string with padded numeric
  749.     ' chars using recorded info in atPS.
  750.  
  751.     For walk = lbA To ubA
  752.         GoBufLoop atPS(walk), sA(walk), sAtemp(walk), n1, citems(walk)
  753.     Next
  754.  
  755.     ' To sort numeric values in a more intuitive order we sort with an indexing
  756.     ' sorter to index the string array which will provide sorted indicies into all
  757.     ' the working arrays.
  758.  
  759.     prevOrder = SortOrder        ' Cache SortOrder property
  760.     prevMethod = SortMethod      ' Cache SortMethod property
  761.     
  762.     ' First sort with binary comparison to seperate upper and lower case letters
  763.     ' in the order specified by CapsFirst.
  764.  
  765.     'CapsFirst: False(0) >> Descending(-1) : True(-1) >> Ascending(1)
  766.     SortOrder = (CapsFirst * -2&) - n1
  767.     SortMethod = vbBinaryCompare
  768.     strSwapSort4Indexed sAtemp, idxA, lbA, ubA
  769.  
  770.     ' Next sort in the desired direction with case-insensitive comparison to group
  771.     ' upper and lower case letters together, but with a stable sorter to preserve the
  772.     ' requested caps-first or lower-first order.
  773.     
  774.     ' Notice we pass on the indexed array in its pre-sorted state to be further modified.
  775.     
  776.     ' Notice also we are comparing the padded items in the temp string array whose items
  777.     ' are still in their original positions, which of course corresponds to the indices
  778.     ' of the source string array.
  779.  
  780.     SortOrder = prevOrder        ' Reset SortOrder property
  781.     SortMethod = vbTextCompare
  782.     strStableSort2Indexed sAtemp, idxA, lbA, ubA
  783.  
  784.     SortMethod = prevMethod      ' Reset SortMethod property
  785. End Sub
  786.  
  787. ' + Pretty Number Compare Function +++++++++++++++++++++++++
  788.  
  789. ' This function will compare two string items containing numeric
  790. ' characters in a more intuitive order. It will take into account
  791. ' all occurences of numbers in string items of any length.
  792.  
  793. Public Function StrCompNumbers(sThis As String, sThan As String, Optional ByVal CapsFirst As Boolean) As eCompare '-⌐Rd-
  794.     Dim tPNthis As tPrettySort, tPNthan As tPrettySort
  795.     Dim sTempThis As String, sTempThan As String
  796.     Dim lenPthis As Long, lenPthan As Long
  797.     Dim lpad As Long, lpads As Long
  798.     Dim nums() As Long, eComp As eCompare
  799.  
  800.     ' First, gather information about the string items to use for the padding process.
  801.  
  802.     lenPthis = Len(sThis)
  803.     lenPthan = Len(sThan)
  804.  
  805.     ' Set nums to the maximum position that numbers can occur in the strings.
  806.  
  807.     If lenPthis > lenPthan Then
  808.         ReDim nums(n0 To lenPthis) As Long
  809.     Else
  810.         ReDim nums(n0 To lenPthan) As Long
  811.     End If
  812.  
  813.     ' Find all occurences of numeric chars in the items.
  814.  
  815.     GoNumLoop tPNthis, sThis, nums, lenPthis + n1
  816.     GoNumLoop tPNthan, sThan, nums, lenPthan + n1
  817.  
  818.     ' Next calculate the padding length for all num inst's in each item, and
  819.     ' add them together to determine the total padding needed for each item.
  820.  
  821.     ' The total lengths are calculated to identify the length that will be
  822.     ' used to pre-allocate the string lengths for faster string operations.
  823.  
  824.     lpad = GoPadLoop(tPNthis, nums)   ' Calc the padding length for this item
  825.     lpads = lpad                      ' Set lpads to this item pad length
  826.     lenPthis = lenPthis + lpad        ' Record the new length of this item when padded
  827.  
  828.     lpad = GoPadLoop(tPNthan, nums)   ' Calc the padding length for than item
  829.     If lpad > lpads Then lpads = lpad ' Set lpads to longest padding length
  830.     lenPthan = lenPthan + lpad        ' Record the new length of than item when padded
  831.  
  832.     padZs = String$(lpads, "0")  ' Create pad to longest padding length
  833.  
  834.     ' Pre-allocate the buffer strings for faster string building operations.
  835.  
  836.     sTempThis = Space$(lenPthis)
  837.     sTempThan = Space$(lenPthan)
  838.  
  839.     ' Next, pad all string items containing numeric characters, based on
  840.     ' the longest number for that position, into the temp string items.
  841.  
  842.     ' Step through each item building the temp string with padded numeric
  843.     ' chars using recorded info in tPNthis and tPNthan.
  844.  
  845.     GoBufLoop tPNthis, sThis, sTempThis, n1, lenPthis
  846.     GoBufLoop tPNthan, sThan, sTempThan, n1, lenPthan
  847.  
  848.     ' Next, we compare the padded items with case-insensitive comparison.
  849.  
  850.     eComp = StrComp(sTempThis, sTempThan, vbTextCompare)
  851.  
  852.     ' If the items are equal with case-insensitive comparison we return the
  853.     ' order specified by CapsFirst, else we return the case-insensitive result.
  854.  
  855.     If eComp = Equal Then
  856.  
  857.         ' To order items that are spelled the same in a more consistent order we
  858.         ' compare with binary comparison to seperate upper and lower case letters
  859.         ' in the order specified by CapsFirst.
  860.  
  861.         'CapsFirst: False(0) >> Descending(-1) : True(-1) >> Ascending(1)
  862.         lpad = (CapsFirst * -2&) - n1
  863.         StrCompNumbers = StrComp(sTempThis, sTempThan, vbBinaryCompare) * lpad
  864.  
  865.     Else
  866.         StrCompNumbers = eComp
  867.     End If
  868.  
  869. End Function
  870.  
  871. ' + Pretty Sorter ++++++++++++++++++++++++++++++++++++++++++
  872.  
  873. ' Sort with binary comparison to seperate upper and lower
  874. ' case letters in the order specified by CapsFirst.
  875.  
  876. ' Then sort in the desired direction with case-insensitive
  877. ' comparison to group upper and lower case letters together,
  878. ' but with a stable sort to preserve the requested caps-first
  879. ' or lower-first order.
  880.  
  881. Sub strPrettySort(sA() As String, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True) '-⌐Rd-
  882.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  883.     Dim aIdx() As Long, lAbuf() As Long
  884.     Dim lpS As Long, lpL As Long
  885.     Dim walk As Long, cnt As Long
  886.  
  887.     cnt = ubA - lbA + n1            ' Grab array item count
  888.     If (cnt < n1) Then Exit Sub     ' If nothing to do then exit
  889.  
  890.     strPrettySortIndexed sA, aIdx, lbA, ubA, CapsFirst
  891.  
  892.     ReDim lAbuf(lbA To ubA) As Long
  893.     lpS = VarPtr(sA(lbA))
  894.     lpL = VarPtr(lAbuf(lbA))
  895.     CopyMemByV lpL, lpS, cnt * n4
  896.  
  897.     For walk = lbA To ubA
  898.         aIdx(walk) = lAbuf(aIdx(walk))
  899.     Next
  900.  
  901.     lpL = VarPtr(aIdx(lbA))
  902.     CopyMemByV lpS, lpL, cnt * n4
  903. End Sub
  904.  
  905. ' + Indexed Pretty Sorter ++++++++++++++++++++++++++++++++++
  906.  
  907. Sub strPrettySortIndexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long, Optional ByVal CapsFirst As Boolean = True) '-⌐Rd-
  908.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  909.     Dim lAbuf() As Long
  910.     Dim lpS As Long, lpL As Long
  911.     Dim walk As Long, cnt As Long
  912.     Dim prevMethod As eCompare
  913.     Dim prevOrder As eSortOrder
  914.  
  915.     cnt = ubA - lbA + n1            ' Grab array item count
  916.     If (cnt < n2) Then Exit Sub     ' If nothing to do then exit
  917.  
  918.     prevOrder = SortOrder           ' Cache SortOrder property
  919.     prevMethod = SortMethod         ' Cache SortMethod property
  920.  
  921.     'False(0) >> Descending(-1) : True(-1) >> Ascending(1)
  922.     SortOrder = (CapsFirst * -2) - 1
  923.     SortMethod = vbBinaryCompare
  924.     strSwapSort4Indexed sA, idxA, lbA, ubA
  925.  
  926.     SortOrder = prevOrder           ' Reset SortOrder property
  927.     SortMethod = vbTextCompare
  928.     strStableSort2Indexed sA, idxA, lbA, ubA
  929.  
  930.     SortMethod = prevMethod         ' Reset SortMethod property
  931. End Sub
  932.  
  933. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  934.  
  935. Private Sub GoNumLoop(tPS As tPrettySort, item As String, nums() As Long, ByVal itemEnd As Long) '-⌐Rd-
  936.     Dim lPos As Long, oc As Long     ' Read pointer and occurence counter
  937.     Dim lInst As Long, clen As Long  ' Inst start and length variables
  938.  
  939.     Do While lPos < itemEnd                         ' Do while position is before item end
  940.         Do: lPos = lPos + n1                        ' Increment position
  941.             If lPos = itemEnd Then Exit Do          ' Search only up to item end
  942.         Loop Until IsNumeric(Mid$(item, lPos, n1))  ' Loop thru item until we find a numeric char
  943.  
  944.         If lPos < itemEnd Then           ' If numeric char found before item end
  945.             oc = oc + n1                 ' Increment num inst occurence count
  946.             lInst = lPos                 ' Cache num inst start position
  947.  
  948.             Do: lPos = lPos + n1                        ' Increment position
  949.             Loop While IsNumeric(Mid$(item, lPos, n1))  ' Find end of num inst
  950.  
  951.             clen = lPos - lInst          ' Cache nums count of num inst
  952.             If clen > nums(lInst) Then   ' Compare the length of num inst's at this pos and
  953.                 nums(lInst) = clen       ' set nums(startpos) to longest num inst length
  954.             End If
  955.             'Assert oc <= MAX_DISCRETE_OCCUR_NUMS
  956.             tPS.idxn(oc) = lInst  ' Record start of this num inst
  957.             tPS.cnums(oc) = clen  ' Record length of this num inst
  958.     End If: Loop
  959.     tPS.occurs = oc   ' Record occurences of num inst's within this item
  960. End Sub
  961.  
  962. Private Function GoPadLoop(tPS As tPrettySort, nums() As Long) As Long '-⌐Rd-
  963.     Dim clen As Long, lInst As Long  ' Write length and index variables
  964.     Dim lpads As Long, lpad As Long  ' Total padding and occurence counter
  965.     Dim oc As Long: oc = n1          ' Set occurence counter
  966.  
  967.     Do Until oc > tPS.occurs      ' Do until no more occurences of numeric chars
  968.         lInst = tPS.idxn(oc)      ' Grab index of next num inst
  969.         clen = tPS.cnums(oc)      ' Grab nums count of next num inst
  970.         lpad = nums(lInst) - clen ' Calc pad len (longest at this pos-clen)
  971.         tPS.cpads(oc) = lpad      ' Store pad len for this item pos
  972.         lpads = lpads + lpad      ' Calc total padding needed for this item
  973.         oc = oc + n1              ' Increment num inst count
  974.     Loop
  975.     GoPadLoop = lpads  ' Return total padding for this item
  976. End Function
  977.  
  978. Private Sub GoBufLoop(tPS As tPrettySort, item As String, temp As String, ByVal lPos As Long, ByVal ctemp As Long) '-⌐Rd-
  979.     Dim lRead As Long, oc As Long    ' Read pointer and occurence counter
  980.     Dim clen As Long, lInst As Long  ' Write length and index variables
  981.     lRead = n1: oc = n1              ' Set pointer and occurence variables
  982.  
  983.     Do Until oc > tPS.occurs                        ' Do until no more occurences of numeric chars
  984.         lInst = tPS.idxn(oc)                        ' Grab index of next num inst
  985.         clen = lInst - lRead                        ' Calc sub-str length up to next num inst
  986.         Mid$(temp, lPos) = Mid$(item, lRead, clen)  ' Grab sub-str up to next num inst
  987.         lPos = lPos + clen                          ' Reset write pointer pos
  988.         clen = tPS.cpads(oc)                        ' Grab pad len for this pos
  989.         Mid$(temp, lPos) = Left$(padZs, clen)       ' Write padding before nums
  990.         lPos = lPos + clen                          ' Reset write pointer pos
  991.         clen = tPS.cnums(oc)                        ' Grab nums count of this num inst
  992.         Mid$(temp, lPos) = Mid$(item, lInst, clen)  ' Write numeric chars
  993.         lPos = lPos + clen                          ' Reset write pointer pos
  994.         lRead = lInst + clen                        ' Reset read pointer pos
  995.         oc = oc + n1                                ' Increment num inst count
  996.     Loop
  997.     If Not lPos > ctemp Then
  998.         Mid$(temp, lPos) = Mid$(item, lRead)        ' Assign the rest of the item to temp array
  999.     End If
  1000. End Sub
  1001.  
  1002. ' + Validate Index Array +++++++++++++++++++++++++++++++++++++++
  1003.  
  1004. ' This will initialize the passed index array if it is not already.
  1005.  
  1006. ' This sub-routine requires that the index array be passed either
  1007. ' prepared for the sort process (see the For loop) or that it be
  1008. ' uninitialized (or Erased).
  1009.  
  1010. ' This permits subsequent sorting of the data without interfering
  1011. ' with the index array if it is already sorted (based on criteria
  1012. ' that may differ from the current process) and so is not in its
  1013. ' uninitialized or primary pre-sort state produced by the For loop.
  1014.  
  1015. Sub ValidateIdxArray(lIdxA() As Long, ByVal lbA As Long, ByVal ubA As Long)
  1016.     Dim bReDim As Boolean, lb As Long, ub As Long, j As Long
  1017.     lb = &H80000000: ub = &H7FFFFFFF
  1018.     bReDim = Not InitedArray(lIdxA, lb, ub)
  1019.     If bReDim = False Then
  1020.         bReDim = lbA < lb Or ubA > ub
  1021.     End If
  1022.     If bReDim Then
  1023.         ReDim lIdxA(lbA To ubA) As Long
  1024.         For j = lbA To ubA
  1025.             lIdxA(j) = j
  1026.         Next
  1027.     End If
  1028. End Sub
  1029.  
  1030. ' + Stable QuickSort 2.2 Indexed Version ++++++++++++++++++++
  1031.  
  1032. ' This is an indexed stable non-recursive quicksort.
  1033.  
  1034. ' This is the latest version of my stable Avalanche algorithm, which
  1035. ' is a non-recursive quicksort based algorithm that has been written
  1036. ' from the ground up as a stable alternative to the blindingly fast
  1037. ' quicksort.
  1038.  
  1039. ' It has the benifit of indexing which allows the source array to
  1040. ' remain unchanged. This also allows the index array to be passed
  1041. ' on to other sort processes to be further manipulated.
  1042.  
  1043. ' It uses a long array that holds references to the string arrays
  1044. ' indices. This is known as an indexed sort. No changes are made
  1045. ' to the source string array.
  1046.  
  1047. ' After a sort procedure is run the long array is ready as a sorted
  1048. ' index to the string array items.
  1049.  
  1050. ' E.G sA(idxA(lo)) returns the lo item in the string array whose
  1051. ' index may be anywhere in the string array.
  1052.  
  1053. Sub strStableSort2Indexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long)
  1054.     ' This is my indexed stable non-recursive quick sort
  1055.     If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  1056.     Dim item As String, lpStr As Long, lpS As Long
  1057.     Dim walk As Long, find As Long, midd As Long
  1058.     Dim base As Long, run As Long, cast As Long
  1059.     Dim idx    clen = gethe eC Write l    Dim baUhe pad  ' Rec
  1060.  
  1061. ' It has the benifit of ind
  1062.     lbA, ubA
  1063. che a1rty
  1064. End tring, ly as ut assigning any rty
  1065. End tringf ind
  1066.     lbA, ubA
  1067. che a1rty
  1068. End Oi
  1069.        lbA,r indices. This is known as F          ris         ris         ris         ris         r1
  1070. ' ind)noccurence counter>Rec
  1071.  
  1072. ' Itkos) tring a=nd
  1073.     lbA, ubA
  1074. sis iuntidxAs) tring a=nd
  1075.    pecifNps, nulen = get8    Dim bReDim As  ing itemste por manipulated.
  1076.  
  1077. ' It uses a long array that holds references to the string arrays
  1078. ' indices. This is known as an indexed sort. No changes are made
  1079. ' to the source string array.
  1080.  
  1081. ' After a sort proceduredicgths for faster string operations.
  1082.  
  1083.     lpad = GoPadLoop(tPNthis, nums)   ' Ca Sor Aft, spet SortMethod property
  1084. End Sub
  1085. aod propertyding length fDim baUhe pad  ' Rec nu count of thxtCompt processes to be fuocesses lestring arrays
  1086. ' indices. This ing
  1087.   ais  esses iIf Not l iIf Not l n(idxA(lo)) ed sor
  1088.  
  1089.                t proceduredicgths for faster 
  1090.   ais Not l iIf NDt8enc * 2ywhcgths y that has bentsm1rtyp_ sA(idxA(lo)) rlylpad      ' 0ed).
  1091.  
  1092. ' This p
  1093.     CopyMemByV lpL, lp) ed sor
  1094.  
  1095.  
  1096.     lbA, ubA
  1097. che a1rty
  1098. End Oi
  1099. agptringf ind
  1100.     lbA, ubA
  1101. che_rtOrder = (Cappare '-⌐Rd-
  1102.     Dim t For los  esses =yp_ sA(idx s  esses =yp lpL =ex Array (Cappare '-⌐Rd-
  1103.     Dim t For los  essrwggggggggster string building operations.
  1104.  
  1105.     sTempThis = Space$(lenPthis)
  1106. y as   lentritidx, ubA
  1107. che a1rty
  1108. End Oy0nse
  1109.  
  1110.     sTempThis = Space lptem pad len) rlyl).>aWrite l    Dim baUex ArraO rlem pad len) rlyl)pThis = Space lptem pad len) rlyl).>aWri,omplen) rlyl).>aWri,omplen) rlyl).>aWri,ompA(idxA(elenElen) rlyl)pThis "+++++++++++
  1111.  
  1112. ' Sort with binarysrlyl
  1113. ' Sort withi As Boolean = True) '-⌐Rd-
  1114.     If Not Intions.
  1115.  
  1116.  :)op
  1117.     If Not lPos > alized (or Erased).
  1118.  
  1119. ' This permits subsequent sorting ofm prebisteeeee
  1120.       If rlyC Lonnnnnnnnnnnnis = Space lptem pad leeFdThan  por maad leeFxsis iunt If r    CopyMeaWri,ompA(ie If rlbA) d Oi
  1121. agptrin=r<:ges of numeric chars in the strie 
  1122.     stzed (or Erased). ,ot withi A prevOrderng buildinsubsequent rraO rlem pad n ubA
  1123. che atem bosite anywhere in th>y
  1124. End Oi
  1125. ae strie 
  1126.     Uhe pad  ' Rec
  1127.  
  1128. ' I Tht rrEnd Oi
  1129. ae strlbA, ubA ' Reset ruildch If rlbA) dixrting ofm.                 tsis iunt If r    CopyMeaWrt2ilbA) BqaA    Mid$(tem  E          2< ofn = treDim l idxA,,,,,,,,,,,,,,st
  1130.      , idxAsAtemp, idxA, lbA, ubA
  1131.  
  1132.     SortMethod = prevMethod      ' Reset SortMethod property
  1133. Ecursivg  ais  eslfEcurs the source stCopyMea  permits su  Sor-   i
  1134. agptringaurce stCopyMn:s  eslfEcurs the source stCop(rf,,,,sdea fAs Boolean = True) +
  1135. ' Sort with binary itemEnd          bm = Not InitedArray(lId). ,ot withi    SortMeti
  1136.     'fas lReadRpns sub-routi 2< oindexe of o=A()  Write l   As Long)hars t num i    lP,,,,,,,,
  1137.   bgesuuuuuuuu If Not InitedArray(sA, lbA, ubA) Then Exit Sub
  1138.     Dim aIdx() As Long, lAbuf() As Long
  1139.     Dim lpS As Long, lpL As Long
  1140.     Dim walk As Long, cnt As Long
  1141.  
  1142.     cnt = ubA - lbA + n1            ' Grab array item count
  1143.     If (cnt < n1) Thsivg por walk = lbAnlirXthhhhhhhhhpermits su   )hars t num i    lP,,f my stable Avalrm walk As Long, cntchn j
  1144.       ce stCm
  1145.     ' ThiMemByV lpA, lbA, ubA) Then Exit Sub
  1146.     Dim aIdx() As t j
  1147.       ce stawb n1    NmpS, lpL,lC lpL,lche a1rt  ais o() As t j
  1148.       d O:s  eslum inst's atCm
  1149.   lee     , idxAsA
  1150. che a1rty
  1151. End Oi
  1152.        lbA,r indices. This is known as F         maet w  filenmPthis = clr As -whO rnorettySthis, Mid$(sThi l)as lRe*s known as F i    nmPt$(sTis k As Lt pointer a,own adLoop(tPS ubsequent rraO rlem pad n nnt <l' Rec nl2d n n       >aWri,ompA(i i,ompA(i ic aIx Descending(-1) : True(-1) >> Ascending(1)
  1153.  Gn GoPadLoopi=bo   lentrit Long
  1154.     Dim walk As Long, find As Long, midd As Long
  1155.     Dim base As Long, run As Long, cast As Long
  1156.     Dim idx    clen = gethe eC Write l    Dim baUhe pad  ' Rec
  1157.  
  1158. ' It has the benifit of ind
  1159.     lbA, ubA
  1160. che a1rty
  1161. End tring, ly as ut assigning anri,omplen) rlyl).>aWrunding a=nis isigninglic chars    Dirscest
  1162.  
  1163.             clen = lPos -faster string operations.
  1164.  
  1165.  ong, lpS As Long
  1166.     Dim walk As Long, find As Long, midd As Long
  1167.  m lpS AEnd Oi
  1168. agptringf ind
  1169.     lbA, ubA n
  1170.         bRehan, lenrettsms with case-insen Lonngly f, leStore padf indr maad leeFxsis iunt If , leStond
  1171.  rwith kcompa =ex Array lglye) +
  1172. ' Sort with bina=ex Arb, nul stawb n1    Nmt pointer a,own adLoop(tPS u .rbm= tPS.idxn(oc)    with bina=ex ArbpoidxA,RArbpoidxArnoretdd As Lon lpaad less su   )harown as F         maet w  filenmPthis = clr As -w pad:            maet w  filenmPthis = clr As -w pad:       lo))TThis o, cast As Long
  1173.   ubA
  1174. lpS AEnd Oi
  1175. lesd leewe  lo))im     maet w  filenmPthis = clr As -w pad:       n      d len f re maet w  filenmPthis = clr As -w pad:   lentrg
  1176.  
  1177.  rssrwggggggggster stri compVor-   i
  1178. ag maet w  ft length
  1179.             End If
  1180.             'Assert oc <= MAX_DISCRETE_OCCUR_NUMS
  1181.           f Grouptbf          'Assert oc y
  1182. End tring, ly as ut oc <= MAX_DISCRETE_OCCUR_NUMS
  1183.           f Grouptbf          'Assert oc y
  1184. End tring, ly as ut ocoupdThan  por maad leeFxsis iunt If r    CopyMeaWri,ompA(ie If rlbA) d Oi
  1185. agptrin=r<:ges of2esdrlbA) d Oi
  1186. agp por maadA    DaadA    ing ofm prU2           f   rray t, leStond
  1187.  rwith kcompa =ex Arrcursive qu n
  1188.   SutrinAH end
  1189. bRehan, lenretagp por maa  ' Rec
  1190. -otagOlr As -w pad:       bRehandfin, lpS As LonDehandfin, l
  1191.   ud len) rlyl).>aWri,oAs -w pad:       n      d len fanri,omplnnnnnnis = Space lptem pad le  len 
  1192.  
  1193.     SortMetho w  filenmPthis = clr As -wnnnnnthis num inst
  1194.     End Is num inst
  1195.     End Isw ubAster stri compVor-  B       tPmd:    or maad lea Sornd tring, ly a.
  1196.  
  1197. ' E.G sA(idxA(lo))( ThxNothis = clr Isw ub 0Ue datadd Oi
  1198. vfanri,omplnnnnnnis = Space lptem pad le  len 
  1199.  
  1200.     SoroD2d n n oeulsrty
  1201. E ub 0Ue datadd Oi
  1202. vfanri,omplnnnnnnis = Space lptem pad le  len 
  1203.  
  1204.     SoroD2aaAbt As i
  1205. vlr ub 0Ue datadd Oi
  1206. vfanri,ompln ndfin, lpS Ald pointer pos
  1207.         oc =  r1
  1208. ' iaadA  smbd)n  lpads + lpad      ' Calc total paddiAi   ' length
  1209.  
  1210.     ' Next, lprence countnnnnis = Spemp,eeFxsie IsNumc w  filenmPthis = clr l       filenmPthis l       filenmPthis l       filenmPthis l       filenmPthis l       filenmPthis l       filenmPthis l   =nifIbPthilenmPthis lcompa        s s
  1211.   SutrinAH end
  1212. baathi( filenhe a1rtyo    Rns    e etho w    Isw ubAssw ubAlenhe a1rtyog, ByVal    lbA'p,eeFxsiAce u )                        ' Grab index of next Rter string   bReDfor thiilenmPthis lco5nter
  1213.     is lco5ntAs Long
  1214.  m lpS AEnd Oi
  1215. agptringf ind
  1216. ,r pos
  1217.      tAs Long
  1218.  m lpS AEnd Oi
  1219. agptrtem num i  d len nd Oi
  1220. agsoccurs      ' Do until no mor         If AEnd Oi
  1221. agpt=ex st c lRead As Long, oc As Ltmaetalrm walkl = (Cappare '-⌐Rd-
  1222. agpt=ex st c lRead As Long, oc As Ltmaetalrm 6o5ntAL =ex Array (Cappare '-⌐Rd-
  1223.     Ditt      Ditt      Ditt  etalrmg, oc lbA, ubAotCompare)
  1224. w pad:   lit
  1225.   SutrinAHpare)
  1226. w pad:   lit
  1227.   SutrinAHD       .lL =ex Arragptr(numssssanextOrd End IaadA  sm)  'a1rt       ' Set occurentrin + n1   As L Isw ubAssw ubAlenhe a1rtyog, ByVaf2esdrlbA) d Oi
  1228. agp ad As )elfrs with the index)elfrs with the index)elfrsiables
  1229.  ->d
  1230. baUrnith b4-
  1231.     Ifs itiablex)elfrsiSDumLoop tPNthan, sDi  sTeany stabAielfrsi compVlae2    f   rray t, ab index of ssw ubAlenhe a1rtyog, ByVaf2esdrlbA) d  As Lh :)oplOftsis iunt If r    Co filenmPthisointer Nextst If amaetal1 f   0rray lum walkl = (C:        atalrmg, ocpyMemByV lp
  1232. w pad:aisointer Nexer
  1233.     iXe datadd len for this pos
  1234.           maetfrsiableod IaadA  sm)t I Co filenmPthisolpfrsiableod IaadA  sm)riteroceduredicg Lh :)opeod IaadA  s Sutrid     lPot As Lhsylo))lestring acdadA  s Su nn    lPot As Lhsylo))lestring acdadA  s Su nn   olds references to thi d len    Dittnnnnis = Spacu SuPthis = emp array
  1235.  ce lp)rencb inded+he source afs i
  1236.           maetfrsiableod IaadA  sm)t I source afs , midd As LongbA  sive qu
  1237.  m lpnst) Theotadd ud
  1238. baUrnith bprty  instAerences to thi d len    Dim lpnng arraypl  lPot As Lhs sourcet f Gr:'=end
  1239. bRehan, lenretagp por maa  ' Rec
  1240. -otamLoop tPNthan,mpVlae2   Not Initedlk As Long, siablrstring acdadA  s Su nulready nn   olds ref c  DimCo filenmPthisointer Nextst If amaetal1 f   0extst If arrrrr         maetfrsiableod IaadA  sm)t I Co filenmPthisolpfrsiableod IaadA  sm)riteet w  ft length
  1241.             End s Lond s Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond Lond n, la3s        End If
  1242.     d Ls Lond s Lond Lond L nn   olds refeb, u Ls Lond s Lond Lond Larsiableod IaadA  sm)Du0 Calc      sdLoop(tPSsesd lb u Ls  im       atalrmg, ocpyMemBy sdLoop(tPSpproduced pr. w  s Lond sa4r    0Sort 2p(tPBy sdLoop(tPSpproduced pr. wing  Not InitedArray(lId)ay t has the benc  DimCoet pr. wing  gOlr As -w pad:       bRehanmByV lp
  1243. w pad:aisointly aitive order. It will take into accoet pr(tPSsesd lb u Ls  im       atlmlr As -whO r'hose
  1244. ' index may be anywhere in the string array.
  1245.  
  1246. Sub strStableSort2Indexed(sA() As String, idxA() As Long, ByVal lbA As Long, ByVal ubA As Long)
  1247.     ' This is my indexed stable non-recursive quick sort
  1248.     If Not InitedArray(sA, lbA, ubA) Then Exit SunAH endy(sA, lbA, ubA) Then l the . It willBblrstr  2    f   rray t, ab index of ssw ubAlenhe a1rtyog, ByVaf2esdrlbA) d  As LlpS Aond Lo   sdLong
  1249.   ung(1)
  1250.  Gn Gtive order.End t)pS Aond Lo n,iong
  1251.  s))( ThxNothis = clr Isw ub,lCompareing acdadA  s eing2iuious Dim yon to seperate upper and lower
  1252. ' case letters in the order specified by CapsFirst.
  1253.  
  1254. ' yw pad:   lentrg
  1255.  
  1256.  rssrwggggggggster stri compVor-   i
  1257. ag maet w  ft length
  1258.       th binary d padding before filenamen.
  1259.  
  1260. ' yw pabefore filenamen.
  1261.  
  1262. ' ywgggggggster st png  ag maet w 0AIdx() As td:   lit
  1263.   SutrinAHpare)
  1264. w pad:   lit
  1265.   SutrinAHD       .lL =ex Arragptr(numssssanextOrd End Iaathi( by CapsFirst.
  1266.  
  1267. ' yw pad:   lentrgg, ByValt
  1268.     cer stri co the order specified by CapsFirst.
  1269.  
  1270. ' yFPr      it
  1271.   SutridA     ' Thisohe order spPindex    yog, ByVaf2esth case-insen Lo'Xnd ine-(C:   'stthi d De ord paded Iaathi( by ord paded Iueh++++++++++++++++++++++se-insen g
  1272.  
  1273. et w  ft le Lo nnis    '++++++dicgths for faster stro the  
  1274. et w  f yw pad:   le>'stt
  1275. -otar s(:   lentrgl iIf Not l n(idmae     ' Cale Lo nnis ee