home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 1_2002.ISO / Data / Zips / Array-hand245698132001.psc / mdlArray.bas
Encoding:
BASIC Source File  |  2001-08-13  |  74.4 KB  |  2,182 lines

  1. Attribute VB_Name = "mdlArray"
  2. ' /*********************************************************
  3. ' | Name:         mdlArray.bas
  4. ' | Description:
  5. ' |   -> Package of all array-related procedures I created over the years.
  6. ' |   -> Includes many sort algorithms.
  7. ' |   ->
  8. ' |   -> This code is intellectual property of Philippe Lord.
  9. ' |   ->
  10. ' |   -> You may use/modify this file as much as you want, as long as this
  11. ' |   -> file commented header remains, and more important,
  12. ' |   -> that it does not get modified in any possible way.
  13. ' |   ->
  14. ' |   -> You may find updates of this code at http://Philippe.Lord.MD
  15. ' |   -> This code was parsed with Marton's VB Code Formatter v4.
  16. ' |   -> That program is a freeware I wrote, available at the above site.
  17. ' |   ->
  18. ' |   -> If you are hiring personnel, feel free to contact me :)
  19. ' |
  20. ' | Created:      13 august 2001
  21. ' | Author(s) info:
  22. ' |   By:         Philippe Lord // Marton
  23. ' |   Email:      StromgaldMarton@Hotmail.com
  24. ' |   ICQ:        12181387
  25. ' | Environment:
  26. ' |   -> Created in 1280x1024
  27. ' |   -> Arial Narrow 8
  28. ' |   -> TAB = 3
  29. ' |   -> WinXP 2428
  30. ' \*********************************************************
  31.  
  32. 'Notes:
  33. '  -> Binary searchs works only on sorted arrays.
  34. '  -> A hash algorithm can only be applied to a string, explaining the absence of HashSearch on other types than strings.
  35. '  -> HashSearch does not requires anything to be sorted.
  36. '  -> If you add or remove a string from the string array on a hash algorithm, you must ABSOLUTELY rebuild TOTALLY the hash table.
  37. '  -> All indexed search & HashSearch will recreate the index if not supplied (supplied empty).
  38. '  -> Indexed sorts will only sort the index array, thus making the algorithm faster.
  39. '     BUT be warned that it is slower on a long array.
  40. '  -> An hash array is bigger than the original array (about 4 times).
  41. '  -> All sort algorithms includes support for ascending/descending order.
  42. '     However, all functions other than sorting does NOT support descending order.
  43. '  -> Standard QuickSort algorithms are generally fast, but there exists an exception...
  44. '     When the array is *nearly* sorted, QuickSort can be slow (up to 2 times slower).
  45. '     However, the included TriQuickSort algorithm does not suffer from this case, because it combines
  46. '     two sort algorithms, and because it uses 3 medians.
  47.  
  48. 'Efficiency recommendations:
  49. '  (We assume the hash algorithm is based on the full string, not only parts of it.)
  50. '  -> The longer the strings are, the better will a binary search be.
  51. '  -> The bigger the string array, the faster a hash search will be. (comment above has priority over this one)
  52. '  -> If you have under 50 items to sort, use ShellSort.
  53. '  -> If you have over 50 items to sort, use TriQuickSort.
  54.  
  55. 'Functions contained within this .bas file:
  56. '  // Add
  57. '  AddToAnyArray                    ' Adds the data at the nth position.
  58. '  AddToLongArray                   ' Adds the long at the nth position.
  59. '  AddToStringArray                 ' Adds the string at the nth position.
  60. '  AddToSortedAnyArray              ' Adds the data in a sorted array, keeping the array sorted.
  61. '  AddToSortedLongArray             ' Adds the long in a sorted long array, keeping the array sorted.
  62. '  AddToSortedStringArray           ' Adds the string in a sorted string array, keeping the array sorted.
  63. '  AddToIndexedAnyArray             ' Adds the data at the end of the array, keeping the index array sorted.
  64. '  AddToIndexedLongArray            ' Adds the long at the end of the long array, keeping the index array sorted.
  65. '  AddToIndexedStringArray          ' Adds the string at the end of the string array, keeping the index array sorted.
  66.  
  67. '  // Remove (if one item, array gets erased)
  68. '  RemoveFromAnyArray               ' Removes the nth entry.
  69. '  RemoveFromLongArray              ' Removes the nth long.
  70. '  RemoveFromStringArray            ' Removes the nth string.
  71. '  RemoveFromIndexedAnyArray        ' Removes the nth entry (either array or index), keeping the index array sorted.
  72. '  RemoveFromIndexedLongArray       ' Removes the nth long (either array or index), keeping the index array sorted.
  73. '  RemoveFromIndexedStringArray     ' Removes the nth string (either array or index), keeping the index array sorted.
  74.  
  75. '  // Hash
  76. '  BuildHashTable                   ' Builds a hash array using sent string array.
  77. '  HashSearch                       ' Returns the position of the searched string on an unsorted string array, using an hash array.
  78.  
  79. '  // Search (-1 = ERROR_NOT_FOUND)
  80. '  BinarySearchAny                  ' Returns the position of the searched data onto a sorted (ascending) array.
  81. '  BinarySearchLong                 ' Returns the position of the searched long onto a sorted (ascending) long array.
  82. '  BinarySearchString               ' Returns the position of the searched string onto a sorted (ascending) string array.
  83. '  IndexedBinarySearchAny           ' Returns the position of the searched data in an array using a sorted (ascending) index.
  84. '  IndexedBinarySearchLong   (slow) ' Returns the position of the searched long in an array using a sorted (ascending) index.
  85. '  IndexedBinarySearchString        ' Returns the position of the searched string in an array using a sorted (ascending) index.
  86. '  SequentialSearchAnyArray         ' Returns the position of the searched data onto an array.
  87. '  SequentialSearchLongArray        ' Returns the position of the searched long onto a long array.
  88. '  SequentialSearchStringArray      ' Returns the position of the searched string onto a string array.
  89. '  isInAnyArray                     ' Determines if data is in array using a sequential search.
  90. '  isInLongArray                    ' Determines if long is in long array using a sequential search.
  91. '  isInStringArray                  ' Determines if string is in string array using a sequential search.
  92.  
  93. '  // Sort
  94. '     // < 50 -> ShellSort          ' Efficiency recommandation
  95. '     // >=50 -> TriQuickSort
  96. '  ShellSortAny                     ' Sorts the array.
  97. '  ShellSortLong                    ' Sorts the long array.
  98. '  ShellSortString                  ' Sorts the string array.
  99. '  TriQuickSortAny                  ' Sorts the array.         // TriQuickSort stands for 3-median quicksort algorithm.
  100. '  TriQuickSortLong                 ' Sorts the long array.    // The TriQuickSort algorithm combines with InsertionSort algorithm
  101. '  TriQuickSortString               ' Sorts the string array.  // when the distance gets below 5, which speeds things A LOT (over 40%).
  102. '  IndexedShellSortAny              ' Sorts the index using sent array.
  103. '  IndexedShellSortLong      (slow) ' Sorts the index using sent long array.
  104. '  IndexedShellSortString           ' Sorts the index using sent string array.
  105. '  IndexedTriQuickSortAny           ' Sorts the index using sent array.
  106. '  IndexedTriQuickSortLong   (slow) ' Sorts the index using sent long array.
  107. '  IndexedTriQuickSortString        ' Sorts the index using sent string array.
  108. '  isSortedAnyArray                 ' Determines if the array is sorted.
  109. '  isSortedLongArray                ' Determines if the long array is sorted.
  110. '  isSortedStringArray              ' Determines if the string array is sorted.
  111. '  isSortedIndexedAnyArray          ' Determines if the index is sorted.
  112. '  isSortedIndexedLongArray         ' Determines if the index is sorted.
  113. '  isSortedIndexedStringArray       ' Determines if the index is sorted.
  114.  
  115. '  // Synchronisation
  116. '  SynchroniseIndexedAnyArray       ' Sorts the array using its index (to get an ascending index).
  117. '  SynchroniseIndexedLongArray      ' Sorts the long array using its index (to get an ascending index).
  118. '  SynchroniseIndexedStringArray    ' Sorts the string array using its index (to get an ascending index).
  119.  
  120. '  // Copy/Move
  121. '  CopyAnyArray                     ' Copies an array.
  122. '  CopyLongArray                    ' Copies a long array.
  123. '  CopyStringArray                  ' Copies a string array.
  124. '  MoveAnyArray                     ' Moves an array. Source array will be erased (VB function 'Erase').
  125. '  MoveLongArray                    ' Moves a long array. Source array will be erased (VB function 'Erase').
  126. '  MoveStringArray                  ' Moves a string array. Source array will be erased (VB function 'Erase').
  127. '  MergeAnyArray                    ' Merges (combine) 2 arrays. Source array will be erased (VB function 'Erase').
  128. '  MergeLongArray                   ' Merges (combine) 2 long arrays. Source array will be erased (VB function 'Erase').
  129. '  MergeStringArray                 ' Merges (combine) 2 string arrays. Source array will be erased (VB function 'Erase').
  130.  
  131. '  // Save/Load
  132. '  SaveLongArray                    ' Dumps a long array in a string.
  133. '  SaveStringArray                  ' Dumps a string array in a string.
  134. '  LoadLongArray                    ' Rebuilds a long array from a string dump.
  135. '  LoadStringArray                  ' Rebuilds a string array from a string dump.
  136.  
  137. '  // Others
  138. '  CreateArray                      ' Returns an array of the type of the first sent argument.
  139. '  DebugDumpArray                   ' MsgBox an array. Use for debugging.
  140. '  ReverseAnyArray                  ' Reverses (inverts) an array.
  141. '  ReverseLongArray                 ' Reverses (inverts) a long array.
  142. '  ReverseStringArray               ' Reverses (inverts) a string array.
  143.  
  144.  
  145. 'Editorial on the TriQuickSort algorithm - Why is TriQuickSort so fast ?
  146. '  Since the TriQuickSort algorithm is in no way a standard sort algorithm, I will try and explain it here.
  147. '  First, I must say that the main idea started from Sun Microsystems, in java source code form. I found
  148. '  Sun's source code after a search on the internet for the 'fastest' sort algorithm (considering a uniprocessor
  149. '  configuration and a nearly-sorted OR totally unsorted array). I compared the ones that performed the best,
  150. '  and soon enough stumbled upon this one (Sun's one). Generally speaking, it was a 3-median QuickSort, a little
  151. '  twinked, of course. The 3-median QuickSort has the advantage of not suffering standard 2-median QuickSort's
  152. '  problems conserning nearly-sorted arrays  (side-note: ever tried sorting a nearly-sorted array using QuickSort?
  153. '  In case you didn't, let me tell you it gets REALLY slow, it can get MUCH slower than bubblesort in certain cases !).
  154. '  It performed very well, but there was a side-note suggesting using a second algorithm when the number of
  155. '  iterations came low (under 10). I though about it, and understood why they suggested that. First, for those
  156. '  who doesn't know how (generally speaking) a QuickSort works, I'll resume it shortly.
  157. '
  158. '     QuickSort is a recursive algorithm (thus eating lots of RAM) which splits in 2 the array,
  159. '     moving the highest ones the right side, and the lower ones the left side, but without sorting either data
  160. '     on the left or right side, all it does is putting all the lowest on the left and the highest on the right.
  161. '     Then, to sort, it calls itself back (recursively) on the left side, and the right side.
  162. '     It continues like this until everything gets sorted. Now there's 2 major problems with this.
  163. '     One is memory usage, and the second is inefficacity (slow) when the borders are close
  164. '     (when 'low' in the recursive tree) (just keep in mind I'm not going into details).
  165. '
  166. '  So now you should understand why I did another version of Sun's sort algorithm ;) I started up the algorithm
  167. '  by porting java source to VB, which led to some difficulties due to the fact that VB does not 'short-cuts'
  168. '  expressions evaluations, making it crash thru a pure porting.
  169. '
  170. '     ex: While (i - 1 >= LBound(sArray)) And (sArray(i - 1) > sTemp)      ' sArray(i - 1) CRASH !!!
  171. '
  172. '  Ok, this was easily fixed, but should give you a small idea of what had to be done. After porting their
  173. '  3-median QuickSort, I made it stop when the delta (difference) of the 2 bounds came under 10, like
  174. '  suggested by Sun Microsystems. Now, if you understood my explanation of the QuickSort algorithm, you
  175. '  should understand too that stopping the process at delta 10 means all you have to do after QuickSorting
  176. '  is to sort each sections of 10, without needing to do any compares with anything else other than the 10
  177. '  entries you're processing. Imagine just that the cutted-QuickSort sorts generally, but you need to finish
  178. '  the work off by processing packets of 10 entries.
  179. '
  180. '  But I must clarify one point.
  181. '
  182. '  Stopping the recursion tree using a delta 10 does not means IN ANY WAY that you're goin to have sections
  183. '  exactly of 10 'well-placed' entries. In fact, if you think well about the problem, and if you understand
  184. '  well the QuickSort algorithm, it means that your sections can vary from your input delta (10)
  185. '  up to 2x delta -1 (19). If you don't understand the previous remark, either trace the QuickSort's code, or read back.
  186. '
  187. '  So what does that means? Well, it means my previous statement (3 paragraphs above) we're not true if you consider
  188. '  10 to be the only valid delta. Consider either a range from 10 to 19. Now read back 3 paragraphs above ;)
  189. '
  190. '  So that was my first idea...sorting each sections individually.
  191. '
  192. '  I searched for the fastest algorithm for processing small arrays, and had in mind to call it n times, where
  193. '  n equals the number of sections. You must keep in mind that to have a good sort algorithm working
  194. '  on 10-19 entries it means your algorithm have to be as simple as possible, because you cannot even afford
  195. '  to do simple mathematical operations. You just need something simple. And fast ;)
  196. '
  197. '  I though of bubblesort first, but later I came up with a similar algorithm, which has the
  198. '  advantage of not being tied to work with a fixed number of entry (because for god's sake i would never let
  199. '  bubblesort the whole array down !). But, since it's roots are based on bubblesort's algorithm,
  200. '  for it to be effective you must keep the delta very low, under 10. That algorithm I'm talking about is
  201. '  called InsertionSort, which sadly was not designed by me. I found InsertionSort to be the perfect algorithm
  202. '  to continue the cutted-QuickSort's job. I'll copy-paste InsertionSort's algorithm below, it's pretty simple.
  203. '  But, like I said earlier, delta 10 (which gives us a 10-19 section's range) would be like saying: Hey, let's
  204. '  give out the main job to InsertionSort (which is normally slow, but in our case it gets VERY fast), which,
  205. '  BTW, is VERY stupid. You can guess I lowered down the QuickSort's delta. If you look at TriQuickSort's source
  206. '  code, you'll notice the parameter iSplit is the delta I'm talking about. I've put a default value of 4 for it,
  207. '  which gives a sections ranging from 4 to 7 in length, which gives very good results. I do not recommend you
  208. '  put a lower value to it, because QuickSort would eat up too much memory AND starts getting slow. If you put
  209. '  higher than 4, the reverse happens...you get a MUCH lower performance because InsertionSort starts bottlenecking
  210. '  a little too much.
  211. '
  212. '      Private Sub InsertionSortAny(ByRef vArray As Variant, ByVal iMin As Long, ByVal iMax As Long)
  213. '         Dim i     As Long
  214. '         Dim j     As Long
  215. '         Dim vTemp As Variant
  216. '
  217. '         For i = iMin + 1 To iMax
  218. '            vTemp = vArray(i)
  219. '            j = i
  220. '
  221. '            Do While j > iMin
  222. '               If vArray(j - 1) <= vTemp Then Exit Do
  223. '
  224. '               vArray(j) = vArray(j - 1)
  225. '               j = j - 1
  226. '            Loop
  227. '
  228. '            vArray(j) = vTemp
  229. '         Next i
  230. '      End Sub
  231. '
  232. 'SYSTEM:
  233. '-------
  234. ' -> P3 650e overclocked to 845MHz
  235. ' -> 384M RAM PC 133
  236. ' -> WinXP 2428
  237. '
  238. 'BENCHMARKS:
  239. '-----------
  240. '
  241. '(All benchmarks are made on an array of 10 000 strings having a length of 100 characters ranging from A to Z)
  242. '
  243. '(All results in seconds)
  244. '
  245. '
  246. 'Using Non-CopyMemory optimized sort algorythm
  247. '------------------------------------------------AVG-------%-----
  248. 'BubbleSort   125.8012  124.6600  125.4101       125.2904  -59421
  249. 'ShellSort    0.5310    0.5325    0.5106         0.5247    -149.3
  250. 'QuickSort    0.2404    0.2481    0.2425         0.2437    -15.77
  251. 'TriQuickSort 0.2107    0.2089    0.2120         0.2105    0.0000
  252. '
  253. 'Using CopyMemory optimized sort algorythm
  254. '------------------------------------------------AVG-------%-----
  255. 'BubbleSort   59.9765   59.3455   59.3642        59.5621   -43471
  256. 'ShellSort    0.3017    0.3121    0.2999         0.3046    -122.8
  257. 'QuickSort    0.1812    0.1788    0.1806         0.1802    -31.82
  258. 'TriQuickSort 0.1309    0.1383    0.1408         0.1367    0.0000
  259. '
  260. 'Using CopyMemory optimized sort algorythm on already sorted string array
  261. '------------------------------------------------AVG-------%-------------
  262. 'BubbleSort   24.1941   24.1231   24.1744        24.1639   -32731
  263. 'ShellSort    0.1215    0.1100    0.1188         0.1167    -58.56
  264. 'QuickSort    0.0892    0.1011    0.1000         0.0968    -31.15
  265. 'TriQuickSort 0.0796    0.0709    0.0702         0.0736    0.0000
  266. '
  267. 'Using CopyMemory optimized sort algorythm on nearly-sorted string array
  268. '------------------------------------------------AVG-------%------------
  269. 'After sorting, we do this (below), then we benchmark the following sort.
  270. '   For i = 0 To n - 1 Step 3
  271. '      SwapStrings sArray(i), sArray(i + 1)
  272. '   Next i
  273. '
  274. 'BubbleSort   24.1350   24.1254   24.1764        24.1456   -27911
  275. 'ShellSort    0.1328    0.1218    0.1187         0.1244    -44.32     ' notice that ShellSort beats QuickSort here in some cases.
  276. 'QuickSort    0.1228    0.1194    0.1181         0.1201    -39.33
  277. 'TriQuickSort 0.0796    0.0795    0.0994         0.0862    0.0000
  278. '
  279. '
  280. 'RESULTS:
  281. '--------
  282. '
  283. 'ALGORYTHM------% SLOWER--
  284. '-------------------------
  285. 'BubbleSort     -40884
  286. 'ShellSort      -93.75
  287. 'QuickSort      -29.52
  288. 'TriQuickSort   0.0000
  289.  
  290.  
  291. Option Explicit
  292.  
  293. ' CopyMemory, my best friend ;)
  294. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef lpDest As Any, ByRef lpSource As Any, ByVal iLen As Long)
  295. Private Const ERROR_NOT_FOUND As Long = &H80000000 ' DO NOT CHANGE, for internal usage only !
  296.  
  297. Public Enum SortOrder
  298.    SortAscending = 0
  299.    SortDescending = 1
  300. End Enum
  301.  
  302. Public Enum RemoveFrom
  303.    RemoveArray = 0
  304.    RemoveIndex = 1
  305. End Enum
  306.  
  307. #Const mdlArray_Loaded = True ' DO NOT EDIT !!!
  308. #Const mdlMarton_Loadable = True
  309.  
  310.  
  311.  
  312. ' /////////
  313. ' // Add //
  314. ' /////////
  315.  
  316.  
  317. Public Sub AddToAnyArray(ByRef vArray As Variant, ByVal vToAdd As Variant, Optional ByVal iPos As Long = -1)
  318.    Dim i       As Long
  319.    Dim iUBound As Long
  320.    
  321.    If Not IsArray(vArray) Then Exit Sub
  322.    
  323.    iUBound = UBound(vArray)
  324.    
  325.    If iUBound = -1 Then vArray = Array(vToAdd): Exit Sub
  326.    
  327.    ' if invalid iPos
  328.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound + 1    ' +1 because we can add array past it's end
  329.    If iPos < 0 Then iPos = 0
  330.    
  331.    iUBound = iUBound + 1
  332.    ReDim Preserve vArray(iUBound)
  333.    
  334.    For i = iUBound To iPos + 1 Step -1
  335.       vArray(i) = vArray(i - 1)
  336.    Next i
  337.    
  338.    vArray(iPos) = vToAdd
  339. End Sub
  340.  
  341. Public Sub AddToLongArray(ByRef iArray() As Long, ByVal iToAdd As Long, Optional ByVal iPos As Long = -1)
  342.    Dim iUBound As Long
  343.    
  344.    iUBound = UBound(iArray)
  345.    
  346.    If iUBound = -1 Then
  347.       ReDim iArray(0)
  348.       iArray(0) = iToAdd
  349.       Exit Sub
  350.    End If
  351.    
  352.    ' if adding at the end
  353.    If (iPos > iUBound) Or (iPos = -1) Then
  354.       ReDim Preserve iArray(iUBound + 1)
  355.       iArray(iUBound + 1) = iToAdd
  356.       Exit Sub
  357.    End If
  358.    
  359.    If iPos < 0 Then iPos = 0
  360.    
  361.    iUBound = iUBound + 1
  362.    ReDim Preserve iArray(iUBound)
  363.    
  364.    CopyMemory iArray(iPos + 1), iArray(iPos), (iUBound - LBound(iArray) - iPos) * Len(iArray(iPos))
  365.    iArray(iPos) = iToAdd
  366. End Sub
  367.  
  368. Public Sub AddToStringArray(ByRef sArray() As String, ByVal sStringToAdd As String, Optional ByVal iPos As Long = -1)
  369.    Dim iUBound As Long
  370.    Dim iTemp   As Long
  371.    
  372.    iUBound = UBound(sArray)
  373.  
  374.    If iUBound = -1 Then
  375.       ReDim sArray(0)
  376.       sArray(0) = sStringToAdd
  377.       Exit Sub
  378.    End If
  379.    
  380.    ' if adding at the end
  381.    If (iPos > iUBound) Or (iPos = -1) Then
  382.       ReDim Preserve sArray(iUBound + 1)
  383.       sArray(iUBound + 1) = sStringToAdd
  384.       Exit Sub
  385.    End If
  386.    
  387.    If iPos < 0 Then iPos = 0
  388.    
  389.    iUBound = iUBound + 1
  390.    ReDim Preserve sArray(iUBound)
  391.    
  392.    CopyMemory ByVal VarPtr(sArray(iPos + 1)), ByVal VarPtr(sArray(iPos)), (iUBound - iPos) * 4
  393.    
  394.    iTemp = 0 ' view this as String(4, Chr(0)) or a NULL value
  395.    CopyMemory ByVal VarPtr(sArray(iPos)), iTemp, 4
  396.    
  397.    sArray(iPos) = sStringToAdd
  398. End Sub
  399.  
  400. Public Sub AddToSortedAnyArray(ByRef vArray As Variant, ByVal vToAdd As Variant)
  401.    Dim iLBound As Long
  402.    Dim iUBound As Long
  403.    Dim iMiddle As Long
  404.    Dim i       As Long
  405.    
  406.    If Not IsArray(vArray) Then Exit Sub
  407.    
  408.    iLBound = LBound(vArray)
  409.    iUBound = UBound(vArray)
  410.  
  411.    ' first, we check the bounds
  412.    If vToAdd <= vArray(iLBound) Then AddToAnyArray vArray, vToAdd, iLBound: Exit Sub
  413.    If vToAdd >= vArray(iUBound) Then AddToAnyArray vArray, vToAdd, iUBound + 1: Exit Sub
  414.  
  415.    Do
  416.       iMiddle = (iLBound + iUBound) \ 2
  417.       
  418.       If vArray(iMiddle) = vToAdd Then
  419.          Exit Do
  420.       ElseIf vArray(iMiddle) < vToAdd Then
  421.          iLBound = iMiddle + 1
  422.       Else
  423.          iUBound = iMiddle - 1
  424.       End If
  425.    Loop Until iLBound > iUBound
  426.    
  427.    iLBound = LBound(vArray)
  428.    iUBound = UBound(vArray)
  429.    
  430.    For i = iMiddle To iLBound Step -1
  431.       If vArray(i) <= vToAdd Then Exit For
  432.    Next i
  433.    
  434.    If vArray(i) = vToAdd Then AddToAnyArray vArray, vToAdd, i: Exit Sub
  435.    
  436.    For i = i + 1 To iUBound
  437.       If vArray(i) >= vToAdd Then AddToAnyArray vArray, vToAdd, i: Exit Sub
  438.    Next i
  439. End Sub
  440.  
  441. Public Sub AddToSortedLongArray(ByRef iArray() As Long, ByVal iToAdd As Long)
  442.    Dim iLBound As Long
  443.    Dim iUBound As Long
  444.    Dim iMiddle As Long
  445.    Dim i       As Long
  446.    
  447.    iLBound = LBound(iArray)
  448.    iUBound = UBound(iArray)
  449.  
  450.    ' first, we check the bounds
  451.    If iToAdd <= iArray(iLBound) Then AddToLongArray iArray, iToAdd, iLBound: Exit Sub
  452.    If iToAdd >= iArray(iUBound) Then AddToLongArray iArray, iToAdd, iUBound + 1: Exit Sub
  453.  
  454.    Do
  455.       iMiddle = (iLBound + iUBound) \ 2
  456.       
  457.       If iArray(iMiddle) = iToAdd Then
  458.          Exit Do
  459.       ElseIf iArray(iMiddle) < iToAdd Then
  460.          iLBound = iMiddle + 1
  461.       Else
  462.          iUBound = iMiddle - 1
  463.       End If
  464.    Loop Until iLBound > iUBound
  465.    
  466.    iLBound = LBound(iArray)
  467.    iUBound = UBound(iArray)
  468.       
  469.    For i = iMiddle To iLBound Step -1
  470.       If iArray(i) <= iToAdd Then Exit For
  471.    Next i
  472.    
  473.    If iArray(i) = iToAdd Then AddToLongArray iArray, iToAdd, i: Exit Sub
  474.    
  475.    For i = i + 1 To iUBound
  476.       If iArray(i) >= iToAdd Then AddToLongArray iArray, iToAdd, i: Exit Sub
  477.    Next i
  478. End Sub
  479.  
  480. Public Sub AddToSortedStringArray(ByRef sArray() As String, ByVal sToAdd As String)
  481.    Dim iLBound As Long
  482.    Dim iUBound As Long
  483.    Dim iMiddle As Long
  484.    Dim i       As Long
  485.    
  486.    iLBound = LBound(sArray)
  487.    iUBound = UBound(sArray)
  488.  
  489.    ' first, we check the bounds
  490.    If sToAdd <= sArray(iLBound) Then AddToStringArray sArray, sToAdd, iLBound: Exit Sub
  491.    If sToAdd >= sArray(iUBound) Then AddToStringArray sArray, sToAdd, iUBound + 1: Exit Sub
  492.  
  493.    Do
  494.       iMiddle = (iLBound + iUBound) \ 2
  495.       
  496.       If sArray(iMiddle) = sToAdd Then
  497.          Exit Do
  498.       ElseIf sArray(iMiddle) < sToAdd Then
  499.          iLBound = iMiddle + 1
  500.       Else
  501.          iUBound = iMiddle - 1
  502.       End If
  503.    Loop Until iLBound > iUBound
  504.    
  505.    iLBound = LBound(sArray)
  506.    iUBound = UBound(sArray)
  507.       
  508.    For i = iMiddle To iLBound Step -1
  509.       If sArray(i) <= sToAdd Then Exit For
  510.    Next i
  511.    
  512.    If sArray(i) = sToAdd Then AddToStringArray sArray, sToAdd, i: Exit Sub
  513.    
  514.    For i = i + 1 To iUBound
  515.       If sArray(i) >= sToAdd Then AddToStringArray sArray, sToAdd, i: Exit Sub
  516.    Next i
  517. End Sub
  518.  
  519. Public Sub AddToIndexedAnyArray(ByRef vArray As Variant, ByRef iIndexArray() As Long, ByVal vToAdd As Variant)
  520.    Dim iLBound As Long
  521.    Dim iUBound As Long
  522.    Dim iMiddle As Long
  523.    Dim i       As Long
  524.    
  525.    If Not IsArray(vArray) Then Exit Sub
  526.    
  527.    AddToAnyArray vArray, vToAdd  ' this adds at the end
  528.    
  529.    iLBound = LBound(vArray)
  530.    iUBound = UBound(vArray)
  531.  
  532.    ' first, we check the bounds
  533.    If vToAdd <= vArray(iIndexArray(iLBound)) Then AddToLongArray iIndexArray, iUBound, iLBound: Exit Sub
  534.    If vToAdd >= vArray(iIndexArray(iUBound - 1)) Then AddToLongArray iIndexArray, iUBound: Exit Sub
  535.  
  536.    Do
  537.       iMiddle = (iLBound + iUBound) \ 2
  538.       
  539.       If vArray(iIndexArray(iMiddle)) = vToAdd Then
  540.          Exit Do
  541.       ElseIf vArray(iIndexArray(iMiddle)) < vToAdd Then
  542.          iLBound = iMiddle + 1
  543.       Else
  544.          iUBound = iMiddle - 1
  545.       End If
  546.    Loop Until iLBound > iUBound
  547.    
  548.    iLBound = LBound(vArray)
  549.    iUBound = UBound(vArray)
  550.    
  551.    For i = iMiddle To iLBound Step -1
  552.       If vArray(iIndexArray(i)) <= vToAdd Then Exit For
  553.    Next i
  554.    
  555.    For i = i To iUBound
  556.       If vArray(iIndexArray(i)) >= vToAdd Then AddToLongArray iIndexArray, iUBound, i: Exit Sub
  557.    Next i
  558. End Sub
  559.  
  560. Public Sub AddToIndexedLongArray(ByRef iArray() As Long, ByRef iIndexArray() As Long, ByVal iToAdd As Long)
  561.    Dim iLBound As Long
  562.    Dim iUBound As Long
  563.    Dim iMiddle As Long
  564.    Dim i       As Long
  565.    
  566.    AddToLongArray iArray, iToAdd  ' this adds at the end
  567.    
  568.    iLBound = LBound(iArray)
  569.    iUBound = UBound(iArray)
  570.  
  571.    ' first, we check the bounds
  572.    If iToAdd <= iArray(iIndexArray(iLBound)) Then AddToLongArray iIndexArray, iUBound, iLBound: Exit Sub
  573.    If iToAdd >= iArray(iIndexArray(iUBound - 1)) Then AddToLongArray iIndexArray, iUBound: Exit Sub
  574.  
  575.    Do
  576.       iMiddle = (iLBound + iUBound) \ 2
  577.       
  578.       If iArray(iIndexArray(iMiddle)) = iToAdd Then
  579.          Exit Do
  580.       ElseIf iArray(iIndexArray(iMiddle)) < iToAdd Then
  581.          iLBound = iMiddle + 1
  582.       Else
  583.          iUBound = iMiddle - 1
  584.       End If
  585.    Loop Until iLBound > iUBound
  586.    
  587.    iLBound = LBound(iArray)
  588.    iUBound = UBound(iArray)
  589.    
  590.    For i = iMiddle To iLBound Step -1
  591.       If iArray(iIndexArray(i)) <= iToAdd Then Exit For
  592.    Next i
  593.    
  594.    For i = i To iUBound
  595.       If iArray(iIndexArray(i)) >= iToAdd Then AddToLongArray iIndexArray, iUBound, i: Exit Sub
  596.    Next i
  597. End Sub
  598.  
  599. Public Sub AddToIndexedStringArray(ByRef sArray() As String, ByRef iIndexArray() As Long, ByVal sToAdd As String)
  600.    Dim iLBound As Long
  601.    Dim iUBound As Long
  602.    Dim iMiddle As Long
  603.    Dim i       As Long
  604.    
  605.    AddToStringArray sArray, sToAdd  ' this adds at the end
  606.    
  607.    iLBound = LBound(sArray)
  608.    iUBound = UBound(sArray)
  609.  
  610.    ' first, we check the bounds
  611.    If sToAdd <= sArray(iIndexArray(iLBound)) Then AddToLongArray iIndexArray, iUBound, iLBound: Exit Sub
  612.    If sToAdd >= sArray(iIndexArray(iUBound - 1)) Then AddToLongArray iIndexArray, iUBound: Exit Sub
  613.  
  614.    Do
  615.       iMiddle = (iLBound + iUBound) \ 2
  616.       
  617.       If sArray(iIndexArray(iMiddle)) = sToAdd Then
  618.          Exit Do
  619.       ElseIf sArray(iIndexArray(iMiddle)) < sToAdd Then
  620.          iLBound = iMiddle + 1
  621.       Else
  622.          iUBound = iMiddle - 1
  623.       End If
  624.    Loop Until iLBound > iUBound
  625.    
  626.    iLBound = LBound(sArray)
  627.    iUBound = UBound(sArray)
  628.    
  629.    For i = iMiddle To iLBound Step -1
  630.       If sArray(iIndexArray(i)) <= sToAdd Then Exit For
  631.    Next i
  632.    
  633.    For i = i To iUBound
  634.       If sArray(iIndexArray(i)) >= sToAdd Then AddToLongArray iIndexArray, iUBound, i: Exit Sub
  635.    Next i
  636. End Sub
  637.  
  638.  
  639. ' ////////////
  640. ' // Remove //
  641. ' ////////////
  642.  
  643.  
  644. Public Sub RemoveFromAnyArray(ByRef vArray As Variant, Optional ByVal iPos As Long = -1)
  645.    Dim i       As Long
  646.    Dim iLBound As Long
  647.    Dim iUBound As Long
  648.    
  649.    If Not IsArray(vArray) Then Exit Sub
  650.    
  651.    iLBound = LBound(vArray)
  652.    iUBound = UBound(vArray)
  653.  
  654.    ' if we only have one element in array
  655.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase vArray: Exit Sub
  656.  
  657.    ' if invalid iPos
  658.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  659.    If iPos < iLBound Then iPos = iLBound
  660.    If iPos = iUBound Then ReDim Preserve vArray(iUBound - 1): Exit Sub
  661.    
  662.    For i = iPos + 1 To iUBound
  663.       vArray(i - 1) = vArray(i)
  664.    Next i
  665.    
  666.    ReDim Preserve vArray(iUBound - 1)
  667. End Sub
  668.  
  669. Public Sub RemoveFromLongArray(ByRef iArray() As Long, Optional ByVal iPos As Long = -1)
  670.    Dim iLBound As Long
  671.    Dim iUBound As Long
  672.    
  673.    iLBound = LBound(iArray)
  674.    iUBound = UBound(iArray)
  675.  
  676.    ' if we only have one element in array
  677.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase iArray: Exit Sub
  678.  
  679.    ' if invalid iPos
  680.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  681.    If iPos < iLBound Then iPos = iLBound
  682.    If iPos = iUBound Then ReDim Preserve iArray(iUBound - 1): Exit Sub
  683.    
  684.    CopyMemory iArray(iPos), iArray(iPos + 1), (iUBound - iLBound - iPos) * Len(iArray(iPos))
  685.    
  686.    ReDim Preserve iArray(iUBound - 1)
  687. End Sub
  688.  
  689. Public Sub RemoveFromStringArray(ByRef sArray() As String, Optional ByVal iPos As Long = -1)
  690.    Dim iLBound As Long
  691.    Dim iUBound As Long
  692.    Dim iTemp   As Long
  693.    
  694.    iLBound = LBound(sArray)
  695.    iUBound = UBound(sArray)
  696.  
  697.    ' if we only have one element in array
  698.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase sArray: Exit Sub
  699.  
  700.    ' if invalid iPos
  701.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  702.    If iPos < iLBound Then iPos = iLBound
  703.    If iPos = iUBound Then ReDim Preserve sArray(iUBound - 1): Exit Sub
  704.    
  705.    iTemp = StrPtr(sArray(iPos))
  706.    
  707.    CopyMemory ByVal VarPtr(sArray(iPos)), ByVal VarPtr(sArray(iPos + 1)), (iUBound - iPos) * 4
  708.    
  709.    ' we do this to have VB unalloc the string to evade memory leaks
  710.    CopyMemory ByVal VarPtr(sArray(iUBound)), iTemp, 4
  711.    
  712.    ReDim Preserve sArray(iUBound - 1)
  713. End Sub
  714.  
  715. Public Sub RemoveFromIndexedAnyArray(ByRef vArray As Variant, ByRef iIndexArray() As Long, Optional ByVal iPos As Long = -1, Optional ByVal RemoveFrom As RemoveFrom = RemoveIndex)
  716.    Dim i       As Long
  717.    Dim iLBound As Long
  718.    Dim iUBound As Long
  719.    Dim iTemp   As Long
  720.    Dim iPos2   As Long
  721.    
  722.    If Not IsArray(vArray) Then Exit Sub
  723.    
  724.    iLBound = LBound(vArray)
  725.    iUBound = UBound(vArray)
  726.  
  727.    ' if we only have one element in array
  728.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase vArray: Erase iIndexArray: Exit Sub
  729.  
  730.    ' if invalid iPos
  731.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  732.    If iPos < iLBound Then iPos = iLBound
  733.    
  734.    iTemp = IIf(RemoveFrom = RemoveArray, iPos, iIndexArray(iPos))
  735.    iPos2 = 0
  736.       
  737.    For i = iLBound To iUBound
  738.       If iIndexArray(i) > iTemp Then
  739.          iIndexArray(i) = iIndexArray(i) - 1
  740.       ElseIf iIndexArray(i) = iTemp Then
  741.          iPos2 = i
  742.       End If
  743.    Next i
  744.    
  745.    RemoveFromAnyArray vArray, iTemp
  746.    RemoveFromLongArray iIndexArray, IIf(RemoveFrom = RemoveArray, iPos2, iPos)
  747. End Sub
  748.  
  749. Public Sub RemoveFromIndexedLongArray(ByRef iArray() As Long, ByRef iIndexArray() As Long, Optional ByVal iPos As Long = -1, Optional ByVal RemoveFrom As RemoveFrom = RemoveIndex)
  750.    Dim i       As Long
  751.    Dim iLBound As Long
  752.    Dim iUBound As Long
  753.    Dim iTemp   As Long
  754.    Dim iPos2   As Long
  755.    
  756.    iLBound = LBound(iArray)
  757.    iUBound = UBound(iArray)
  758.  
  759.    ' if we only have one element in array
  760.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase iArray: Erase iIndexArray: Exit Sub
  761.  
  762.    ' if invalid iPos
  763.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  764.    If iPos < iLBound Then iPos = iLBound
  765.    
  766.    iTemp = IIf(RemoveFrom = RemoveArray, iPos, iIndexArray(iPos))
  767.    iPos2 = 0
  768.       
  769.    For i = iLBound To iUBound
  770.       If iIndexArray(i) > iTemp Then
  771.          iIndexArray(i) = iIndexArray(i) - 1
  772.       ElseIf iIndexArray(i) = iTemp Then
  773.          iPos2 = i
  774.       End If
  775.    Next i
  776.    
  777.    RemoveFromLongArray iArray, iTemp
  778.    RemoveFromLongArray iIndexArray, IIf(RemoveFrom = RemoveArray, iPos2, iPos)
  779. End Sub
  780.  
  781. Public Sub RemoveFromIndexedStringArray(ByRef sArray() As String, ByRef iIndexArray() As Long, Optional ByVal iPos As Long = -1, Optional ByVal RemoveFrom As RemoveFrom = RemoveIndex)
  782.    Dim i       As Long
  783.    Dim iLBound As Long
  784.    Dim iUBound As Long
  785.    Dim iTemp   As Long
  786.    Dim iPos2   As Long
  787.    
  788.    iLBound = LBound(sArray)
  789.    iUBound = UBound(sArray)
  790.  
  791.    ' if we only have one element in array
  792.    If (iUBound = -1) Or (iUBound - iLBound = 0) Then Erase sArray: Erase iIndexArray: Exit Sub
  793.  
  794.    ' if invalid iPos
  795.    If (iPos > iUBound) Or (iPos = -1) Then iPos = iUBound
  796.    If iPos < iLBound Then iPos = iLBound
  797.    
  798.    iTemp = IIf(RemoveFrom = RemoveArray, iPos, iIndexArray(iPos))
  799.    iPos2 = 0
  800.       
  801.    For i = iLBound To iUBound
  802.       If iIndexArray(i) > iTemp Then
  803.          iIndexArray(i) = iIndexArray(i) - 1
  804.       ElseIf iIndexArray(i) = iTemp Then
  805.          iPos2 = i
  806.       End If
  807.    Next i
  808.    
  809.    RemoveFromStringArray sArray, iTemp
  810.    RemoveFromLongArray iIndexArray, IIf(RemoveFrom = RemoveArray, iPos2, iPos)
  811. End Sub
  812.  
  813.  
  814. ' //////////
  815. ' // Hash //
  816. ' //////////
  817.  
  818.  
  819. Public Sub BuildHashTable(ByRef sArray() As String, ByRef iHashArray() As Long)
  820.    Dim i        As Long ' Loop Counter
  821.    Dim iLBound  As Long
  822.    Dim iUBound  As Long
  823.    Dim iUBound2 As Long
  824.    Dim iMax     As Long
  825.    Dim iIndex   As Long
  826.  
  827.    iLBound = LBound(sArray)
  828.    iUBound = UBound(sArray)
  829.    
  830.    iMax = (iUBound + 1) * 4
  831.  
  832.    ReDim iHashArray(0 To iMax - 1) As Long
  833.    iUBound2 = UBound(iHashArray)
  834.    
  835.    For i = LBound(iHashArray) To iUBound2
  836.       iHashArray(i) = ERROR_NOT_FOUND
  837.    Next
  838.  
  839.    For i = iLBound To iUBound
  840.       iIndex = GetFastXorHash(sArray(i)) Mod iMax
  841.       
  842.       Do Until iHashArray(iIndex) = ERROR_NOT_FOUND ' remember the hash array is 4 time bigger than the string array, thus this CANNOT be an infinite loop
  843.          iIndex = (iIndex + 1) Mod iMax
  844.       Loop
  845.       
  846.       iHashArray(iIndex) = i
  847.    Next i
  848. End Sub
  849.  
  850. Public Function HashSearch(ByRef sArray() As String, ByRef iHashArray() As Long, ByVal sFind As String) As Long
  851.    Dim i           As Long
  852.    Dim iMax        As Long
  853.    Dim bInitialize As Boolean
  854.  
  855.    ' create the hash array if necessary
  856.    bInitialize = False
  857.    If UBound(iHashArray) = -1 Then bInitialize = True Else If iHashArray(LBound(iHashArray)) = iHashArray(UBound(iHashArray)) Then bInitialize = True
  858.    If bInitialize = True Then BuildHashTable sArray, iHashArray
  859.    
  860.    iMax = UBound(iHashArray) + 1
  861.  
  862.    i = GetFastXorHash(sFind) Mod iMax
  863.  
  864.    Do Until iHashArray(i) = ERROR_NOT_FOUND
  865.       If sArray(iHashArray(i)) = sFind Then HashSearch = iHashArray(i): Exit Function
  866.       
  867.       i = (i + 1) Mod iMax
  868.    Loop
  869.    
  870.    HashSearch = -1
  871. End Function
  872.  
  873.  
  874. ' ////////////
  875. ' // Search //
  876. ' ////////////
  877.  
  878.  
  879. Public Function BinarySearchAny(ByRef vArray As Variant, ByVal vFind As Variant) As Long
  880.    Dim iLBound As Long
  881.    Dim iUBound As Long
  882.    Dim iMiddle As Long
  883.  
  884.    If Not IsArray(vArray) Then Exit Function
  885.  
  886.    iLBound = LBound(vArray)
  887.    iUBound = UBound(vArray)
  888.  
  889.    Do
  890.       iMiddle = (iLBound + iUBound) \ 2
  891.       
  892.       If vArray(iMiddle) = vFind Then
  893.          BinarySearchAny = iMiddle
  894.          Exit Function
  895.       ElseIf vArray(iMiddle) < vFind Then
  896.          iLBound = iMiddle + 1
  897.       Else
  898.          iUBound = iMiddle - 1
  899.       End If
  900.    Loop Until iLBound > iUBound
  901.    
  902.    BinarySearchAny = -1
  903. End Function
  904.  
  905. Public Function BinarySearchLong(ByRef iArray() As Long, ByVal iFind As Long) As Long
  906.    Dim iLBound As Long
  907.    Dim iUBound As Long
  908.    Dim iMiddle As Long
  909.  
  910.    iLBound = LBound(iArray)
  911.    iUBound = UBound(iArray)
  912.  
  913.    Do
  914.       iMiddle = (iLBound + iUBound) \ 2
  915.       
  916.       If iArray(iMiddle) = iFind Then
  917.          BinarySearchLong = iMiddle
  918.          Exit Function
  919.       ElseIf iArray(iMiddle) < iFind Then
  920.          iLBound = iMiddle + 1
  921.       Else
  922.          iUBound = iMiddle - 1
  923.       End If
  924.    Loop Until iLBound > iUBound
  925.    
  926.    BinarySearchLong = -1
  927. End Function
  928.  
  929. Public Function BinarySearchString(ByRef sArray() As String, ByVal sFind As String) As Long
  930.    Dim iLBound As Long
  931.    Dim iUBound As Long
  932.    Dim iMiddle As Long
  933.  
  934.    iLBound = LBound(sArray)
  935.    iUBound = UBound(sArray)
  936.  
  937.    Do
  938.       iMiddle = (iLBound + iUBound) \ 2
  939.       
  940.       If sArray(iMiddle) = sFind Then
  941.          BinarySearchString = iMiddle
  942.          Exit Function
  943.       ElseIf sArray(iMiddle) < sFind Then
  944.          iLBound = iMiddle + 1
  945.       Else
  946.          iUBound = iMiddle - 1
  947.       End If
  948.    Loop Until iLBound > iUBound
  949.    
  950.    BinarySearchString = -1
  951. End Function
  952.  
  953. Public Function IndexedBinarySearchAny(ByRef vArray As Variant, ByRef iIndexArray() As Long, ByVal vFind As Variant) As Long
  954.    Dim iLBound     As Long
  955.    Dim iUBound     As Long
  956.    Dim iMiddle     As Long
  957.    Dim bInitialize As Boolean
  958.  
  959.    If Not IsArray(vArray) Then Exit Function
  960.  
  961.    iLBound = LBound(vArray)
  962.    iUBound = UBound(vArray)
  963.    
  964.    'initialize the index array if necessary
  965.    bInitialize = False
  966.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  967.    If bInitialize = True Then CreateIndex iIndexArray, vArray
  968.    
  969.    Do
  970.       iMiddle = (iLBound + iUBound) \ 2
  971.       
  972.       If vArray(iIndexArray(iMiddle)) = vFind Then
  973.          IndexedBinarySearchAny = iIndexArray(iMiddle)
  974.          Exit Function
  975.       ElseIf vArray(iIndexArray(iMiddle)) < vFind Then
  976.          iLBound = iMiddle + 1
  977.       Else
  978.          iUBound = iMiddle - 1
  979.       End If
  980.    Loop Until iLBound > iUBound
  981.    
  982.    IndexedBinarySearchAny = -1
  983. End Function
  984.  
  985. Public Function IndexedBinarySearchLong(ByRef iArray() As Long, ByRef iIndexArray() As Long, ByVal iFind As Long) As Long
  986.    Dim iLBound     As Long
  987.    Dim iUBound     As Long
  988.    Dim iMiddle     As Long
  989.    Dim bInitialize As Boolean
  990.  
  991.    iLBound = LBound(iArray)
  992.    iUBound = UBound(iArray)
  993.    
  994.    'initialize the index array if necessary
  995.    bInitialize = False
  996.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  997.    If bInitialize = True Then CreateIndex iIndexArray, iArray
  998.  
  999.    Do
  1000.       iMiddle = (iLBound + iUBound) \ 2
  1001.       
  1002.       If iArray(iIndexArray(iMiddle)) = iFind Then
  1003.          IndexedBinarySearchLong = iIndexArray(iMiddle)
  1004.          Exit Function
  1005.       ElseIf iArray(iIndexArray(iMiddle)) < iFind Then
  1006.          iLBound = iMiddle + 1
  1007.       Else
  1008.          iUBound = iMiddle - 1
  1009.       End If
  1010.    Loop Until iLBound > iUBound
  1011.    
  1012.    IndexedBinarySearchLong = -1
  1013. End Function
  1014.  
  1015. Public Function IndexedBinarySearchString(ByRef sArray() As String, ByRef iIndexArray() As Long, ByVal sFind As String) As Long
  1016.    Dim iLBound     As Long
  1017.    Dim iUBound     As Long
  1018.    Dim iMiddle     As Long
  1019.    Dim bInitialize As Boolean
  1020.  
  1021.    iLBound = LBound(sArray)
  1022.    iUBound = UBound(sArray)
  1023.    
  1024.    'initialize the index array if necessary
  1025.    bInitialize = False
  1026.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1027.    If bInitialize = True Then CreateIndex iIndexArray, sArray
  1028.  
  1029.    Do
  1030.       iMiddle = (iLBound + iUBound) \ 2
  1031.       
  1032.       If sArray(iIndexArray(iMiddle)) = sFind Then
  1033.          IndexedBinarySearchString = iIndexArray(iMiddle)
  1034.          Exit Function
  1035.       ElseIf sArray(iIndexArray(iMiddle)) < sFind Then
  1036.          iLBound = iMiddle + 1
  1037.       Else
  1038.          iUBound = iMiddle - 1
  1039.       End If
  1040.    Loop Until iLBound > iUBound
  1041.    
  1042.    IndexedBinarySearchString = -1
  1043. End Function
  1044.  
  1045. Public Function SequentialSearchAnyArray(ByRef vArray As Variant, ByVal vFind As Variant) As Long
  1046.    Dim i       As Long
  1047.    Dim iLBound As Long
  1048.    Dim iUBound As Long
  1049.  
  1050.    If Not IsArray(vArray) Then Exit Function
  1051.  
  1052.    iLBound = LBound(vArray)
  1053.    iUBound = UBound(vArray)
  1054.  
  1055.    For i = iLBound To iUBound
  1056.       If vArray(i) = vFind Then SequentialSearchAnyArray = i: Exit Function
  1057.    Next i
  1058.  
  1059.    SequentialSearchAnyArray = -1
  1060. End Function
  1061.  
  1062. Public Function SequentialSearchLongArray(ByRef iArray() As Long, ByVal iFind As Long) As Long
  1063.    Dim i       As Long
  1064.    Dim iLBound As Long
  1065.    Dim iUBound As Long
  1066.  
  1067.    iLBound = LBound(iArray)
  1068.    iUBound = UBound(iArray)
  1069.  
  1070.    For i = iLBound To iUBound
  1071.       If iArray(i) = iFind Then SequentialSearchLongArray = i: Exit Function
  1072.    Next i
  1073.  
  1074.    SequentialSearchLongArray = -1
  1075. End Function
  1076.  
  1077. Public Function SequentialSearchStringArray(ByRef sArray() As String, ByVal sFind As String) As Long
  1078.    Dim i       As Long
  1079.    Dim iLBound As Long
  1080.    Dim iUBound As Long
  1081.  
  1082.    iLBound = LBound(sArray)
  1083.    iUBound = UBound(sArray)
  1084.  
  1085.    For i = iLBound To iUBound
  1086.       If sArray(i) = sFind Then SequentialSearchStringArray = i: Exit Function
  1087.    Next i
  1088.  
  1089.    SequentialSearchStringArray = -1
  1090. End Function
  1091.  
  1092. Public Function isInAnyArray(ByRef vArray As Variant, ByVal vFind As Variant) As Boolean
  1093.    If Not IsArray(vArray) Then isInAnyArray = False: Exit Function
  1094.    isInAnyArray = IIf(SequentialSearchAnyArray(vArray, vFind) = -1, False, True)
  1095. End Function
  1096.  
  1097. Public Function isInLongArray(ByRef iArray() As Long, ByVal iFind As Long) As Boolean
  1098.    isInLongArray = IIf(SequentialSearchLongArray(iArray, iFind) = -1, False, True)
  1099. End Function
  1100.  
  1101. Public Function isInStringArray(ByRef sArray() As String, ByVal sFind As String) As Boolean
  1102.    isInStringArray = IIf(SequentialSearchStringArray(sArray, sFind) = -1, False, True)
  1103. End Function
  1104.  
  1105.  
  1106. ' //////////
  1107. ' // Sort //
  1108. ' //////////
  1109.  
  1110.  
  1111. Public Sub ShellSortAny(ByRef vArray As Variant, Optional ByVal SortOrder As SortOrder = SortAscending)
  1112.    Dim i          As Long   ' Loop Counter
  1113.    Dim j          As Long
  1114.    Dim iLBound    As Long
  1115.    Dim iUBound    As Long
  1116.    Dim iMax       As Long
  1117.    Dim vTemp      As Variant
  1118.    Dim distance   As Long
  1119.    Dim bSortOrder As Boolean
  1120.    
  1121.    If Not IsArray(vArray) Then Exit Sub
  1122.    
  1123.    iLBound = LBound(vArray)
  1124.    iUBound = UBound(vArray)
  1125.  
  1126.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1127.    iMax = iUBound - iLBound + 1
  1128.    
  1129.    Do
  1130.       distance = distance * 3 + 1
  1131.    Loop Until distance > iMax
  1132.  
  1133.    Do
  1134.       distance = distance \ 3
  1135.       For i = distance + iLBound To iUBound
  1136.          vTemp = vArray(i)
  1137.          j = i
  1138.          Do While (vArray(j - distance) > vTemp) Xor bSortOrder
  1139.             vArray(j) = vArray(j - distance)
  1140.             j = j - distance
  1141.             If j - distance < iLBound Then Exit Do
  1142.          Loop
  1143.          vArray(j) = vTemp
  1144.       Next i
  1145.    Loop Until distance = 1
  1146. End Sub
  1147.  
  1148. Public Sub ShellSortLong(ByRef iArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1149.    Dim i          As Long   ' Loop Counter
  1150.    Dim j          As Long
  1151.    Dim iLBound    As Long
  1152.    Dim iUBound    As Long
  1153.    Dim iMax       As Long
  1154.    Dim iTemp      As Long
  1155.    Dim distance   As Long
  1156.    Dim bSortOrder As Boolean
  1157.    
  1158.    iLBound = LBound(iArray)
  1159.    iUBound = UBound(iArray)
  1160.  
  1161.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1162.    iMax = iUBound - iLBound + 1
  1163.    
  1164.    Do
  1165.       distance = distance * 3 + 1
  1166.    Loop Until distance > iMax
  1167.  
  1168.    Do
  1169.       distance = distance \ 3
  1170.       For i = distance + iLBound To iUBound
  1171.          iTemp = iArray(i)
  1172.          j = i
  1173.          Do While (iArray(j - distance) > iTemp) Xor bSortOrder
  1174.             iArray(j) = iArray(j - distance)
  1175.             j = j - distance
  1176.             If j - distance < iLBound Then Exit Do
  1177.          Loop
  1178.          iArray(j) = iTemp
  1179.       Next i
  1180.    Loop Until distance = 1
  1181. End Sub
  1182.  
  1183. Public Sub ShellSortString(ByRef sArray() As String, Optional ByVal SortOrder As SortOrder = SortAscending)
  1184.    Dim i          As Long   ' Loop Counter
  1185.    Dim j          As Long
  1186.    Dim iLBound    As Long
  1187.    Dim iUBound    As Long
  1188.    Dim iMax       As Long
  1189.    Dim sTemp      As String
  1190.    Dim distance   As Long
  1191.    Dim bSortOrder As Boolean
  1192.    
  1193.    iLBound = LBound(sArray)
  1194.    iUBound = UBound(sArray)
  1195.  
  1196.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1197.    iMax = iUBound - iLBound + 1
  1198.    
  1199.    Do
  1200.       distance = distance * 3 + 1
  1201.    Loop Until distance > iMax
  1202.  
  1203.    Do
  1204.       distance = distance \ 3
  1205.       For i = distance + iLBound To iUBound
  1206.          CopyMemory ByVal VarPtr(sTemp), ByVal VarPtr(sArray(i)), 4 'sTemp = sArray(i)
  1207.          j = i
  1208.          Do While (sArray(j - distance) > sTemp) Xor bSortOrder
  1209.             CopyMemory ByVal VarPtr(sArray(j)), ByVal VarPtr(sArray(j - distance)), 4 'sArray(j) = sArray(j - distance)
  1210.             j = j - distance
  1211.             If j - distance < iLBound Then Exit Do
  1212.          Loop
  1213.          CopyMemory ByVal VarPtr(sArray(j)), ByVal VarPtr(sTemp), 4 'sArray(j) = sTemp
  1214.       Next i
  1215.    Loop Until distance = 1
  1216.    
  1217.    ' delete temp var (sTemp)
  1218.    i = 0
  1219.    CopyMemory ByVal VarPtr(sTemp), ByVal VarPtr(i), 4
  1220. End Sub
  1221.  
  1222. Public Sub TriQuickSortAny(ByRef vArray As Variant, Optional ByVal SortOrder As SortOrder = SortAscending)
  1223.    Dim iLBound As Long
  1224.    Dim iUBound As Long
  1225.    Dim i       As Long
  1226.    Dim j       As Long
  1227.    Dim vTemp   As Variant
  1228.    
  1229.    If Not IsArray(vArray) Then Exit Sub
  1230.    
  1231.    iLBound = LBound(vArray)
  1232.    iUBound = UBound(vArray)
  1233.    
  1234.    ' *NOTE*  the value 4 is VERY important here !!!
  1235.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1236.    TriQuickSortAny2 vArray, 4, iLBound, iUBound
  1237.    InsertionSortAny vArray, iLBound, iUBound
  1238.    
  1239.    If SortOrder = SortDescending Then ReverseAnyArray vArray
  1240. End Sub
  1241.  
  1242. Public Sub TriQuickSortLong(ByRef iArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1243.    Dim iLBound As Long
  1244.    Dim iUBound As Long
  1245.    Dim i       As Long
  1246.    Dim j       As Long
  1247.    Dim iTemp   As Long
  1248.    
  1249.    iLBound = LBound(iArray)
  1250.    iUBound = UBound(iArray)
  1251.    
  1252.    ' *NOTE*  the value 4 is VERY important here !!!
  1253.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1254.    TriQuickSortLong2 iArray, 4, iLBound, iUBound
  1255.    InsertionSortLong iArray, iLBound, iUBound
  1256.    
  1257.    If SortOrder = SortDescending Then ReverseLongArray iArray
  1258. End Sub
  1259.  
  1260. Public Sub TriQuickSortString(ByRef sArray() As String, Optional ByVal SortOrder As SortOrder = SortAscending)
  1261.    Dim iLBound As Long
  1262.    Dim iUBound As Long
  1263.    Dim i       As Long
  1264.    Dim j       As Long
  1265.    Dim sTemp   As String
  1266.    
  1267.    iLBound = LBound(sArray)
  1268.    iUBound = UBound(sArray)
  1269.    
  1270.    ' *NOTE*  the value 4 is VERY important here !!!
  1271.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1272.    TriQuickSortString2 sArray, 4, iLBound, iUBound
  1273.    InsertionSortString sArray, iLBound, iUBound
  1274.    
  1275.    If SortOrder = SortDescending Then ReverseStringArray sArray
  1276. End Sub
  1277.  
  1278. Public Sub IndexedShellSortAny(ByRef vArray As Variant, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1279.    Dim i           As Long   ' Loop Counter
  1280.    Dim j           As Long
  1281.    Dim iLBound     As Long
  1282.    Dim iUBound     As Long
  1283.    Dim iMax        As Long
  1284.    Dim vTemp       As Variant
  1285.    Dim iIndexTemp  As Long
  1286.    Dim distance    As Long
  1287.    Dim bInitialize As Boolean
  1288.    Dim bSortOrder  As Boolean
  1289.  
  1290.    If Not IsArray(vArray) Then Exit Sub
  1291.  
  1292.    iLBound = LBound(vArray)
  1293.    iUBound = UBound(vArray)
  1294.  
  1295.    'initialize the index array if necessary
  1296.    bInitialize = False
  1297.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1298.    If bInitialize = True Then CreateIndex iIndexArray, vArray
  1299.  
  1300.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1301.    iMax = iUBound - iLBound + 1
  1302.    
  1303.    Do
  1304.       distance = distance * 3 + 1
  1305.    Loop Until distance > iMax
  1306.  
  1307.    Do
  1308.       distance = distance \ 3
  1309.       For i = distance + iLBound To iUBound
  1310.          iIndexTemp = iIndexArray(i)
  1311.          vTemp = vArray(iIndexTemp)
  1312.          j = i
  1313.          Do While (vArray(iIndexArray(j - distance)) > vTemp) Xor bSortOrder
  1314.             iIndexArray(j) = iIndexArray(j - distance)
  1315.             j = j - distance
  1316.             If j - distance < iLBound Then Exit Do
  1317.          Loop
  1318.          iIndexArray(j) = iIndexTemp
  1319.       Next i
  1320.    Loop Until distance = 1
  1321. End Sub
  1322.  
  1323. Public Sub IndexedShellSortLong(ByRef iArray() As Long, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1324.    Dim i           As Long   ' Loop Counter
  1325.    Dim j           As Long
  1326.    Dim iLBound     As Long
  1327.    Dim iUBound     As Long
  1328.    Dim iMax        As Long
  1329.    Dim iTemp       As Long
  1330.    Dim iIndexTemp  As Long
  1331.    Dim distance    As Long
  1332.    Dim bInitialize As Boolean
  1333.    Dim bSortOrder  As Boolean
  1334.  
  1335.    iLBound = LBound(iArray)
  1336.    iUBound = UBound(iArray)
  1337.  
  1338.    'initialize the index array if necessary
  1339.    bInitialize = False
  1340.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1341.    If bInitialize = True Then CreateIndex iIndexArray, iArray
  1342.  
  1343.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1344.    iMax = iUBound - iLBound + 1
  1345.    
  1346.    Do
  1347.       distance = distance * 3 + 1
  1348.    Loop Until distance > iMax
  1349.  
  1350.    Do
  1351.       distance = distance \ 3
  1352.       For i = distance + iLBound To iUBound
  1353.          iIndexTemp = iIndexArray(i)
  1354.          iTemp = iArray(iIndexTemp)
  1355.          j = i
  1356.          Do While (iArray(iIndexArray(j - distance)) > iTemp) Xor bSortOrder
  1357.             iIndexArray(j) = iIndexArray(j - distance)
  1358.             j = j - distance
  1359.             If j - distance < iLBound Then Exit Do
  1360.          Loop
  1361.          iIndexArray(j) = iIndexTemp
  1362.       Next i
  1363.    Loop Until distance = 1
  1364. End Sub
  1365.  
  1366. Public Sub IndexedShellSortString(ByRef sArray() As String, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1367.    Dim i           As Long   ' Loop Counter
  1368.    Dim j           As Long
  1369.    Dim iLBound     As Long
  1370.    Dim iUBound     As Long
  1371.    Dim iMax        As Long
  1372.    Dim sTemp       As String
  1373.    Dim iIndexTemp  As Long
  1374.    Dim distance    As Long
  1375.    Dim bInitialize As Boolean
  1376.    Dim bSortOrder  As Boolean
  1377.  
  1378.    iLBound = LBound(sArray)
  1379.    iUBound = UBound(sArray)
  1380.  
  1381.    'initialize the index array if necessary
  1382.    bInitialize = False
  1383.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1384.    If bInitialize = True Then CreateIndex iIndexArray, sArray
  1385.  
  1386.    bSortOrder = IIf(SortOrder = SortAscending, False, True)
  1387.    iMax = iUBound - iLBound + 1
  1388.    
  1389.    Do
  1390.       distance = distance * 3 + 1
  1391.    Loop Until distance > iMax
  1392.  
  1393.    Do
  1394.       distance = distance \ 3
  1395.       For i = distance + iLBound To iUBound
  1396.          iIndexTemp = iIndexArray(i)
  1397.          sTemp = sArray(iIndexTemp)
  1398.          j = i
  1399.          Do While (sArray(iIndexArray(j - distance)) > sTemp) Xor bSortOrder
  1400.             iIndexArray(j) = iIndexArray(j - distance)
  1401.             j = j - distance
  1402.             If j - distance < iLBound Then Exit Do
  1403.          Loop
  1404.          iIndexArray(j) = iIndexTemp
  1405.       Next i
  1406.    Loop Until distance = 1
  1407. End Sub
  1408.  
  1409. Public Sub IndexedTriQuickSortAny(ByRef vArray As Variant, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1410.    Dim iLBound     As Long
  1411.    Dim iUBound     As Long
  1412.    Dim i           As Long
  1413.    Dim j           As Long
  1414.    Dim iPos        As Long
  1415.    Dim bInitialize As Boolean
  1416.  
  1417.    If Not IsArray(vArray) Then Exit Sub
  1418.    
  1419.    'initialize the index array if necessary
  1420.    bInitialize = False
  1421.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1422.    If bInitialize = True Then CreateIndex iIndexArray, vArray
  1423.    
  1424.    iLBound = LBound(vArray)
  1425.    iUBound = UBound(vArray)
  1426.    
  1427.    ' *NOTE*  the value 4 is VERY important here !!!
  1428.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1429.    IndexedTriQuickSortAny2 vArray, iIndexArray, 4, iLBound, iUBound
  1430.    IndexedInsertionSortAny vArray, iIndexArray, iLBound, iUBound
  1431.    
  1432.    If SortOrder = SortDescending Then ReverseLongArray iIndexArray
  1433. End Sub
  1434.  
  1435. Public Sub IndexedTriQuickSortLong(ByRef iArray() As Long, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1436.    Dim iLBound     As Long
  1437.    Dim iUBound     As Long
  1438.    Dim i           As Long
  1439.    Dim j           As Long
  1440.    Dim iPos        As Long
  1441.    Dim bInitialize As Boolean
  1442.  
  1443.    If Not IsArray(iArray) Then Exit Sub
  1444.    
  1445.    'initialize the index array if necessary
  1446.    bInitialize = False
  1447.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1448.    If bInitialize = True Then CreateIndex iIndexArray, iArray
  1449.    
  1450.    iLBound = LBound(iArray)
  1451.    iUBound = UBound(iArray)
  1452.    
  1453.    ' *NOTE*  the value 4 is VERY important here !!!
  1454.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1455.    IndexedTriQuickSortLong2 iArray, iIndexArray, 4, iLBound, iUBound
  1456.    IndexedInsertionSortLong iArray, iIndexArray, iLBound, iUBound
  1457.    
  1458.    If SortOrder = SortDescending Then ReverseLongArray iIndexArray
  1459. End Sub
  1460.  
  1461. Public Sub IndexedTriQuickSortString(ByRef sArray() As String, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending)
  1462.    Dim iLBound     As Long
  1463.    Dim iUBound     As Long
  1464.    Dim i           As Long
  1465.    Dim j           As Long
  1466.    Dim iPos        As Long
  1467.    Dim bInitialize As Boolean
  1468.  
  1469.    If Not IsArray(sArray) Then Exit Sub
  1470.    
  1471.    'initialize the index array if necessary
  1472.    bInitialize = False
  1473.    If UBound(iIndexArray) = -1 Then bInitialize = True Else If iIndexArray(LBound(iIndexArray)) = 0 And iIndexArray(UBound(iIndexArray)) = 0 Then bInitialize = True
  1474.    If bInitialize = True Then CreateIndex iIndexArray, sArray
  1475.    
  1476.    iLBound = LBound(sArray)
  1477.    iUBound = UBound(sArray)
  1478.    
  1479.    ' *NOTE*  the value 4 is VERY important here !!!
  1480.    ' DO NOT CHANGE 4 FOR A LOWER VALUE !!!
  1481.    IndexedTriQuickSortString2 sArray, iIndexArray, 4, iLBound, iUBound
  1482.    IndexedInsertionSortString sArray, iIndexArray, iLBound, iUBound
  1483.    
  1484.    If SortOrder = SortDescending Then ReverseLongArray iIndexArray
  1485. End Sub
  1486.  
  1487. Public Function isSortedAnyArray(ByRef vArray As Variant, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1488.    Dim i       As Long
  1489.    Dim iLBound As Long
  1490.    Dim iUBound As Long
  1491.    Dim iStep   As Long
  1492.    
  1493.    If Not IsArray(vArray) Then isSortedAnyArray = False: Exit Function
  1494.    
  1495.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1496.    iLBound = IIf(SortOrder = SortAscending, LBound(vArray), UBound(vArray))
  1497.    iUBound = IIf(SortOrder = SortAscending, UBound(vArray), LBound(vArray)) - iStep
  1498.    
  1499.    For i = iLBound To iUBound Step iStep
  1500.       If vArray(i) > vArray(i + iStep) Then isSortedAnyArray = False: Exit Function
  1501.    Next i
  1502.    
  1503.    isSortedAnyArray = True
  1504. End Function
  1505.  
  1506. Public Function isSortedLongArray(ByRef iArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1507.    Dim i       As Long
  1508.    Dim iLBound As Long
  1509.    Dim iUBound As Long
  1510.    Dim iStep   As Long
  1511.    
  1512.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1513.    iLBound = IIf(SortOrder = SortAscending, LBound(iArray), UBound(iArray))
  1514.    iUBound = IIf(SortOrder = SortAscending, UBound(iArray), LBound(iArray)) - iStep
  1515.    
  1516.    For i = iLBound To iUBound Step iStep
  1517.       If iArray(i) > iArray(i + iStep) Then isSortedLongArray = False: Exit Function
  1518.    Next i
  1519.    
  1520.    isSortedLongArray = True
  1521. End Function
  1522.  
  1523. Public Function isSortedStringArray(ByRef sArray() As String, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1524.    Dim i       As Long
  1525.    Dim iLBound As Long
  1526.    Dim iUBound As Long
  1527.    Dim iStep   As Long
  1528.    
  1529.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1530.    iLBound = IIf(SortOrder = SortAscending, LBound(sArray), UBound(sArray))
  1531.    iUBound = IIf(SortOrder = SortAscending, UBound(sArray), LBound(sArray)) - iStep
  1532.    
  1533.    For i = iLBound To iUBound Step iStep
  1534.       If sArray(i) > sArray(i + iStep) Then isSortedStringArray = False: Exit Function
  1535.    Next i
  1536.    
  1537.    isSortedStringArray = True
  1538. End Function
  1539.  
  1540. Public Function isSortedIndexedAnyArray(ByRef vArray As Variant, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1541.    Dim i       As Long
  1542.    Dim iLBound As Long
  1543.    Dim iUBound As Long
  1544.    Dim iStep   As Long
  1545.    
  1546.    If Not IsArray(vArray) Then isSortedIndexedAnyArray = False: Exit Function
  1547.    
  1548.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1549.    iLBound = IIf(SortOrder = SortAscending, LBound(vArray), UBound(vArray))
  1550.    iUBound = IIf(SortOrder = SortAscending, UBound(vArray), LBound(vArray)) - iStep
  1551.    
  1552.    For i = iLBound To iUBound Step iStep
  1553.       If vArray(iIndexArray(i)) > vArray(iIndexArray(i + iStep)) Then isSortedIndexedAnyArray = False: Exit Function
  1554.    Next i
  1555.    
  1556.    isSortedIndexedAnyArray = True
  1557. End Function
  1558.  
  1559. Public Function isSortedIndexedLongArray(ByRef iArray() As Long, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1560.    Dim i       As Long
  1561.    Dim iLBound As Long
  1562.    Dim iUBound As Long
  1563.    Dim iStep   As Long
  1564.    
  1565.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1566.    iLBound = IIf(SortOrder = SortAscending, LBound(iArray), UBound(iArray))
  1567.    iUBound = IIf(SortOrder = SortAscending, UBound(iArray), LBound(iArray)) - iStep
  1568.    
  1569.    For i = iLBound To iUBound Step iStep
  1570.       If iArray(iIndexArray(i)) > iArray(iIndexArray(i + iStep)) Then isSortedIndexedLongArray = False: Exit Function
  1571.    Next i
  1572.    
  1573.    isSortedIndexedLongArray = True
  1574. End Function
  1575.  
  1576. Public Function isSortedIndexedStringArray(ByRef sArray() As String, ByRef iIndexArray() As Long, Optional ByVal SortOrder As SortOrder = SortAscending) As Boolean
  1577.    Dim i       As Long
  1578.    Dim iLBound As Long
  1579.    Dim iUBound As Long
  1580.    Dim iStep   As Long
  1581.    
  1582.    iStep = IIf(SortOrder = SortAscending, 1, -1)
  1583.    iLBound = IIf(SortOrder = SortAscending, LBound(sArray), UBound(sArray))
  1584.    iUBound = IIf(SortOrder = SortAscending, UBound(sArray), LBound(sArray)) - iStep
  1585.    
  1586.    For i = iLBound To iUBound Step iStep
  1587.       If sArray(iIndexArray(i)) > sArray(iIndexArray(i + iStep)) Then isSortedIndexedStringArray = False: Exit Function
  1588.    Next i
  1589.    
  1590.    isSortedIndexedStringArray = True
  1591. End Function
  1592.  
  1593.  
  1594. ' /////////////////////
  1595. ' // Synchronisation //
  1596. ' /////////////////////
  1597.  
  1598.  
  1599. Public Sub SynchroniseIndexedAnyArray(ByRef vArray As Variant, ByRef iIndexArray() As Long)
  1600.    Dim i            As Long
  1601.    Dim iLBound      As Long
  1602.    Dim iUBound      As Long
  1603.    Dim vArrayTemp() As Variant
  1604.    
  1605.    If Not IsArray(vArray) Then Exit Sub
  1606.    
  1607.    iLBound = LBound(vArray)
  1608.    iUBound = UBound(vArray)
  1609.    
  1610.    ' vArrayTemp serves as a copy of vArray so that the synchronise effect is saved directly on vArray.
  1611.    CopyAnyArray vArray, vArrayTemp
  1612.    
  1613.    For i = iLBound To iUBound
  1614.       vArray(i) = vArrayTemp(iIndexArray(i))
  1615.    Next i
  1616.    
  1617.    ' recreate the index
  1618.    CreateIndex iIndexArray, vArray
  1619.    
  1620.    Erase vArrayTemp
  1621. End Sub
  1622.  
  1623. Public Sub SynchroniseIndexedLongArray(ByRef iArray() As Long, ByRef iIndexArray() As Long)
  1624.    Dim i            As Long
  1625.    Dim iLBound      As Long
  1626.    Dim iUBound      As Long
  1627.    Dim iArrayTemp() As Long
  1628.  
  1629.    iLBound = LBound(iArray)
  1630.    iUBound = UBound(iArray)
  1631.  
  1632.    ' because we want our synchronise effect to be saved directly on iArray.
  1633.    MoveLongArray iArray, iArrayTemp
  1634.    ReDim iArray(iLBound To iUBound)
  1635.  
  1636.    For i = iLBound To iUBound
  1637.       iArray(i) = iArrayTemp(iIndexArray(i))
  1638.    Next i
  1639.  
  1640.    ' recreate the index
  1641.    CreateIndex iIndexArray, iArray
  1642.  
  1643.    Erase iArrayTemp
  1644. End Sub
  1645.  
  1646. Public Sub SynchroniseIndexedStringArray(ByRef sArray() As String, ByRef iIndexArray() As Long)
  1647.    Dim i            As Long
  1648.    Dim iLBound      As Long
  1649.    Dim iUBound      As Long
  1650.    Dim sArrayTemp() As String
  1651.    Dim iNullArray() As Long ' we use this array to imitate ZeroMemory behavior using CopyMemory with 0's
  1652.    Dim nBytes       As Long
  1653.    
  1654.    iLBound = LBound(sArray)
  1655.    iUBound = UBound(sArray)
  1656.    
  1657.    ReDim iNullArray(iUBound - iLBound + 1)
  1658.    nBytes = (iUBound - iLBound + 1) * 4
  1659.  
  1660.    ' because we want our synchronise effect to be saved directly on sArray.
  1661.    MoveStringArray sArray, sArrayTemp
  1662.    ReDim sArray(iLBound To iUBound)
  1663.  
  1664.    For i = iLBound To iUBound
  1665.       CopyMemory ByVal VarPtr(sArray(i)), ByVal VarPtr(sArrayTemp(iIndexArray(i))), 4
  1666.       'sArray(i) = sArrayTemp(iIndexArray(i))
  1667.    Next i
  1668.    
  1669.    ' *NOTE* for an unexplicable reason, ZeroMemory is far less stable to use than CopyMemory. (incompatible with WinXP)
  1670.    'ZeroMemory ByVal VarPtr(sArraySource(iLBound)), nBytes
  1671.    
  1672.    CopyMemory ByVal VarPtr(sArrayTemp(iLBound)), ByVal VarPtr(iNullArray(0)), nBytes
  1673.  
  1674.    ' recreate the index
  1675.    CreateIndex iIndexArray, sArray
  1676.  
  1677.    Erase sArrayTemp
  1678. End Sub
  1679.  
  1680.  
  1681. ' ///////////////
  1682. ' // Copy/Move //
  1683. ' ///////////////
  1684.  
  1685.  
  1686. Public Sub CopyAnyArray(ByRef vArraySource As Variant, ByRef vArrayDest As Variant)
  1687.    Dim i       As Long
  1688.    Dim iLBound As Long
  1689.    Dim iUBound As Long
  1690.    
  1691.    If (Not IsArray(vArraySource)) Or (Not IsArray(vArrayDest)) Then Exit Sub
  1692.    
  1693.    iLBound = LBound(vArraySource)
  1694.    iUBound = UBound(vArraySource)
  1695.    
  1696.    ReDim vArrayDest(iLBound To iUBound)
  1697.    For i = iLBound To iUBound
  1698.       vArrayDest(i) = vArraySource(i)
  1699.    Next i
  1700. End Sub
  1701.  
  1702. Public Sub CopyLongArray(ByRef iArraySource() As Long, ByRef iArrayDest() As Long)
  1703.    ReDim iArrayDest(LBound(iArraySource) To UBound(iArraySource))
  1704.    CopyMemory iArrayDest(0), iArraySource(0), (UBound(iArraySource) - LBound(iArraySource) + 1) * Len(iArraySource(0))
  1705. End Sub
  1706.  
  1707. Public Sub CopyStringArray(ByRef sArraySource() As String, ByRef sArrayDest() As String)
  1708.    Dim i       As Long
  1709.    Dim iLBound As Long
  1710.    Dim iUBound As Long
  1711.    
  1712.    iLBound = LBound(sArraySource)
  1713.    iUBound = UBound(sArraySource)
  1714.    
  1715.    ReDim sArrayDest(iLBound To iUBound)
  1716.    For i = iLBound To iUBound
  1717.       sArrayDest(i) = sArraySource(i)  ' cannot CopyMemory !
  1718.    Next i
  1719. End Sub
  1720.  
  1721. Public Sub MoveAnyArray(ByRef vArraySource As Variant, ByRef vArrayDest As Variant)
  1722.    If (Not IsArray(vArraySource)) Or (Not IsArray(vArrayDest)) Then Exit Sub
  1723.    CopyAnyArray vArraySource, vArrayDest
  1724.    Erase vArraySource
  1725. End Sub
  1726.  
  1727. Public Sub MoveLongArray(ByRef iArraySource() As Long, ByRef iArrayDest() As Long)
  1728.    CopyLongArray iArraySource, iArrayDest
  1729.    Erase iArraySource
  1730. End Sub
  1731.  
  1732. Public Sub MoveStringArray(ByRef sArraySource() As String, ByRef sArrayDest() As String)
  1733.    Dim iLBound      As Long
  1734.    Dim iUBound      As Long
  1735.    Dim nBytes       As Long
  1736.    Dim iNullArray() As Long ' we use this array to imitate ZeroMemory behavior using CopyMemory with 0's
  1737.    
  1738.    iLBound = LBound(sArraySource)
  1739.    iUBound = UBound(sArraySource)
  1740.    
  1741.    ReDim iNullArray(iUBound - iLBound + 1)
  1742.    nBytes = (iUBound - iLBound + 1) * 4
  1743.    
  1744.    ReDim sArrayDest(iLBound To iUBound) As String
  1745.    
  1746.    CopyMemory ByVal VarPtr(sArrayDest(iLBound)), ByVal VarPtr(sArraySource(iLBound)), nBytes
  1747.    
  1748.    ' *NOTE* for an unexplicable reason, ZeroMemory is far less stable to use than CopyMemory. (incompatible with WinXP)
  1749.    'ZeroMemory ByVal VarPtr(sArraySource(iLBound)), nBytes
  1750.    
  1751.    CopyMemory ByVal VarPtr(sArraySource(iLBound)), ByVal VarPtr(iNullArray(0)), nBytes
  1752.    
  1753.    Erase sArraySource
  1754. End Sub
  1755.  
  1756. Public Sub MergeAnyArray(ByRef vArraySource As Variant, ByRef vArrayDest As Variant, Optional ByVal iPos As Long = -1)
  1757.    Dim i        As Long
  1758.    Dim iLBound  As Long
  1759.    Dim iUBound  As Long
  1760.    Dim iUBound2 As Long
  1761.    Dim iTemp    As Long
  1762.   
  1763.    If (Not IsArray(vArraySource)) Or (Not IsArray(vArrayDest)) Then Exit Sub
  1764.    
  1765.    iLBound = LBound(vArraySource)
  1766.    iUBound = UBound(vArraySource)
  1767.    iUBound2 = UBound(vArrayDest)
  1768.    iTemp = iUBound - iLBound + 1
  1769.    
  1770.    If (iPos > UBound(vArrayDest) + 1) Or (iPos = -1) Then iPos = UBound(vArrayDest) + 1
  1771.    If iPos < 0 Then iPos = 0
  1772.    
  1773.    ReDim Preserve vArrayDest(LBound(vArrayDest) To UBound(vArrayDest) + iTemp)
  1774.    For i = iUBound2 To iPos Step -1
  1775.       vArrayDest(i + iTemp) = vArrayDest(i)
  1776.    Next i
  1777.    
  1778.    iUBound = iPos + iTemp - 1
  1779.    
  1780.    For i = iPos To iUBound
  1781.       vArrayDest(i) = vArraySource(i - iPos)
  1782.    Next i
  1783.    
  1784.    Erase vArraySource
  1785. End Sub
  1786.  
  1787. Public Sub MergeLongArray(ByRef iArraySource() As Long, ByRef iArrayDest() As Long, Optional ByVal iPos As Long = -1)
  1788.    Dim i        As Long
  1789.    Dim iLBound  As Long
  1790.    Dim iUBound  As Long
  1791.    Dim iUBound2 As Long
  1792.    Dim iTemp    As Long
  1793.    
  1794.    iLBound = LBound(iArraySource)
  1795.    iUBound = UBound(iArraySource)
  1796.    iUBound2 = UBound(iArrayDest)
  1797.    iTemp = iUBound - iLBound + 1
  1798.    
  1799.    ReDim Preserve iArrayDest(LBound(iArrayDest) To iUBound2 + iTemp)
  1800.    
  1801.    If (iPos > iUBound2 + 1) Or (iPos = -1) Then
  1802.       iPos = iUBound2 + 1
  1803.    Else
  1804.       If iPos < 0 Then iPos = 0
  1805.       CopyMemory iArrayDest(iPos + iTemp), iArrayDest(iPos), (iUBound2 - LBound(iArrayDest) - iPos + 1) * Len(iArrayDest(iPos))
  1806.    End If
  1807.    
  1808.    CopyMemory iArrayDest(iPos), iArraySource(0), iTemp * Len(iArrayDest(iPos))
  1809.    
  1810.    Erase iArraySource
  1811. End Sub
  1812.  
  1813. Public Sub MergeStringArray(ByRef sArraySource() As String, ByRef sArrayDest() As String, Optional ByVal iPos As Long = -1)
  1814.    Dim i            As Long
  1815.    Dim iLBound      As Long
  1816.    Dim iUBound      As Long
  1817.    Dim iUBound2     As Long
  1818.    Dim iTemp        As Long
  1819.    Dim iNull        As Long
  1820.    Dim iNullArray() As Long ' we use this array to imitate ZeroMemory behavior using CopyMemory with 0's
  1821.    
  1822.    iLBound = LBound(sArraySource)
  1823.    iUBound = UBound(sArraySource)
  1824.    iUBound2 = UBound(sArrayDest)
  1825.    iTemp = iUBound - iLBound + 1
  1826.    ReDim iNullArray(iTemp)
  1827.    
  1828.    ReDim Preserve sArrayDest(LBound(sArrayDest) To iUBound2 + iTemp)
  1829.    
  1830.    If (iPos > iUBound2 + 1) Or (iPos = -1) Then
  1831.       iPos = iUBound2 + 1
  1832.    Else
  1833.       If iPos < 0 Then iPos = 0
  1834.       
  1835.       CopyMemory ByVal VarPtr(sArrayDest(iPos + iTemp)), ByVal VarPtr(sArrayDest(iPos)), (iUBound2 - LBound(sArrayDest) - iPos + 1) * 4
  1836.    End If
  1837.    
  1838.    iTemp = iTemp * 4
  1839.  
  1840.    CopyMemory ByVal VarPtr(sArrayDest(iPos)), ByVal VarPtr(sArraySource(iLBound)), iTemp
  1841.    
  1842.    ' *NOTE* for an unexplicable reason, ZeroMemory is far less stable to use than CopyMemory. (incompatible with WinXP)
  1843.    'ZeroMemory ByVal VarPtr(sArraySource(iLBound)), iTemp * 4
  1844.    
  1845.    CopyMemory ByVal VarPtr(sArraySource(iLBound)), ByVal VarPtr(iNullArray(0)), iTemp
  1846.  
  1847.    Erase sArraySource
  1848. End Sub
  1849.  
  1850.  
  1851. ' ///////////////
  1852. ' // Save/Load //
  1853. ' ///////////////
  1854.  
  1855.  
  1856. Public Function SaveLongArray(ByRef iArray() As Long) As String
  1857.    Dim iLBound  As Long
  1858.    Dim iUBound  As Long
  1859.    Dim iUBound2 As Long
  1860.    Dim i        As Long
  1861.    Dim s()      As Byte
  1862.    
  1863.    iLBound = LBound(iArray)
  1864.    iUBound = UBound(iArray)
  1865.    iUBound2 = 3
  1866.    
  1867.    ReDim s(iUBound2)
  1868.    CopyMemory ByVal VarPtr(s(0)), iUBound - iLBound + 1, 4 ' number of element
  1869.    
  1870.    For i = iLBound To iUBound
  1871.       iUBound2 = iUBound2 + 4
  1872.       
  1873.       ReDim Preserve s(iUBound2)
  1874.       
  1875.       CopyMemory ByVal VarPtr(s(iUBound2 - 3)), iArray(i), 4
  1876.    Next i
  1877.    
  1878.    ' SaveLongArray = s  ' this does not works (!?!)
  1879.    SaveLongArray = Space(iUBound2 + 1)
  1880.    For i = 0 To iUBound2
  1881.       Mid(SaveLongArray, i + 1, 1) = Chr(s(i))
  1882.    Next i
  1883. End Function
  1884.  
  1885. Public Function SaveStringArray(ByRef sArray() As String) As String
  1886.    Dim iLBound  As Long
  1887.    Dim iUBound  As Long
  1888.    Dim iUBound2 As Long
  1889.    Dim i        As Long
  1890.    Dim iLen     As Long
  1891.    Dim s()      As Byte
  1892.  
  1893.    iLBound = LBound(sArray)
  1894.    iUBound = UBound(sArray)
  1895.    iUBound2 = 3
  1896.    iLen = iUBound - iLBound + 1
  1897.  
  1898.    ReDim s(iUBound2)
  1899.    CopyMemory ByVal VarPtr(s(0)), iLen, 4 ' number of element
  1900.  
  1901.    For i = iLBound To iUBound
  1902.       iLen = Len(sArray(i))
  1903.       iUBound2 = iUBound2 + iLen + 4
  1904.  
  1905.       ReDim Preserve s(iUBound2)
  1906.  
  1907.       CopyMemory ByVal VarPtr(s(iUBound2 - iLen - 3)), iLen, 4 ' length of nth element
  1908.       CopyMemory ByVal VarPtr(s(iUBound2 - iLen + 1)), ByVal sArray(i), iLen ' data
  1909.    Next i
  1910.  
  1911.    ' SaveStringArray = s  ' this does not works (!?!)
  1912.    SaveStringArray = Space(iUBound2 + 1)
  1913.    For i = 0 To iUBound2
  1914.       Mid(SaveStringArray, i + 1, 1) = Chr(s(i))
  1915.    Next i
  1916. End Function
  1917.  
  1918. Public Sub LoadLongArray(ByRef iArray() As Long, ByRef sString As String)
  1919.    Dim iUBound As Long
  1920.    Dim i       As Long
  1921.    Dim iPos    As Long
  1922.    Dim s()     As Byte
  1923.    
  1924.    If Len(sString) = 0 Then Exit Sub
  1925.    
  1926.    ' we copy the string to a byte array to avoid unicode bugs (strings CAN be saved in unicode in memory)
  1927.    ReDim s(Len(sString) - 1)
  1928.    CopyMemory ByVal VarPtr(s(0)), ByVal sString, Len(sString)
  1929.    
  1930.    CopyMemory ByVal VarPtr(iUBound), ByVal VarPtr(s(0)), 4 ' number of elements
  1931.    iUBound = iUBound - 1
  1932.    ReDim iArray(iUBound)
  1933.    iPos = 0
  1934.    
  1935.    For i = 0 To iUBound
  1936.       iPos = iPos + 4
  1937.       CopyMemory ByVal VarPtr(iArray(i)), ByVal VarPtr(s(iPos)), 4
  1938.    Next i
  1939. End Sub
  1940.  
  1941. Public Sub LoadStringArray(ByRef sArray() As String, ByRef sString As String)
  1942.    Dim iUBound As Long
  1943.    Dim i       As Long
  1944.    Dim iPos    As Long
  1945.    Dim iLen    As Long
  1946.    Dim s()     As Byte
  1947.    
  1948.    If Len(sString) = 0 Then Exit Sub
  1949.    
  1950.    ' we copy the string to a byte array to avoid unicode bugs (strings CAN be saved in unicode in memory)
  1951.    ReDim s(Len(sString) - 1)
  1952.    CopyMemory ByVal VarPtr(s(0)), ByVal sString, Len(sString)
  1953.    
  1954.    CopyMemory ByVal VarPtr(iUBound), ByVal VarPtr(s(0)), 4 ' number of elements
  1955.    iUBound = iUBound - 1
  1956.    ReDim sArray(iUBound)
  1957.    iPos = 0
  1958.    
  1959.    For i = 0 To iUBound
  1960.       iPos = iPos + 4
  1961.       CopyMemory ByVal VarPtr(iLen), ByVal VarPtr(s(iPos)), 4 ' length of string
  1962.       
  1963.       If iLen > 0 Then
  1964.          sArray(i) = Mid(sString, iPos + 5, iLen)
  1965.          iPos = iPos + iLen
  1966.       Else
  1967.          sArray(i) = vbNullString
  1968.       End If
  1969.    Next i
  1970. End Sub
  1971.  
  1972.  
  1973. ' ////////////
  1974. ' // Others //
  1975. ' ////////////
  1976.  
  1977.  
  1978. ' Returns an array of the type of the first sent argument.
  1979. Public Function CreateArray(ParamArray values() As Variant) As Variant
  1980.    Dim i       As Long
  1981.    Dim iUBound As Long
  1982.    Dim vTemp   As Variant
  1983.  
  1984.    iUBound = UBound(values)
  1985.     
  1986.    ' we can't use the vbObject constant for objects because the VarType() function might return the type of the object's default property
  1987.    If IsObject(values(0)) Then
  1988.       ReDim oObjectArray(0 To iUBound) As Object
  1989.  
  1990.       For i = 0 To iUBound
  1991.          Set oObjectArray(i) = values(i)
  1992.       Next i
  1993.  
  1994.       CreateArray = oObjectArray()
  1995.       Exit Function
  1996.    End If
  1997.     
  1998.    Select Case VarType(values(0))
  1999.       Case vbLong
  2000.          ReDim lArray(0 To iUBound) As Long
  2001.          vTemp = lArray()
  2002.       Case vbString
  2003.          ReDim sArray(0 To iUBound) As String
  2004.          vTemp = sArray()
  2005.       Case vbInteger
  2006.          ReDim iArray(0 To iUBound) As Integer
  2007.          vTemp = iArray()
  2008.       Case vbSingle
  2009.          ReDim sngArray(0 To iUBound) As Single
  2010.          vTemp = sngArray()
  2011.       Case vbDouble
  2012.          ReDim dArray(0 To iUBound) As Double
  2013.          vTemp = dArray()
  2014.       Case vbCurrency
  2015.          ReDim cArray(0 To iUBound) As Currency
  2016.          vTemp = cArray()
  2017.       Case vbDate
  2018.          ReDim datArray(0 To iUBound) As Date
  2019.          vTemp = datArray()
  2020.       Case vbBoolean
  2021.          ReDim bArray(0 To iUBound) As Boolean
  2022.          vTemp = bArray()
  2023.       Case Else
  2024.          ' unsupported data type (UDT or array)
  2025.    End Select
  2026.             
  2027.    For i = 0 To iUBound
  2028.       vTemp(i) = values(i)
  2029.    Next i
  2030.  
  2031.    CreateArray = vTemp
  2032. End Function
  2033.  
  2034. ' MsgBox an array. Use for debugging.
  2035. Public Sub DebugDumpArray(ByRef vArray As Variant, Optional ByVal iColumnWidth As Long = 4)
  2036.    Dim iLBound As Long
  2037.    Dim iUBound As Long
  2038.    Dim i       As Long
  2039.    Dim j       As Long
  2040.    Dim iPos    As Long
  2041.    Dim sString As String
  2042.    
  2043.    If Not IsArray(vArray) Then Exit Sub
  2044.    If iColumnWidth < 1 Then iColumnWidth = 1
  2045.  
  2046.    iLBound = LBound(vArray)
  2047.    iUBound = UBound(vArray)
  2048.    iPos = iLBound - 1
  2049.    sString = "Dumping array:" & vbTab & "Type -> " & TypeName(vArray) & " <" & iLBound & " To " & iUBound & ">" & vbCrLf & vbCrLf
  2050.  
  2051.    If iUBound > 100 Then iUBound = 100 ' MsgBox can't show over 100 anyway.
  2052.  
  2053.    For i = iLBound To iUBound
  2054.       If iPos + iColumnWidth > iUBound Then iColumnWidth = iUBound - iPos
  2055.       
  2056.       For j = 1 To iColumnWidth
  2057.          iPos = iPos + 1
  2058.          
  2059.          sString = sString & iPos & ":  " & vArray(iPos) & vbTab
  2060.       Next j
  2061.       
  2062.       sString = sString & vbCrLf
  2063.    Next i
  2064.    
  2065.    MsgBox sString
  2066. End Sub
  2067.  
  2068. Public Sub ReverseAnyArray(ByRef vArray As Variant)
  2069.    Dim iLBound As Long
  2070.    Dim iUBound As Long
  2071.    
  2072.    If Not IsArray(vArray) Then Exit Sub
  2073.  
  2074.    iLBound = LBound(vArray)
  2075.    iUBound = UBound(vArray)
  2076.    
  2077.    While iLBound < iUBound
  2078.       SwapAny vArray(iLBound), vArray(iUBound)
  2079.    
  2080.       iLBound = iLBound + 1
  2081.       iUBound = iUBound - 1
  2082.    Wend
  2083. End Sub
  2084.  
  2085. Public Sub ReverseLongArray(ByRef iArray() As Long)
  2086.    Dim iLBound As Long
  2087.    Dim iUBound As Long
  2088.  
  2089.    iLBound = LBound(iArray)
  2090.    iUBound = UBound(iArray)
  2091.    
  2092.    While iLBound < iUBound
  2093.       SwapLongs iArray(iLBound), iArray(iUBound)
  2094.    
  2095.       iLBound = iLBound + 1
  2096.       iUBound = iUBound - 1
  2097.    Wend
  2098. End Sub
  2099.  
  2100. Public Sub ReverseStringArray(ByRef sArray() As String)
  2101.    Dim iLBound As Long
  2102.    Dim iUBound As Long
  2103.  
  2104.    iLBound = LBound(sArray)
  2105.    iUBound = UBound(sArray)
  2106.    
  2107.    While iLBound < iUBound
  2108.       SwapStrings sArray(iLBound), sArray(iUBound)
  2109.    
  2110.       iLBound = iLBound + 1
  2111.       iUBound = iUBound - 1
  2112.    Wend
  2113. End Sub
  2114.  
  2115.  
  2116.  
  2117.  
  2118.  
  2119.  
  2120.  
  2121.  
  2122.  
  2123.  
  2124.  
  2125.  
  2126. ' //////////////////
  2127. ' // Private Subs //
  2128. ' //////////////////
  2129.  
  2130.  
  2131. ' this sub is intended for internal usage. It only fills iIndexArray().
  2132. Private Sub CreateIndex(ByRef iIndexArray() As Long, ByRef vSizeArray As Variant)
  2133.    Dim i       As Long
  2134.    Dim iLBound As Long
  2135.    Dim iUBound As Long
  2136.    
  2137.    iLBound = LBound(vSizeArray)
  2138.    iUBound = UBound(vSizeArray)
  2139.    
  2140.    ReDim iIndexArray(iLBound To iUBound)
  2141.    For i = iLBound To iUBound
  2142.       iIndexArray(i) = i
  2143.    Next
  2144. End Sub
  2145.  
  2146. Private Sub TriQuickSortAny2(ByRef vArray As Variant, ByVal iSplit As Long, ByVal iMin As Long, ByVal iMax As Long)
  2147.    Dim i     As Long
  2148.    Dim j     As Long
  2149.    Dim vTemp As Variant
  2150.    
  2151.    ' *NOTE* no checks are made in this function because it is used internally.
  2152.    ' Validity checks are made in the public function that calls this one.
  2153.    
  2154.    If (iMax - iMin) > iSplit Then
  2155.       i = (iMax + iMin) / 2
  2156.       
  2157.       If vArray(iMin) > vArray(i) Then SwapAny vArray(iMin), vArray(i)
  2158.       If vArray(iMin) > vArray(iMax) Then SwapAny vArray(iMin), vArray(iMax)
  2159.       If vArray(i) > vArray(iMax) Then SwapAny vArray(i), vArray(iMax)
  2160.       
  2161.       j = iMax - 1
  2162.       SwapAny vArray(i), vArray(j)
  2163.       i = iMin
  2164.       vTemp = vArray(j)
  2165.       
  2166.       Do
  2167.          Do
  2168.             i = i + 1
  2169.          Loop While vArray(i) < vTemp
  2170.          
  2171.          Do
  2172.             j = j - 1
  2173.          Loop While vArray(j) > vTemp
  2174.          
  2175.          If j < i Then Exit Do
  2176.          SwapAny vArray(i), vArray(j)
  2177.       Loop
  2178.       
  2179.       SwapAny vArray(i), vArray(iMax - 1)
  2180.       
  2181.       TriQuickSortAny2 vArray, iSplit, iMind SwapAny vArray(iMin), v iMin(j)
  2182. ong, ByVal iMaxnd As Long
  2183.  Endrray(iMin), v iMin(j)
  2184. ong, ByVal iMaxnd As