Leaderboard
Popular Content
Showing content with the highest reputation on 12/15/2019 in all areas
-
UI Automation Events In standard Windows GUI code, you can respond to Windows messages by registering and implementing a message handler function. A WM_NOTIFY message handler is registered and implemented in this way: ; Registration GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" ) ; Implementation Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam ) ; Code EndFunc The WM_NOTIFY function is a callback function. In UI Automation code, you can respond to UI Automation events by registering and implementing an event handler object. A FocusChangedEventHandler object is registered and implemented in this way: ; Registration UIAEH_FocusChangedEventHandlerCreate() $oUIAutomation.AddFocusChangedEventHandler( 0, $oUIAEH_FocusChangedEventHandler ) ; Implementation ; This is the function that receives events Func UIAEH_FocusChangedEventHandler_HandleFocusChangedEvent( $pSelf, $pSender ) ; Code EndFunc $oUIAEH_FocusChangedEventHandler is the event handler object. It's a callback object. This thread is about how to respond to UI Automation events by registering, implementing and using event handler objects. User Defined Functions Registering and implementing the event handler object in the code above is very simple. To make it that simple, it's necessary to include a few functions in a UDF. The zip-file at bottom of post contains a UIAEH_FocusChangedEventHandler.au3 UDF that allows the registration and implementation of the event handler object as simple as shown above. UIAEH_FocusChangedEventHandlerCreate() is a function in this UDF. The function creates the $oUIAEH_FocusChangedEventHandler object. Because this is a callback object it must be implemented through ObjectFromTag() (coded in ObjectFromTag.au3 in zip-file) function by trancexx and not through the usual ObjCreateInterface() which is used to create other UI Automation objects. (The warning at top of the help text is much more an error in the help text than an error in the function. Thousands of examples show that ObjCreateInterface() works flawlessly.) You don't need to use ObjectFromTag() in your own code. This function is completely handled in the UDFs. UIAEH_FocusChangedEventHandler_HandleFocusChangedEvent() is the function that receives events. This function is coded but commented out in UIAEH_FocusChangedEventHandler.au3. Copy this function to your own code. See FocusChangedEventHandler example below. UIA Event Handlers The first version of Microsoft's UI Automation code (the Windows 7 version that works on Windows XP, Windows Vista, Windows 7 and all later versions) implements four different event handler objects. In addition to the FocusChangedEventHandler object, it's the AutomationEventHandler, PropertyChangedEventHandler and StructureChangedEventHandler objects. The zip-file at the bottom contains the corresponding three event handler UDFs. By the way, these four event handlers are implemented in AutoIt several years ago in this and the following posts. FocusChangedEventHandler exposes a method to handle events that are raised when the keyboard focus moves to another UI Automation element. AutomationEventHandler exposes a method to handle Microsoft UI Automation events. These events include MenuOpened ($UIA_MenuOpenedEventId) and Window_WindowOpened ($UIA_Window_WindowOpenedEventId) events. See module UIA_EventIds in UIA_Constants.au3 for a complete list. Note that EventIds that belong to Windows 8 or later cannot be used in this version of the event handler. But they can be used after code updates over the coming weeks. 2019-12-15 If you select Desktop, AutomationEvent and all Events (Event Ids) in UI Automation Event Monitor below and click Start, you'll see the following output in SciTE console: $oUIAEH_AutomationEventHandler OK Desktop: AddAutomationEventHandler() = AsyncContentLoaded OK Desktop: AddAutomationEventHandler() = AutomationFocusChanged ERR Desktop: AddAutomationEventHandler() = AutomationPropertyChanged OK Desktop: AddAutomationEventHandler() = InputDiscarded OK Desktop: AddAutomationEventHandler() = InputReachedOtherElement OK Desktop: AddAutomationEventHandler() = InputReachedTarget OK Desktop: AddAutomationEventHandler() = Invoke_Invoked OK Desktop: AddAutomationEventHandler() = LayoutInvalidated OK Desktop: AddAutomationEventHandler() = MenuClosed OK Desktop: AddAutomationEventHandler() = MenuModeEnd OK Desktop: AddAutomationEventHandler() = MenuModeStart OK Desktop: AddAutomationEventHandler() = MenuOpened OK Desktop: AddAutomationEventHandler() = Selection_Invalidated OK Desktop: AddAutomationEventHandler() = SelectionItem_ElementAddedToSelection OK Desktop: AddAutomationEventHandler() = SelectionItem_ElementRemovedFromSelection OK Desktop: AddAutomationEventHandler() = SelectionItem_ElementSelected OK Desktop: AddAutomationEventHandler() = StructureChanged OK Desktop: AddAutomationEventHandler() = Text_TextChanged OK Desktop: AddAutomationEventHandler() = Text_TextSelectionChanged OK Desktop: AddAutomationEventHandler() = ToolTipClosed OK Desktop: AddAutomationEventHandler() = ToolTipOpened OK Desktop: AddAutomationEventHandler() = Window_WindowClosed OK Desktop: AddAutomationEventHandler() = Window_WindowOpened OK Note the error for the AutomationFocusChanged event handler (at the top of the list). I've found that there is an error, but so far have no explanation for it. PropertyChangedEventHandler exposes a method to handle Microsoft UI Automation events that occur when a property is changed. These events include Name ($UIA_NamePropertyId) and ValueValue ($UIA_ValueValuePropertyId) changes. See module UIA_PropertyIds in UIA_Constants.au3 for a complete list. Note that PropertyIds that belong to Windows 8 or later cannot be used in this version of the event handler. But they can be used after code updates over the coming weeks. StructureChangedEventHandler exposes a method to handle events that occur when the Microsoft UI Automation tree structure is changed. This event handler seems to be more valuable in eg. screen readers than in usual UI Automation code. NotificationEventHandler exposes a method to handle Microsoft UI Automation notification events. See this post. FocusChangedEventHandler example This is full code in Examples\FocusChangedEventHandlerEx.au3. Run the code in SciTE with F5. #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 ;#AutoIt3Wrapper_UseX64=y Opt( "MustDeclareVars", 1 ) #include "..\Includes\UIAEH_FocusChangedEventHandler.au3" Example() Func Example() UIAEH_FocusChangedEventHandlerCreate() If Not IsObj( $oUIAEH_FocusChangedEventHandler ) Then Return ConsoleWrite( "$oUIAEH_FocusChangedEventHandler ERR" & @CRLF ) ConsoleWrite( "$oUIAEH_FocusChangedEventHandler OK" & @CRLF ) Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtag_IUIAutomation ) If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "$oUIAutomation ERR" & @CRLF ) ConsoleWrite( "$oUIAutomation OK" & @CRLF ) Local $iError = $oUIAutomation.AddFocusChangedEventHandler( 0, $oUIAEH_FocusChangedEventHandler ) If $iError Then Return ConsoleWrite( "AddFocusChangedEventHandler() ERR" & @CRLF ) ConsoleWrite( "AddFocusChangedEventHandler() OK" & @CRLF ) HotKeySet( "{ESC}", "Quit" ) While Sleep(10) WEnd EndFunc Func Quit() UIAEH_FocusChangedEventHandlerDelete() Exit EndFunc ; This is the function that receives events Func UIAEH_FocusChangedEventHandler_HandleFocusChangedEvent( $pSelf, $pSender ) ; Ret: long Par: ptr ConsoleWrite( @CRLF & "UIAEH_FocusChangedEventHandler_HandleFocusChangedEvent: " & $pSender & @CRLF ) Local $oSender = ObjCreateInterface( $pSender, $sIID_IUIAutomationElement, $dtag_IUIAutomationElement ) $oSender.AddRef() ConsoleWrite( "Title = " & UIAEH_GetCurrentPropertyValue( $oSender, $UIA_NamePropertyId ) & @CRLF & _ "Class = " & UIAEH_GetCurrentPropertyValue( $oSender, $UIA_ClassNamePropertyId ) & @CRLF & _ "Ctrl type = " & UIAEH_GetCurrentPropertyValue( $oSender, $UIA_ControlTypePropertyId ) & @CRLF & _ "Ctrl name = " & UIAEH_GetCurrentPropertyValue( $oSender, $UIA_LocalizedControlTypePropertyId ) & @CRLF & _ "Handle = " & "0x" & Hex( UIAEH_GetCurrentPropertyValue( $oSender, $UIA_NativeWindowHandlePropertyId ) ) & @CRLF & _ "Value = " & UIAEH_GetCurrentPropertyValue( $oSender, $UIA_ValueValuePropertyId ) & @CRLF ) Return 0x00000000 ; $S_OK #forceref $pSelf EndFunc ; Auxiliary function (for simple properties only) ; There must be only one instance of this function Func UIAEH_GetCurrentPropertyValue( $oSender, $iPropertyId ) Local $vPropertyValue $oSender.GetCurrentPropertyValue( $iPropertyId, $vPropertyValue ) Return $vPropertyValue EndFunc Test the code by opening the Desktop context menu and move the mouse over the menu items. When the mouse hovers over a menu item it gets focus. Exit with Esc. AutomationEventHandlerEx.au3 Test the example by opening some windows, menus and tooltips. PropertyChangedEventHandlerEx.au3 Open Windows Task Manager (Ctrl+Shift+Esc) and you'll get a whole bunch of property changed events. Note that to access Windows Task Manager (and other programs) on Windows 10, use #RequireAdmin in the script and run SciTE as administrator. StructureChangedEventHandlerEx.au3 Clicking on a folder in File/Windows Explorer treeview generates a lot of structure changed events. New examples will be added over the coming weeks. UI Automation Event Monitor UI Automation Event Monitor is a tool to detect UI Automation events. The first group of toolbar buttons is used to set up the UI Automation event handler. The images have a PropertyChangedEventHandler set up to monitor Name ($UIA_NamePropertyId) and ValueValue ($UIA_ValueValuePropertyId) property changes in Windows Task Manager. Note that to access Windows Task Manager (and other programs) on Windows 10, use #RequireAdmin in the script (UIAEHEvents.au3) and run SciTE as administrator. These four toolbar buttons are hot-track enabled with the technique used in this example. The buttons open four listviews to set up the event handler. All listviews have multiple selections enabled. Select multiple items at once and check a single item in/out to check all selected items in/out. Use mouse or Space key to check an item in/out. In the first image, the mouse hovers over the Top Windows button so that it's selected. In the second image, the Top Windows button is clicked so that the listview is opened. Moving the mouse over the next three buttons automatically opens the corresponding listviews due to hot-tracking. Click the toolbar button again to close the listview or move the mouse above the title bar. Windows Task Manager is checked in the Top Windows listview. The Event Handlers listview is enabled when a Top Window is checked. The Event Ids listview is enabled when the AutomationEvent event handler is checked. The Property Ids listview is enabled when the PropertyChanged event handler is checked. In Event Ids and Property Ids listview you can select an item by pressing the first letter on the keyboard. The second group of toolbar buttons is used to Start/Stop the event handler (or multiple event handlers), Pause/Continue updating the Events listview, and Clear the listview. Note that the Pause button only stops updating the listview. Events are still detected and stored in $aEvents array. All new events are added at the bottom of the listview when you click Continue or Stop. The Pause Main Loop button is used to stop the GUI main loop, which significantly increases the performance of the Events listview. When the GUI main loop is paused you can only click the Continue Main Loop button (same button as Pause Main Loop) and you can close the GUI. Events listview The Events listview is a virtual listview because of performance and to make it easy to constantly add new rows to the bottom of the listview. Nevertheless, there can still be performance issues when a lot of events are generated. To remedy these issues, it's possible to stop the GUI main loop with the Pause Main Loop button. This significantly increases the performance of the Events listview. When there are a large number of events (when many new rows are added quickly at the bottom of the listview), this procedure must be followed to stop the event handler (or multiple event handlers): Make sure the GUI main loop isn't paused. Click the Pause button (second toolbar group) to pause Events listview updates. Click the Stop button to stop the event handler. When there are a small number of events you can immediately click the Stop button. To test the UI Automation Event Monitor, try performing the four examples (in Examples\, briefly described above) through the monitor GUI. There'll probably be some GUI updates in the coming weeks. UIA Menu, Tooltip and Window Events Monitor is a simpler version of UI Automation Event Monitor that detects only MenuOpened, ToolTipOpened and Window_WindowOpened events (AutomationEvents). Updates Windows 8, Windows 8.1 and Windows 10 updates Threads UI Automation UDFs contains all include files. UIASpy - UI Automation Spy Tool is a GUI tool that provides information about windows and controls and their interconnection and provides functionality to generate sample code. UIASpy is essential for creating UI Automation code. In Using UI Automation Code in AutoIt you can find and download examples and read information about using UIA code. IUIAutomation MS framework automate chrome, FF, IE, .... created by junkew August 2013 is the first AutoIt thread on UIA code. Zip-file The zip contains source files for GUIs and examples. Note that UI Automation UDFs must be installed in the Includes folder. You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Note that under Windows 7 and earlier versions, only the original four event handlers (AutomationEventHandler, FocusChangedEventHandler, PropertyChangedEventHandler, StructureChangedEventHandler) introduced in Windows 7 can be used. Comments are welcome. Let me know if there are any issues. UIAEHEvents.7z2 points
-
_AutoItErrorTrap.au3 (UDF) - Error detection in AutoIt scripts!
GoogleDude reacted to JScript for a topic
Function Reference _AutoItErrorTrap.au3 UDF to intercept the error window of AutoIt, showing more details about the error, including ability to save and send by email! (Detection and treatment of errors in the AutoIt scripts!) Sintax: ; Adding the directives below, will cause your program be compiled with the indexing of the original lines shown in SciTE: #AutoIt3Wrapper_Run_Before=ShowOriginalLine.exe %in% #AutoIt3Wrapper_Run_After=ShowOriginalLine.exe %in% ;Or you can use the program "ShowOriginalLine.exe" directly and choose the script you want to add support to show the original lines! #include "_AutoItErrorTrap.au3" ; _AutoItErrorTrap ([sTitle [, sText [, fUseCallBack]]]) _AutoItErrorTrap()Supports: ;Error detection in your AutoIt script and local #Includes! ;According to the AutoIt Help, the definition for "local Includes" is: ;------------------------------------------------------------------------------ ;If "..." is used, the filename is taken to be relative to the current script. ;------------------------------------------------------------------------------ ;This means that for you to have your UDF or other external module with support to show the original lines, one should use as follows: #include "[path] filename" ; See the AutoIt help for more details.Downloads: Version: 0.11 _AutoItErrorTrap_(RedirectLink).html Note: The error detection is performed in the same executable, does not requiring opening two .exes for this action! Works with CUI too. Usage example is included! (You need to compile first, but it also work with non compiled scripts using context menu [Run Script...]) Sample: More sample: Fixes: #ShowLine_Off $hForm = GUICreate("My GUI menu", 480, 360) $iFileMenu = GUICtrlCreateMenu("&File") $iFileItem = GUICtrlCreateMenuItem("Open", $iFileMenu) GUICtrlSetState(-1, $GUI_DEFBUTTON) $iHelpMenu = GUICtrlCreateMenu("?") GUICtrlCreateMenuItem("Save", $iFileMenu) GUICtrlSetState(-1, $GUI_DISABLE) #ShowLine_OnWithout them the rows above would be as follows: $__iLineNumber=1 & " - " & '$hForm = GUICreate("My GUI menu", 480, 360)...' $hForm = GUICreate("My GUI menu", 480, 360) $__iLineNumber=3 & " - " & '$iFileMenu = GUICtrlCreateMenu("&File")' $iFileMenu = GUICtrlCreateMenu("&File") $__iLineNumber=4 & " - " & '$iFileItem = GUICtrlCreateMenuItem("Ope...' $iFileItem = GUICtrlCreateMenuItem("Open", $iFileMenu) $__iLineNumber=5 & " - " & 'GUICtrlSetState(-1, $GUI_DEFBUTTON)' GUICtrlSetState(-1, $GUI_DEFBUTTON) $__iLineNumber=6 & " - " & '$iHelpMenu = GUICtrlCreateMenu("?")' $iHelpMenu = GUICtrlCreateMenu("?") $__iLineNumber=7 & " - " & 'GUICtrlCreateMenuItem("Save", $iFileMenu)' GUICtrlCreateMenuItem("Save", $iFileMenu) $__iLineNumber=8 & " - " & 'GUICtrlSetState(-1, $GUI_DISABLE)' GUICtrlSetState(-1, $GUI_DISABLE) And the directive #ShowOriginalLine_Param with the following options: /SV or /SkipAllVars ; Skip all Variables to show original line numbers. /SG or /SkipGlobalVars ; Skip Global Variables to show original line numbers. /SL or /SkipLocalVars ; Skip Local Variables to show original line numbers.29/10/2012 -> *Adding the directives below, will cause your program be compiled with the indexing of the original lines shown in SciTE: #AutoIt3Wrapper_Run_Before=ShowOriginalLine.exe %in% #AutoIt3Wrapper_Run_After=ShowOriginalLine.exe %in%*Or you can use the program ShowOriginalLine.exe directly and choose the script you want to add support to show the original lines!*It was added #region Translations with the variables available for translation! 0.10.2312.2600b 25/10/2012 -> Added support for viewing the last screen before the error! Now the window can expand and retract again...Change the hook CALLWNDRETPROC to CALLWNDPROC (better...). Added a help button [?] In the main window and minor changes in the code. 0.10.2312.2600b 23/10/2012 -> *Added support to display the original line numbers in the scripts compiled! Thanks to @FireFox for this tip! $hAET_GETERROR = StringRegExpReplace($hAET_GETERROR, "d+[0-9]", Eval("__iLineNumber") & @CRLF)*Additional information suggested by @ricky03, thank you!*Some bugs were fixed and the file ShowOriginalLineNumbers.au3 was added in zip file. 0.10.2112.2600b 21/10/2012 -> Bugs were fixed and a few functions have been rewritten!0.10.1912.2600b 19/10/2012 -> The UDF was almost completely rewritten, now no longer need to include _AdlibEnhance.au3.The detection of error is instant and does not consume more CPU. Interface compatibility with Windows XP 0.10.1812.2600b 18/10/2012 -> Improvements such as icons on the buttons and added a button to save the error information!0.10.1712.2600b 17/10/2012 -> First release!Regards, João Carlos.1 point -
This is a simple and practical way to start and manage many processes. I use it to quickly query many remote clients for varyous informations. For processes I mean external programs that can run autonomously and that allows I/O redirection. Here the advantage is that when you start a process, you can also determine which function in your main program should receive results when the external process finishes executing. So you can have different processes and different "callback" functions. The whole works nearly autonomously and asynchronously. While spawned processes works, you can perform other tasks in the main script and leave the worry of receiving and managing the results, to the pre-established functions. As I sayd, everything is almost completely automatic and the processes do their work asynchronously and independently of the flow of the main script. To check and manage processes status, you have only to call as often as possible the _TasksCheckStatus() function that will quickly handle the running processes. AdlibRegister() could be used for this ripetitive "polling". In short: 1) setup single commandlines that will "generate" your wanted result 2) establish wich function of your main script should receive the above result 3) an optional Timeout in seconds is allowed (task from point 1 will be killed at timeout) see function header for details. The provided example of use, needs to be executed in a network environment with preferably many clients, and you have to be administrator. Populate the $aIPList[] array with ClientNames (or ip addresses), and this example script will query all the clients about some informations by running many processes asincronously. Then it will collect results from the processes as soon as "answers" are available and will populate the ListView with that data. I used a ListView so that you can see that all the cells will fill (quite quickly) in an asynchronous way. If you are not in a network with many clients, then this example is nearly useless... (sorry) Hope it can be of use... (suggestions, bug reports and emrovements are welcome) MultiTask.udf #include-once ; #include "Array.au3" ; #INDEX# ======================================================================================================================= ; Title .........: Multi Task Management (tasks spawned by the user via the _TaskRun function of this udf) ; AutoIt Version : ; Language ......: ; Description ...: Functions that assists to execute and manage user's spawned tasks. ; Author(s) .....: Chimp ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _TaskRun Run a new task and stacks its references for later use ; _TasksCheckStatus Check the status of all running tasks and call the CallBack function when a task completed ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#============================================================================================================ ; __TaskRemove frees task's stacked references ; __NOP No OPeration function ; =============================================================================================================================== Global $aTasks[1][7] = [[0]] ; Create and initialize the task reference stack to 0 ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TaskRun ; Description ...: Runs a new task as specified in $sCommand ; Syntax ........: _TaskRun($vIndex, $sCommand[, $sParser = "__NOP"[, $vTimeout = False]]) ; Parameters ....: $vIndex - A variant value passed by the caller as a reference to this task. ; this value will be returned to the callback function along with results. ; ; $sCommand - A string value containing the full command to be executed ; ; $sParser - [optional] The name of the function to be called at the end of this task. ; Default is __NOP that is a "do nothing" function. ; P.S. ; the $aParser function will be called by the _TasksCheckStatus() function when ; this task will finish to run: ; an 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task ; ; $vTimeout - [optional] A variant value. Default is False. ; if specified is the number of seconds for the Timeout. ; After this amount of seconds, if this task is still running, it is killed ; ; Return values .: the total number of stacked tasks ; Author ........: Chimp ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TaskRun($vIndex, $sCommand, $sParser = "__NOP", $vTimeout = False) ; ; Stack structure: ; ; +-----+ ; 0 | nr. | <-- [0][0] number of running tasks ; +-----+-----+-----+-----+-----+-----+-----+ ; 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ; +-----+-----+-----+-----+-----+-----+-----+ ; n | * | * | * | * | * | * | * | ; | | | | | | | ; | | | | | | [n][6] setted with the TimerInit() value at the start of this task. ; | | | | | | ; | | | | | [n][5] contains the required Timeout in seconds (Default is set to False = no timeout) ; | | | | | ; | | | | [n][4] The CallBack function name that will receive the results of this task ; | | | | ; | | | [n][3] the error message returned by the StdErr stream of this task ; | | | ; | | [n][2] the result of the command returned by the StdOut stream of this task ; | | ; | [n][1] the reference of this task passed by the user (will be passed back to the caller along with results) ; | ; [n][0] the PID of this running task ; ReDim $aTasks[$aTasks[0][0] + 2][7] ; add a new element to the stack $aTasks[0][0] += 1 ; add 1 to the number of running tasks ; Run the passed command with the I/O streams redirected $aTasks[$aTasks[0][0]][0] = Run($sCommand, "", @SW_HIDE, 6) ; 6 -> $STDOUT_CHILD (0x2) + $STDERR_CHILD (0x4) ; store references of this task to the stack $aTasks[$aTasks[0][0]][1] = $vIndex $aTasks[$aTasks[0][0]][4] = $sParser $aTasks[$aTasks[0][0]][5] = $vTimeout ; can be False or the number of seconds for the timeout of this task $aTasks[$aTasks[0][0]][6] = TimerInit() ; store the TimerInit() value for this task Return $aTasks[0][0] ; return the total number of running tasks EndFunc ;==>_TaskRun ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TasksCheckStatus ; Description ...: Scans the status of all active tasks and checks if some task has finished its job ; This function should be called as aften as possible ; Syntax ........: _TaskCheckStatus() ; Parameters ....: None ; Return values .: number of still running tasks; * see remarks ; Author ........: Chimp ; Modified ......: ; Remarks .......: When a task finish, it is removed from the stack and results are passed to the callback function. ; An 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task (approximative time) ; is an approximate datum because it also includes the delay ; added by the main loop before calling _TasksCheckStatus() ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TasksCheckStatus() If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return Local $bEndTask ; will be setted to True if the checked task has finished or is killed by timeout Local $iPointer = 1 ; start checking from first task in the stack Local $aResults[4] ; this Array will be loaded with the results of the task Local $sCallBack ; the name of the function to call when a task ends its job Local $aArgs[2] = ["CallArgArray", ""] ; "special" array will be loaded with parameters for the CallBack function While $iPointer <= $aTasks[0][0] $bEndTask = False $aTasks[$iPointer][2] &= StdoutRead($aTasks[$iPointer][0]) ; read and store the StdOut stream $aTasks[$iPointer][3] &= StderrRead($aTasks[$iPointer][0]) ; read and store the StdErr stream ; If @error Then ; if there is an @error is because this task has finished its job If Not ProcessExists($aTasks[$iPointer][0]) Then ; if this task has finished its job $bEndTask = True ; flag the end of the work ElseIf $aTasks[$iPointer][5] <> False Then ; if task is still running see if a Timeout check was rquired and if so if is been reached If Int(TimerDiff($aTasks[$iPointer][6]) / 1000) >= $aTasks[$iPointer][5] Then ; timeout reached or exceeded!! $aTasks[$iPointer][3] = "@error Timeout " & $aTasks[$iPointer][3] ; insert an error message at the beginning of the StdErr stream StdioClose($aTasks[$iPointer][0]) ; close I/O streams ProcessClose($aTasks[$iPointer][0]) ; kill this process $bEndTask = True ; flag the end of this task EndIf EndIf If $bEndTask Then ; if this task has finished, get its results and send to the CallBack function $aResults[0] = $aTasks[$iPointer][1] ; ............. Index (the caller reference) $aResults[1] = $aTasks[$iPointer][2] ; ............. the StdOut generated by this task $aResults[2] = $aTasks[$iPointer][3] ; ............. the StdErr generated by this task $aResults[3] = TimerDiff($aTasks[$iPointer][6]) ; .. time spent by this task $sCallBack = $aTasks[$iPointer][4] ; the name of the function to be called $aArgs[1] = $aResults ; second element of the "CallArgArray" special array contains the $aResults array ; loaded with the parameters to be send to the CallBack function. (array in array) ; (the CallBack function will receive only the 1D 4 elements array $aResults) __TaskRemove($iPointer) ; remove references of this task from the stack ; call the CallBack function and pass the results of this task. ; (CallBack function should return as soon as possible because it stops the CheckStatus for the other tasks) ; Call($sCallBack, $aArgs) ; Call CallBack function --->---+ ; | ; <--- and return here ---------------------------------+ EndIf $iPointer += 1 ; check next task WEnd Return $aTasks[0][0] EndFunc ;==>_TasksCheckStatus ; Internal use. Remove the task references from the stack Func __TaskRemove($iElement) #cs If $iElement > $aTasks[0][0] Or $iElement < 1 Then Return StdioClose($aTasks[$iElement][0]) _ArrayDelete($aTasks, $iElement) #ce ; - new -------------------------------------------------- ; remove element without the _Array* udf If $iElement > 0 And $iElement <= $aTasks[0][0] Then StdioClose($aTasks[$iElement][0]) If $aTasks[0][0] > 1 Then For $i = 0 To UBound($aTasks, 2) - 1 $aTasks[$iElement][$i] = $aTasks[$aTasks[0][0]][$i] Next EndIf Else Return ; queue is empty or the required element is out of bound EndIf $aTasks[0][0] -= 1 ReDim $aTasks[$aTasks[0][0] + 1][UBound($aTasks, 2)] Return $aTasks[0][0] ; returns the number of tasks still running EndFunc ;==>__TaskRemove ; Internal use. An empty function Func __NOP($aDummy) ; NOP (No OPeration) EndFunc ;==>__NOP Example of use ; #RequireAdmin #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include '.\MultiTask.au3' ; ; IMPORTANT following array should be populated with many real HostNames ; here are just nonsense items used as placeholders Global $aIPList[] = [@ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName] Global $hGrid ; The ListView Handle Global $ahGrid[1] ; An array to keep handles of any listview row Example() MsgBox(0, "Debug:", "Done" & @CRLF & "Hit OK to exit") Func Example() Local $Form1 = GUICreate("Clients status", 760, 400) ; ; Create the ListView $hGrid = GUICtrlCreateListView("HostName|IP|Info|Last reboot|CPU|Last logon|Current logon", 0, 0, 760, 400) _GUICtrlListView_SetColumnWidth($hGrid, 0, 140) ; HostName _GUICtrlListView_SetColumnWidth($hGrid, 1, 100) ; IP _GUICtrlListView_SetColumnWidth($hGrid, 2, 80) ; Ping info ms or Off or Unknown or Timeout) _GUICtrlListView_SetColumnWidth($hGrid, 3, 120) ; Last Reboot _GUICtrlListView_SetColumnWidth($hGrid, 4, 40) ; cpu load ; last 2 columns a mutually exclusive. If there is a user logged it's shown on the last column ; if there is not a user logged then the last user that was logged is shown on column 4 instead _GUICtrlListView_SetColumnWidth($hGrid, 5, 140) ; Last logged UserId (if nobody is logged now) _GUICtrlListView_SetColumnWidth($hGrid, 6, 140) ; Currently logged UserId _GUICtrlListView_SetExtendedListViewStyle($hGrid, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT)) ; show grid; select whole row ; GUISetState(@SW_SHOW) ; following line is needed if you have to refill the listview ; _GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($hGrid)) ; empty the listview ReDim $ahGrid[UBound($aIPList)] ; this loop will run all needed tasks at once. ; The results of the tasks will be managed by the callback functions (nearly) autonomously and asynchronously For $i = 0 To UBound($aIPList) - 1 $ahGrid[$i] = _GUICtrlListView_AddItem($hGrid, "") ; create a listview row ; ; ... for each client ... ; ; spawn ping commands. result will be send to the _PingParse() function _TaskRun($i, "Ping -a -n 1 -4 " & $aIPList[$i], "_PingParse") ; spawn commands to recall LoggedOn User. Result will be send to the _LoggedOn() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " computersystem get username /value", "_LoggedOn", 5) ; 5 = timeout in 5 seconds ; spawn commands to recall Last reboot time. result will be send to the _LastReboot() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " os get lastbootuptime /value", "_LastReboot", 5) ; spawn commands to recall % of CPU load. result will be send to the _CPU_load() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " cpu get loadpercentage /value", "_CPU_load", 7) Next ; now, while all tasks are running ; we could perform other activities in the meantime ; or we can wait the end of all the tasks Do Sleep(250) ; you could perform other jobs here while waiting for the tasks to finish Until Not _TasksCheckStatus() ; <-- this function performs management of running tasks ; and should be called as often as possible EndFunc ;==>Example ; below are CallBack functions ; #FUNCTION# ==================================================================================================================== ; Name ..........: _PingParse (a callback function) ; Description ...: this function analize the output of a ping command and "extract" needed infos ; it fills columns 0 1 and 2 of the list view ($aTarget[0] is the line number of the listview to be filled) ; Syntax ........: _PingParse($aTarget[, $bDomain = True]) ; Parameters ....: $sTarget - An array with Ping results passed by the MultiTasks as soon as any ping task ends ; the passed array contains following data: ; $aTarget[0] Index for this task ; $aTarget[1] StdOut from ping (the whole output of the ping) ; $aTarget[2] StdErr from ping ; $aTarget[3] Time spent by this task to complete (is NOT the ping roundtrip time) ; $bDomain - [optional] A binary value. Default is True. (keep domain info in host name) ; ; Return values .: None. It Fills Listview columns 0, 1 and 2 ; column 0 : resolved HostName or "" ; column 1 : IP address or "" (this can contain last known IP even if now is offline) ; column 2 : roundtrip time or "Unknown" or "timeout" or "Off" ; ; Author ........: Chimp ; =============================================================================================================================== Func _PingParse($aTarget, $bDomain = True) ; $aTarget contains 4 elements: [0] Index, [1] StdOut, [2] StdErr, [3] time spent by this task Local $sOutput = $aTarget[1] ; stdout Local $0, $1, $2, $3, $aMs ; Local $iAnswer = -1, $iName = -1 Local $aResult[3] = ["", "", ""] ; [0]ms, [1]HostName, [2]IP $aMs = StringRegExp($sOutput, "([0-9]*)ms", 3) If Not @error Then ; Ping replayed $aResult[0] = $aMs[UBound($aMs) - 1] ; average ms Else ; $aResult[0] = "off" EndIf $0 = StringInStr($sOutput, "Ping") $1 = StringInStr($sOutput, "[") ; HostName decoded? If $1 Then ; HostName decoded $2 = StringInStr($sOutput, "]") $3 = StringInStr($sOutput, " ", 0, -2, $1) $aResult[1] = StringMid($sOutput, $3 + 1, $1 - $3 - 1) ; HostName $aResult[2] = StringMid($sOutput, $1 + 1, $2 - $1 - 1) ; IP Else If $0 Then ; pinging an IP address? ; $aResult[1] = "" ; no HostName Local $aFindIP = StringRegExp($sOutput, "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", 3) If Not @error Then $aResult[2] = $aFindIP[0] Else ; unknown HostName $aResult[0] = "Unknown" $aResult[1] = $aIPList[$aTarget[0]] ; retrieve HostName from the $aIPList array EndIf EndIf If $bDomain = False Then Local $aSplit = StringSplit($aResult[1], ".", 2) ; 2 = $STR_NOCOUNT $aResult[1] = $aSplit[0] ; romove .domain part from the HostName EndIf If StringLeft($aTarget[2], 14) = "@error Timeout" Then $aResult[0] = "Timeout" ; Now that we have the infos, we compile related cells in ListView ; grid row-handle data column _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[1], 0) ; first column "HostName" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[2], 1) ; second column "IP address" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[0], 2) ; third column "Infos about the ping" ; ConsoleWrite("Debug: " & "-> " & $aResult[0] & @TAB & $aResult[1] & @TAB & $aResult[2] & @CRLF) EndFunc ;==>_PingParse Func _LastReboot($aParameters) ; Last reboot DateTime $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], WMIDateStringToDate(StringMid($aParameters[1], $equal + 1)), 3) ; column 3 EndIf EndFunc ;==>_LastReboot Func _CPU_load($aParameters) ; % of CPU load $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], StringMid($aParameters[1], $equal + 1), 4) ; column 4 EndIf EndFunc ;==>_CPU_load Func _LoggedOn($aParameters) ; User now logged $aParameters[1] = StringStripWS($aParameters[1], 8) ; if none is logged, then find the user that was last logged (also using _TaskRun) If $aParameters[1] = "" Or $aParameters[1] = "UserName=" Then ; following syntax is by @iamtheky (thanks) ; https://www.autoitscript.com/forum/topic/189845-regexp-pattern-in-findstr-dos-command/?do=findComment&comment=1363106 Local $sCmd = 'cmd /c FOR /F "usebackq skip=2 tokens=1-3" %A IN (`REG QUERY ' & '"\\' & $aIPList[$aParameters[0]] & _ '\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI" /REG:64 /v LastLoggedOnUser 2^>nul`) Do @echo %C' _TaskRun($aParameters[0], $sCmd, "_LastLogged", 5) ; find last logged user and, when ready, send result to _LastLogged() Else ; if someone is logged then write username to column 6 Local $aUser = StringSplit($aParameters[1], "=", 2) ; 2 = $STR_NOCOUNT _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aUser[UBound($aUser) - 1], 6) ; column 6 EndIf EndFunc ;==>_LoggedOn Func _LastLogged($aParameters) _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aParameters[1], 5) ; column 5 EndFunc ;==>_LastLogged Func WMIDateStringToDate($dtmDate) ; thanks to @kylomas ; https://www.autoitscript.com/forum/topic/169252-wmi-password-age-issue/?do=findComment&comment=1236082 ; reformat date to mm/dd/yyyy hh:mm:ss and zero fill single digit values Return StringRegExpReplace(StringRegExpReplace($dtmDate, '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*', '$2/$3/$1 $4:$5:$6'), '(?<!\d)(\d/)', '0$1') EndFunc ;==>WMIDateStringToDate1 point
-
jQuerify a web page (injecting jQuery)
Letraindusoir reacted to Gianni for a topic
as already specified in the script's comments: If you want to load jQuery from a disk file instead of directly download it from the web, use the following statement: $oScript.TextContent = FileRead(@ScriptDir & "\jquery.min.js") of course the jquery.min.js file must already be present in the same dir of the script1 point -
ArrayWorkshop is what I like to think of as the 'Leibnitzian' approach to multidimensional arrays: formulaic beyond 3 dimensions. Most of the functions allow you to target the dimension in which you want processing to be done. It shouldn't take too long to figure out how this works after you've run some examples. _ArrayAttach treats multidimensional arrays as if they were lego bricks - any array can be attached regardless of bounds or dimensions. #include-once ; #INDEX# ====================================================================================================================== ; Title .........: ArrayWorkshop [version 1.0.1] ; AutoIt Version : 3.3.14.2 ; Language ......: English ; Description ...: Multidimensional array functions. ; Notes .........: In this library, an array region is defined as follows: ; 1. items within a one dimensional array ; 2. lists within a two dimensional array (rows or columns) ; 3. tables within a three dimensional array ; 4. cuboidal areas within a four dimension array ; 5. four dimensional cuboids within a five dimensional array ; etc... ; To reduce bloat, several functions access part of the script referred to (in the comments) as 'Remote Loops'. ; Arrays containing zero elements are not supported and will cause functions to return an error. ; Limiting the number of dimensions to single digits was a practical decision to simplify syntax. ; Credit must go to fellow AutoIt forum members whose code or suggestions have been influential in the ; development of these functions: Jos van der Zande, jguinch, jchd, LazyCoder, Tylo, Ultima, Melba23, BrewManNH ; The above list is not exhaustive, nor are the names in any particular order. ; Author(s) .....: czardas ; ============================================================================================================================== ; #CURRENT# ==================================================================================================================== ; _ArrayAttach [limit = 9 dimensions] ; _ArraySortXD [limit = 9 dimensions] ; _ArrayTransform [limit = 9 dimensions] ; _ArrayUniqueXD [limit = 9 dimensions] ; _DeleteDimension [limit = 9 dimensions] ; _DeleteRegion [limit = 9 dimensions] ; _ExtractRegion [limit = 9 dimensions] ; _InsertRegion [limit = 9 dimensions] ; _PreDim [limit = 9 dimensions] ; _ReverseArray [limit = 9 dimensions] ; _SearchArray [limit = 9 dimensions] ; _ShuffleArray [limit = 9 dimensions] ; ============================================================================================================================== ; #INTERNAL_USE_ONLY#=========================================================================================================== ; __AcquireExponent, __CreateTrac, __ExtractVector, __FindExact, __FindExactCase, __FindString, __FindStringCase, __FindWord, ; __FindWordCase __FloodFunc, ___FloodXD, ___FormatNum, __GetBounds, __HiddenIndices, ___Search1D, ___NewArray, ___NumCompare, ; __QuickSort1D, __QuickSortXD, __ResetBounds, ___Reverse1D, __Separate1D, __Separate256, __SeparateXD, __Shuffle1D, ; __ShuffleXD , __TagSortSwap, __TagSortSwapXD ; ============================================================================================================================== #Au3Stripper_Off Global $g__ARRWSHOP_RESUME = True ; prevents certain processes from running when set to False [do not use in your script] Global Const $g__ARRWSHOP_SUB = ChrW(57344) ; [U+E000] ; #FUNCTION# =================================================================================================================== ; Name...........: _ArrayAttach ; Description ...: Joins two arrays together. ; Syntax.........: _ArrayAttach($aTarget, $aSource [, $iDimension = 1]) ; Parameters.....; $aTarget - [ByRef] Target array to which the source array (or data) will be concatenated. ; $aSource - The array to attach to the target. ; $iDimension - [Optional] Integer value - the dimension in which concatenation occurs. Default = 1st dimension ; Return values .: Success - Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 $aTarget is not a valid array. ; |@error = 2 Dimension limit exceeded. ; |@error = 3 Arrays must contain at least one element. ; |@error = 4 Output array size exceeds AutoIt limits. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; Extra dimensions are added to the target when: ; 1. the source array has more dimensions than the target [or] ; 2. the 3rd parameter ($iDimension) is greater than the number of dimensions available. ; ============================================================================================================================== Func _ArrayAttach(ByRef $aTarget, $aSource, $iDimension = 1) If Not IsArray($aTarget) Then Return SetError(1) ; the target must be an array Local $aBoundSrc[1] If Not IsArray($aSource) Then ; convert $aSource into an array $aBoundSrc[0] = $aSource ; use $aBoundSrc as a temporary array to preserve memory $aSource = $aBoundSrc ; conversion by assignment EndIf $aBoundSrc = __GetBounds($aSource, 9) ; get the bounds of the source array If @error Then Return SetError(3) ; $aSource must contain at least one element Local $aBoundTgt = __GetBounds($aTarget, 9) ; get the bounds of the target array If @error Then Return SetError(3) ; $aTarget must contain at least one element $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $aBoundTgt[0] > 9 Or $aBoundSrc[0] > 9 Or $iDimension > 9 Or $iDimension < 1 Then Return SetError(2) ; dimension limit exceeded Local $iDim = ($aBoundSrc[0] > $aBoundTgt[0]) ? $aBoundSrc[0] : $aBoundTgt[0] ; minimum number of dimensions needed for the output If $iDimension > $iDim Then $iDim = $iDimension ; the specified dimension may not yet exist Local $aBoundNew[$iDim + 1] ; output bounds $aBoundNew[0] = $iDim ; element 0 contains the number of dimensions For $i = 1 To $iDim ; get the minimum bounds within all dimensions If $i <> $iDimension Then $aBoundNew[$i] = ($aBoundSrc[$i] > $aBoundTgt[$i]) ? $aBoundSrc[$i] : $aBoundTgt[$i] Else ; expansion within the explicit dimension is determined differently $aBoundNew[$i] = $aBoundSrc[$i] + $aBoundTgt[$i] ; add the number of sub-indices together EndIf $aBoundSrc[$i] -= 1 ; convert to the final index value in each dimension of the source array Next Local $iCount = 1 ; check output bounds remain within range For $i = 1 To $aBoundNew[0] $iCount *= $aBoundNew[$i] If $iCount > 16777216 Then Return SetError(4) ; output array size exceeds AutoIt limits Next _PreDim($aTarget, $iDim) ; add more dimensions if needed __ResetBounds($aTarget, $aBoundNew) ; ReDim the target to make space for more elements If $iDim = 1 Then ; [do not send to remote loop region] For $iRegion = $aBoundTgt[$iDimension] To $aBoundNew[$iDimension] - 1 ; to the new bounds of the [only] dimension $aTarget[$iRegion] = $aSource[$iRegion - $aBoundTgt[$iDimension]] Next Return EndIf Local $sTransfer = '$aSource' & __HiddenIndices($aBoundSrc[0], $iDimension), _ ; to access elements at their original indices $iFrom, $aFloodFill = __FloodFunc() $aBoundSrc[$iDimension] = 0 ; whichever loop this relates to must only run once on each encounter For $iRegion = $aBoundTgt[$iDimension] To $aBoundNew[$iDimension] - 1 ; to the new bounds of the specified dimension $iFrom = $iRegion - $aBoundTgt[$iDimension] ; adjusted to begin transfer from source element 0 $aFloodFill[$iDim]($aTarget, $aBoundSrc, $iDimension, $iRegion, $iFrom, $aSource, $sTransfer) ; flood the region Next EndFunc ;==>_ArrayAttach ; #FUNCTION# =================================================================================================================== ; Name...........: _ArraySortXD ; Description ...: Sorts multidimensional arrays according to miscellaneous criteria. ; Syntax.........: _ArraySortXD($aArray [, $iDimension = 1 [, $iAlgorithm = 0 [, $iEnd = -1 [, $1 = 0 [, $2 = 0 ], etc... ]]]] ; Parameters.....; $aArray - [ByRef] The array to sort. ; $iDimension - [Optional] Integer value - the dimension in which sorting occurs. Default = 1st dimension ; $iAlgorithm - [Optional] Integer value - defines the sorting criteria. Default = lexical [see comments] ; $iEnd - [Optional] Integer value - final index to stop sorting (within the explicit dimension). Default = -1 ; $1, $2, $3, $4, $5, $6, $7, $8, $9 - [Optional] - start sub-indices within each dimension. [see comments] ; Return values .: Success - Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 The first parameter is not a valid array. ; |@error = 2 Array does not contain any elements. ; |@error = 3 Dimension does not exist. ; |@error = 4 Out of range start parameter detected. ; |@error = 5 Out of range $iEnd value detected. ; |@error = 6 Bad algorithm detected. ; Author ........: czardas ; Comments ......; The algorithm parameter is binary flag. You can combine any of the following values: ; $iAlgorithm = 0 - alphabetical (lexical - applies to all items including numbers) ; $iAlgorithm = 1 - descending (applies to sorted items only) ; $iAlgorithm = 2 - numeric (applies to numbers only) ; $iAlgorithm = 4 - alphanumeric (applies to all items - overrides flag 2) ; $iAlgorithm = 256 - sort decimal strings by magnitude (combine with flags 2 and 4) ; $iAlgorithm = 512 - maintain original sequence of non-numeric items (only applies to flag 2) ; You can combine the various flags using BitOR() or you can also add them together. ; When sorting with flag 2, numbers always appear before unsorted items. ; When sorting with flag 4, numbers appear before non-numeric items in ascending order. ; Flag 256 will be ignored if not combined with flags 2 or 4. ; Flag 512 will be ignored if not combined with flag 2. ; ----------------------------------------------------------- ; Optional start sub-indices; $1, $2, $3, $4, $5, $6, $7, $8 and $9; work as follows: ; In the explicit dimension the start index should be less than $iEnd. ; In all other dimensions start values also represent end values. [range = 1, not multi-regional within XD] ; This function works for arrays of up to nine dimensions. ; ============================================================================================================================== Func _ArraySortXD(ByRef $aArray, $iDimension = 1, $iAlgorithm = 0, $iEnd = -1, $1 = 0, $2 = 0, $3 = 0, $4 = 0, $5 = 0, $6 = 0, $7 = 0, $8 = 0, $9 = 0) If Not IsArray($aArray) Then Return SetError(1) ; $aArray must be an array Local $aBound = __GetBounds($aArray) ; get the bounds of the array If @error Then Return SetError(2) ; $aArray must contain more than zero elements $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension > $aBound[0] Or $iDimension < 1 Then Return SetError(3) ; dimension limit exceeded Local $aParam = [$aBound[0], $1, $2, $3, $4, $5, $6, $7, $8, $9] ; [$aParam can be oversized] For $i = 1 To $aBound[0] $aParam[$i] = ($aParam[$i] = Default) ? 0 : Int($aParam[$i]) If $aParam[$i] < 0 Or $aParam[$i] >= $aBound[$i] Then Return SetError(4) ; out of range start parameter was detected Next $iEnd = ($iEnd = -1 Or $iEnd = Default) ? $aBound[$iDimension] - 1 : Int($iEnd) Local $iStart = $aParam[$iDimension] If $iEnd <= $iStart Or $iEnd >= $aBound[$iDimension] Then Return SetError(5) ; meaningless $iEnd value $iAlgorithm = ($iAlgorithm = Default) ? 0 : Int($iAlgorithm) If BitAND(0xFFFFFCF8, $iAlgorithm) Then Return SetError(6) ; meaningless $iAlgorithm ; determine the type of algorithm Local $bNumeric = BitAND($iAlgorithm, 2) Or BitAND($iAlgorithm, 4), _ $bAlpha = BitAND($iAlgorithm, 4) Or Not $bNumeric, _ $bDecimals = $bNumeric And BitAND($iAlgorithm, 256), _ $bSequential = BitAND($iAlgorithm, 512) And BitAND($iAlgorithm, 2) And Not $bAlpha, _ $bReverse = Mod($iAlgorithm, 2) <> 0 Local $aTrac, $aVector, $iItems = 0 ; to count numeric elements [if needed] If $aBound[0] = 1 Then ; First deal with 1D arrays [optimized for algorithms 0, 2 and 4 without any other flags] If $bNumeric Then ; numeric [1D] If Not ($bDecimals Or $bSequential) Then ; tracking is not needed [conditional may require modification at some point to accomodate new algorithms] $iItems = __Separate1D($aArray, $iStart, $iEnd) ; place numbers before strings __QuickSort1D($aArray, $iStart, $iStart + $iItems - 1, 2) ; sort numbers numerically Else ; employ a track and trace mechanism $aTrac = __CreateTrac($aBound[$iDimension], $iStart, $iEnd) ; instead of sorting the array we will track migrating indices If $bDecimals Then $aVector = $aArray $iItems = __Separate256($aVector, $aTrac, $iStart, $iEnd) ; [numbers and decimal strings] __QuickSortXD($aVector, $aTrac, $iStart, $iStart + $iItems - 1, 256) ; pretend to sort the array but instead relocate indices in $aTrac $aVector = '' ; reduce memory usage ASAP Else ; [$bSequential = True] $iItems = __SeparateXD($aArray, $aTrac, $iStart, $iEnd) ; [numbers only] __QuickSortXD($aArray, $aTrac, $iStart, $iStart + $iItems - 1, 2) ; pretend to sort the array but instead relocate indices in $aTrac EndIf EndIf If $bAlpha Then If $bDecimals Then __QuickSortXD($aArray, $aTrac, $iStart + $iItems, $iEnd, 0) ; sort strings lexically Else __QuickSort1D($aArray, $iStart + $iItems, $iEnd, 0) ; sort strings lexically and finish If $bReverse Then ___Reverse1D($aArray, $iStart, $iEnd) ; descending Return ; finished EndIf ElseIf $bSequential Then __QuickSort1D($aTrac, $iStart + $iItems, $iEnd, 2) ; sort non-numeric element indices corrupted during separation ElseIf Not $bDecimals Then ; tracking was not used, so we are almost done here If $bReverse Then ___Reverse1D($aArray, $iStart, $iStart + $iItems - 1) ; descending Return ; finished EndIf If $bReverse Then ___Reverse1D($aTrac, $iStart, ($bAlpha ? $iEnd : $iStart + $iItems - 1)) ; reverse indices in $aTrac __TagSortSwap($aArray, $aTrac, $iStart, $iEnd) ; similar to the knight's tour problem Else ; lexical [1D] __QuickSort1D($aArray, $iStart, $iEnd, 0) If $bReverse Then ___Reverse1D($aArray, $iStart, $iEnd) ; descending EndIf Else ; multidimensional [XD] $aTrac = __CreateTrac($aBound[$iDimension], $iStart, $iEnd) ; used to track migrating indices $aVector = __ExtractVector($aArray, $iDimension, $aParam) ; first extract the vector to use for sorting (row or column etc...) ; now come similar arguments as for 1D If $bNumeric Then ; numeric [XD] $iItems = $bDecimals ? __Separate256($aVector, $aTrac, $iStart, $iEnd) : __SeparateXD($aVector, $aTrac, $iStart, $iEnd) __QuickSortXD($aVector, $aTrac, $iStart, $iStart + $iItems - 1, ($bDecimals ? 256 : 2)) ; relocate indices in $aTrac If $bAlpha Then __QuickSortXD($aVector, $aTrac, $iStart + $iItems, $iEnd, 0) ; sort strings lexically ElseIf $bSequential Then $aVector = '' ; free up memory __QuickSort1D($aTrac, $iStart + $iItems, $iEnd, 2) ; sort non-numeric element indices corrupted during separation EndIf $aVector = '' ; as above If $bReverse Then ___Reverse1D($aTrac, $iStart, ($bAlpha ? $iEnd : $iStart + $iItems - 1)) Else __QuickSortXD($aVector, $aTrac, $iStart, $iEnd, 0) ; relocate indices in $aTrac $aVector = '' ; as above If $bReverse Then ___Reverse1D($aTrac, $iStart, $iEnd) ; descending EndIf If $aBound[0] = 2 And $iDimension = 1 Then ; slightly more optimal method __TagSortSwapXD($aArray, $aTrac, $iStart, $iEnd) ; similar to the knight's tour problem Else $aParam = $aBound ; original parameters are no longer needed $aParam[$iDimension] = 1 Local $aRegion = ___NewArray($aParam) ; to store extracted regions For $i = 1 To $aParam[0] $aParam[$i] -= 1 Next Local $sIndices = __HiddenIndices($aBound[0], $iDimension), $fnFloodFill = __FloodFunc()[$aBound[0]], _ $iCurr, $iNext, $sTransfer = '$aSource' & $sIndices ; array syntax ; [now comes the knight's tour in 9 dimensions] ;) For $iInit = $iStart To $iEnd ; initialize each swap sequence If $aTrac[$iInit] <> $iInit Then ; regions will now be overwritten in accordance with tracking information $iCurr = $iInit ; set the current region as the start of the sequence $fnFloodFill($aRegion, $aParam, $iDimension, 0, $iInit, $aArray, $sTransfer) ; extract region $sTransfer = '$aTarget' & $sIndices ; array syntax Do $fnFloodFill($aArray, $aParam, $iDimension, $iCurr, $aTrac[$iCurr], '', $sTransfer) ; overwrite each region in the sequence $iNext = $aTrac[$iCurr] ; get the next index in the sequence $aTrac[$iCurr] = $iCurr ; set to ignore overwritten regions on subsequent encounters $iCurr = $iNext ; follow the trail as far as it goes [index could be higher or lower] Until $aTrac[$iCurr] = $iInit ; all sequences end at this juncture $sTransfer = '$aSource' & $sIndices ; now we know where to put the initial region we copied earlier $fnFloodFill($aArray, $aParam, $iDimension, $iCurr, 0, $aRegion, $sTransfer) $aTrac[$iCurr] = $iCurr ; set to ignore on subsequent encounters [as above] EndIf Next EndIf EndIf EndFunc ;==>_ArraySortXD ; #FUNCTION# =================================================================================================================== ; Name...........: _ArrayTransform ; Description ...: Alters the shape of a multidimensional array without losing data. ; Syntax.........: _ArrayTransform($aArray [, $sShape = Default]) ; Parameters.....; $aArray - The array to modify. ; $sShape - [Optional] String or integer value - output dimension bounds sequence [See Comments] ; Return values .: Success - Returns the transformed array [ByRef]. ; Failure sets @error as follows: ; |@error = 1 The 1st parameter is not a valid array. ; |@error = 2 Bad $sShape parameter. ; Author ........: czardas ; Comments ......: The shape parameter must be a sequence of unique digits: each refering to a different dimension. ; For a 2D array the default shape parameter is '21', and for 3D it is '321' etc... ; The default output sequence transposes the array by reversing the dimension bounds ==> [7][8] becomes [8][7]. ; If you want a different output shape, change the dimension bounds sequence. ; eg. when $sShape = '2431', [1][2][3][4] will become [2][4][3][1] ; This function is limited to arrays of between two and nine dimensions. ; ============================================================================================================================== Func _ArrayTransform(ByRef $aArray, $sShape = Default) ; [default shape = reverse sequence '987654321'] If Not IsArray($aArray) Then Return SetError(1) ; not a valid array. Local $aBound = __GetBounds($aArray) If @error Or $aBound[0] = 1 Or $aBound[0] > 9 Then Return SetError(1) If $sShape = Default Then $sShape = StringRight('987654321', $aBound[0]) If StringLen($sShape) <> $aBound[0] Or Not StringIsDigit($sShape) Then Return SetError(2) ; bad $sShape parameter Local $aTrac = StringSplit($sShape, ''), $sTransfer = '$aSource' For $i = 1 To $aBound[0] If Not StringInStr($sShape, $i) Then Return SetError(2) ; bad $sShape parameter [dimensions must already exist] $sTransfer &= '[$a[' & $aTrac[$i] & ']]' ; default ==> '$aSource[$a[9]][$a[8]][$a[7]][$a[6]][$a[5]] etc...' Next Local $aBoundNew = $aBound __TagSortSwap($aBoundNew, $aTrac, 1, $aBoundNew[0]) Local $aNewArray = ___NewArray($aBoundNew), $fnFloodFill = __FloodFunc()[$aBound[0]] For $i = 1 To $aBoundNew[0] $aBoundNew[$i] -= 1 Next $fnFloodFill($aNewArray, $aBoundNew, 0, 0, '', $aArray, $sTransfer) $aArray = $aNewArray EndFunc ;==>_ArrayTransform ; #FUNCTION# =================================================================================================================== ; Name...........: _ArrayUniqueXD ; Description ...: Removes duplicate items from an array (or duplicate regions from a multidimensional array). ; Syntax.........: _ArrayUniqueXD($aArray [, $bCaseSense = False [, $iDimension = 1]]) ; Parameters.....; $aArray - The array containing duplicate regions to be removed. ; $bCaseSense - [Optional] Set to true for case sensitive matches. Default value = False ; $iDimension - [Optional] Integer value - the dimension to which uniqueness applies. Default = 1st dimension ; Return values .: Success - Returns the unique array [ByRef]. ; Failure sets @error as follows: ; |@error = 1 The 1st parameter is not an array. ; |@error = 2 The 1st parameter does not contain any elements. ; |@error = 3 The 3rd parameter is not a valid dimension. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; All elements within a region must be duplicated (in juxtaposed positions) before removal takes place. ; Integers of the same magnitude are treated as duplicates regardless of data type. ; This function does not remove regions containing objects, DLLStructs or other arrays. ; Example .......; _ArrayUniqueXD($aArray, Default, 2) ; [$iDimension = 2] ; ==> deletes duplicate COLUMNS from a 2D array ; ============================================================================================================================== Func _ArrayUniqueXD(ByRef $aArray, $bCaseSense = Default, $iDimension = 1) If Not IsArray($aArray) Or UBound($aArray, 0) > 9 Then Return SetError(1) ; not a valid array Local $aBound = __GetBounds($aArray, 9) If @error Then Return SetError(2) ; array contains zero elements $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension < 1 Or $iDimension > $aBound[0] Then Return SetError(3) ; dimension does not exist If $aBound[$iDimension] < 2 Then Return ; the array is already unique For $i = 1 To 9 $aBound[$i] -= 1 ; set the max index in each dimension Next Local $sExpression = '$aArray' ; to access elements at their original indices For $i = 1 To $aBound[0] If $i <> $iDimension Then $sExpression &= '[$' & $i & ']' ; default expression = '$aArray[$iFrom][$2][$3][$4][$5] etc...' Else $sExpression &= '[$iFrom]' EndIf Next Local $sTransfer = '$aTarget' & __HiddenIndices($aBound[0], $iDimension), _ $fnFloodFill = __FloodFunc()[$aBound[0]], $iDim = $aBound[0], _ ; preserve this value $iItems = 0, $aFunction[1] = [0], $vElement, $iInt, $sName $aBound[0] = $aBound[$iDimension] ; the first loop will run to the bounds of the specified dimension $aBound[$iDimension] = 0 ; whichever loop this relates to must only run once on each encounter If $bCaseSense = Default Then $bCaseSense = False Local $oDictionary = ObjCreate("Scripting.Dictionary") For $iFrom = 0 To $aBound[0] $sName = '' ; clear previous name For $9 = 0 To $aBound[9] For $8 = 0 To $aBound[8] For $7 = 0 To $aBound[7] For $6 = 0 To $aBound[6] For $5 = 0 To $aBound[5] For $4 = 0 To $aBound[4] For $3 = 0 To $aBound[3] For $2 = 0 To $aBound[2] For $1 = 0 To $aBound[1] $vElement = Execute($sExpression) ; get the data contained in each element ; use non-hexadecimal characters (other than x) as datatype identifiers and convert the data to hexadecimal where necessary Switch VarGetType($vElement) Case 'String' If Not $bCaseSense Then $vElement = StringUpper($vElement) ; generates a case insensitive name segment $sName &= 's' & StringToBinary($vElement, 4) ; UTF8 [$SB_UTF8] Case 'Int32', 'Int64' ; use decimal without conversion ; the minus sign of a negative integer is replaced with 'g': to distinguish it from the positive value $sName &= ($vElement < 0) ? 'g' & StringTrimLeft($vElement, 1) : 'i' & $vElement Case 'Double' ; may be an integer $iInt = Int($vElement) $sName &= ($vElement = $iInt) ? (($iInt < 0) ? 'g' & StringTrimLeft($iInt, 1) : 'i' & $iInt) : 'h' & Hex($vElement) Case 'Bool' ; True or False $sName &= ($vElement = True) ? 't' : 'v' Case 'Binary' $sName &= 'y' & $vElement Case 'Ptr' $sName &= 'p' & $vElement Case 'Keyword' ; Default or Null (other variable declarations are illegal) $sName &= ($vElement = Default) ? 'w' : 'n' Case 'Function', 'UserFunction' ; string conversion will fail For $k = 1 To $aFunction[0] ; unique functions are stored in a separate array If $vElement = $aFunction[$k] Then ; this function has been encountered previously $sName &= 'u' & $k ContinueLoop 2 EndIf Next $aFunction[0] += 1 If $aFunction[0] > UBound($aFunction) - 1 Then ReDim $aFunction[$aFunction[0] + 10] $aFunction[$aFunction[0]] = $vElement $sName &= 'u' & $aFunction[0] Case Else ; Array, Object, DLLStruct [or Map] $sName = False ; set to ignore ExitLoop 9 EndSwitch Next Next Next Next Next Next Next Next Next If $sName Then If $oDictionary.Exists($sName) Then ContinueLoop ; this region has been seen previously $oDictionary.Item($sName) ; use $sName as key EndIf ; overwrite the next region (assumes that the first duplicate region will be found quite quickly) If $iDim = 1 Then $aArray[$iItems] = $aArray[$iFrom] Else $fnFloodFill($aArray, $aBound, $iDimension, $iItems, $iFrom, '', $sTransfer) ; access the remote loop region EndIf $iItems += 1 Next $aBound[0] = $iDim ; reset the number of dimensions For $i = 1 To $aBound[0] ; reset the bounds $aBound[$i] += 1 Next $aBound[$iDimension] = $iItems ; new bounds of the explicit dimension __ResetBounds($aArray, $aBound) ; remove the remaining duplicate array regions EndFunc ;==>_ArrayUniqueXD ; #FUNCTION# =================================================================================================================== ; Name...........: _DeleteDimension ; Description ...: Delete dimensions, or a range of dimensions, from a multidimensional array. ; Syntax.........: _DeleteDimension(ByRef $aArray, $iDimension, $iRange = 1) ; Parameters.....; $aArray - [ByRef] The array from which dimensions are deleted. ; $iDimension - Integer value - the dimension (or the first dimension) to delete. ; $iRange - [Optional] Integer value - the number of dimensions to delete. Default = 1 ; Return values .: Success - Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 $aArray is not a valid array. ; |@error = 2 Dimension limit exceeded. ; |@error = 3 Arrays must contain at least one element. ; |@error = 4 Meaningless range value [range must be greater than zero]. ; |@error = 5 Illegal operation - deleting all dimensions is not supported. ; Author ........: czardas ; Comments ......; Beware - deleting dimensions can have very destructive effect on the array's contents. ; Deletes all regions with indices greater than zero while removing each dimension. ; This function will not delete all the dimensions from an array. ; This function is limited to arrays of up to nine dimensions. ; ============================================================================================================================== Func _DeleteDimension(ByRef $aArray, $iDimension, $iRange = 1) If Not IsArray($aArray) Then Return SetError(1) ; this parameter must be an array Local $aBound = __GetBounds($aArray) If @error Then Return SetError(3) ; arrays must contain at least one element If $aBound[0] > 9 Then Return SetError(2) $iDimension = Int($iDimension) If $iDimension < 1 Or $iDimension > $aBound[0] Then Return SetError(2) ; check for dimension range overflow and set to ignore [subject to review] $iRange = ($iRange = Default) ? 1 : Int($iRange) If $iRange < 1 Then Return SetError(4) ; range must be greater than zero If $iDimension + $iRange - 1 >= $aBound[0] Then $iRange = 1 + $aBound[0] - $iDimension If $iRange = $aBound[0] Then Return SetError(5) ; illegal operation [deleting all dimensions is not an intended feature] Local $sTransfer = '$aSource', $iCount = 1, $aBoundNew[$aBound[0] - $iRange + 1] $aBoundNew[0] = $aBound[0] - $iRange For $i = 1 To $aBound[0] If $i < $iDimension Or $i > $iDimension + $iRange - 1 Then ; dimensions to keep $aBoundNew[$iCount] = $aBound[$i] ; assign the bounds of the new array $sTransfer &= '[$a[' & $iCount & ']]' $iCount += 1 Else ; dimensions to delete For $i = $iDimension To $iDimension + $iRange - 1 ; run through the range $sTransfer &= '[0]' ; delete / hide dimensions from the fill instructions Next $i -= 1 ; prevents incrementing the loop iteration count twice EndIf Next Local $aNewArray = ___NewArray($aBoundNew) For $i = 1 To $aBoundNew[0] $aBoundNew[$i] -= 1 ; maximum sub-index within each dimension Next Local $aFloodFill = __FloodFunc() $aFloodFill[$aBoundNew[0]]($aNewArray, $aBoundNew, 0, 0, '', $aArray, $sTransfer) ; flood the new array $aArray = $aNewArray EndFunc ;==>_DeleteDimension ; #FUNCTION# =================================================================================================================== ; Name...........: _DeleteRegion ; Description ...: Deletes a region from a multidimensional array. ; Syntax.........: _DeleteRegion($aArray, $iDimension [, $iSubIndex = 0 [, $iRange = 1]]) ; Parameters.....; $aArray - [ByRef] The array to delete the region from. ; $iDimension - [Optional] Integer value - the dimension used to define the region. Default = 1 ; $iSubIndex - [Optional] Integer value - the index, or start of, of the region to delete. Default = 0 ; $iRange - [Optional] Integer value - the size of the region to delete. Default = 1 ; Return values .: Success - Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 $aArray is not a valid array. ; |@error = 2 Dimension limit exceeded. ; |@error = 3 Dimension does not exist in the array. ; |@error = 4 Arrays must contain at least one element. ; |@error = 5 Sub-index does not exist in the dimension. ; |@error = 6 Invalid Range. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; ============================================================================================================================== Func _DeleteRegion(ByRef $aArray, $iDimension = 1, $iSubIndex = 0, $iRange = 1) If Not IsArray($aArray) Then Return SetError(1) Local $aBound = __GetBounds($aArray) ; get the bounds of each dimension If @error Then Return SetError(4) ; $aArray must contain at least one element If $aBound[0] > 9 Then Return SetError(2) ; nine dimension limit $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension > $aBound[0] Or $iDimension < 1 Then Return SetError(3) ; out of bounds dimension $iSubIndex = ($iSubIndex = Default) ? 0 : Int($iSubIndex) If $iSubIndex < 0 Or $iSubIndex > $aBound[$iDimension] - 1 Then Return SetError(5) ; sub-index does not exist in the dimension $iRange = ($iRange = Default) ? 1 : Int($iRange) If $iRange < 1 Then Return SetError(6) ; range must be greater than zero $iRange = ($iSubIndex + $iRange < $aBound[$iDimension]) ? $iRange : $aBound[$iDimension] - $iSubIndex ; corrects for overflow If $iRange = $aBound[$iDimension] Then Return SetError(6) ; deleting the whole region is not currently supported [give reason] $aBound[$iDimension] -= $iRange ; the size of the dimension in the new array If $aBound[0] = 1 Then For $iNext = $iSubIndex To $aBound[$iDimension] - 1 $aArray[$iNext] = $aArray[$iNext + $iRange] Next ReDim $aArray[$aBound[$iDimension]] Return EndIf Local $iMaxIndex = $aBound[$iDimension] - 1 For $i = 1 To $aBound[0] $aBound[$i] -= 1 Next $aBound[$iDimension] = 0 ; set to loop once [one region at a time] Local $iFrom, $sTransfer = '$aTarget' & __HiddenIndices($aBound[0], $iDimension), $fnFloodFill = __FloodFunc()[$aBound[0]] For $iNext = $iSubIndex To $iMaxIndex $iFrom = $iNext + $iRange $fnFloodFill($aArray, $aBound, $iDimension, $iNext, $iFrom, '', $sTransfer) ; overwrite the final [untouched] region Next $aBound[$iDimension] = $iMaxIndex For $i = 1 To $aBound[0] $aBound[$i] += 1 Next __ResetBounds($aArray, $aBound) ; delete remaining indices EndFunc ;==>_DeleteRegion ; #FUNCTION# =================================================================================================================== ; Name...........: _ExtractRegion ; Description ...: Extracts a region from a multidimensional array. ; Syntax.........: _ExtractRegion($aArray, $iDimension [, $iSubIndex = 0 [, $iRange = 1]]) ; Parameters.....; $aArray - The array from which to extract the region. ; $iDimension - Integer value - the dimension used to define the region. ; $iSubIndex - [Optional] Integer value - the index, or start of, of the region to extract. Default = 0 ; $iRange - [Optional] Integer value - the number of regions to extract. Default = 1 ; Return values .: Success - Returns a new array containing all the extracted regions within the defined range. ; Failure sets @error as follows: ; |@error = 1 $aArray is not a valid array. ; |@error = 2 Dimension limit exceeded. ; |@error = 3 Dimension does not exist in the array. ; |@error = 4 Arrays must contain at least one element. ; |@error = 5 Sub-index does not exist in the dimension. ; |range must be greater than zero ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; The extracted region will contain the same number of dimensions as the original array. ; ============================================================================================================================== Func _ExtractRegion($aArray, $iDimension, $iSubIndex = 0, $iRange = 1) If Not IsArray($aArray) Then Return SetError(1) Local $aBound = __GetBounds($aArray) ; get the bounds of each dimension If @error Then Return SetError(4) ; $aArray must contain at least one element If $aBound[0] > 9 Then Return SetError(2) ; nine dimension limit $iDimension = Int($iDimension) If $iDimension > $aBound[0] Or $iDimension < 1 Then Return SetError(3) ; out of bounds dimension $iSubIndex = ($iSubIndex = Default) ? 0 : Int($iSubIndex) If $iSubIndex < 0 Or $iSubIndex > $aBound[$iDimension] - 1 Then Return SetError(5) ; sub-index does not exist in the dimension $iRange = ($iRange = Default) ? 1 : Int($iRange) If $iRange < 1 Then Return SetError(6) ; range must be greater than zero $iRange = ($iSubIndex + $iRange < $aBound[$iDimension]) ? $iRange : $aBound[$iDimension] - $iSubIndex $aBound[$iDimension] = $iRange ; the size of the dimension in the new array Local $aRegion = ___NewArray($aBound) ; create new array For $i = 1 To $aBound[0] $aBound[$i] -= 1 Next If $aBound[0] = 1 Then For $iNext = 0 To $iRange - 1 $aRegion[$iNext] = $aArray[$iNext + $iSubIndex] Next Return $aRegion EndIf $aBound[$iDimension] = 0 ; set to loop once [one region at a time] Local $iFrom, $sTransfer = '$aSource' & __HiddenIndices($aBound[0], $iDimension), $fnFloodFill = __FloodFunc()[$aBound[0]] For $iNext = 0 To $iRange - 1 $iFrom = $iNext + $iSubIndex $fnFloodFill($aRegion, $aBound, $iDimension, $iNext, $iFrom, $aArray, $sTransfer) ; extract region Next Return $aRegion EndFunc ;==>_ExtractRegion ; #FUNCTION# =================================================================================================================== ; Name...........: _InsertRegion ; Description ...: Inserts a multidimensional array into another multidimensional array. ; Syntax.........: _InsertRegion($aTarget, $aSource [, $iDimension = 1 [, $iSubIndex = 0]]) ; Parameters.....; $aTarget - The array to modify. ; $aSource - The array to insert. ; $iDimension - Integer value - the dimension in which insertion takes place. ; $iSubIndex - [Optional] Integer value - sub-index within the dimension where insertion occurs. Default = 0 ; Return values .: Success - Returns the target array ByRef. ; Failure sets @error as follows: ; |@error = 1 $aTarget is not a valid array. ; |@error = 2 $aSource is not a valid array. ; |@error = 3 Arrays must contain at least one element. ; |@error = 4 Array bounds do not match. ; |@error = 5 Output array size exceeds AutoIt limits. ; |@error = 6 $iSubIndex is out of range. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; The target array must contain the same number of dimensions as the source array. ; With the exception of $iDimension; the bounds of all other dimensions, in both arrays, must match. ; ============================================================================================================================== Func _InsertRegion(ByRef $aTarget, $aSource, $iDimension = 1, $iSubIndex = 0) If Not IsArray($aTarget) Or UBound($aTarget, 0) > 9 Then Return SetError(1) If Not IsArray($aSource) Or UBound($aSource, 0) > 9 Then Return SetError(2) Local $aBoundTgt = __GetBounds($aTarget) If @error Then Return SetError(3) ; arrays must contain at least one element Local $aBoundSrc = __GetBounds($aSource) If @error Then Return SetError(3) ; arrays must contain at least one element $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) For $i = 1 To $aBoundTgt[0] If $aBoundTgt[$i] <> $aBoundSrc[$i] And $iDimension <> $i Then Return SetError(4) ; array bounds are inconsistent Next $iSubIndex = ($iSubIndex = Default) ? 0 : Int($iSubIndex) If $iSubIndex < 0 Or $iSubIndex > $aBoundTgt[$iDimension] Then Return SetError(5) ; $iSubIndex is out of range Switch $iSubIndex Case 0 _ArrayAttach($aSource, $aTarget, $iDimension) If @error Then Return SetError(6) ; output array size exceeds AutoIt limits $aTarget = $aSource Case $aBoundTgt[$iDimension] _ArrayAttach($aTarget, $aSource, $iDimension) If @error Then Return SetError(6) ; output array size exceeds AutoIt limits Case Else ; check output bounds remain within range before modifications begin $aBoundSrc[$iDimension] += $aBoundTgt[$iDimension] Local $iCount = 1 For $i = 1 To $aBoundSrc[0] $iCount *= $aBoundSrc[$i] If $iCount > 16777216 Then Return SetError(6) ; output array size exceeds AutoIt limits Next Local $aEnd = _ExtractRegion($aTarget, $iDimension, $iSubIndex, 16777216) ; out of bounds range values extract all remaining sub-indices within the dimension If @error Then Return SetError(@error) $aBoundTgt[$iDimension] = $iSubIndex __ResetBounds($aTarget, $aBoundTgt) _ArrayAttach($aTarget, $aSource, $iDimension) _ArrayAttach($aTarget, $aEnd, $iDimension) EndSwitch EndFunc ;==>_InsertRegion ; #FUNCTION# =================================================================================================================== ; Name...........: _PreDim ; Description ...: Changes the size of an array by adding, or removing, dimensions. ; Syntax.........: _PreDim($aArray, $iDimensions [, $iPush = False]) ; Parameters.....; $aArray - The original array. ; $iDimensions - The number of dimensions in the returned array. ; $bPush - [Optional] If set to True, new dimensions are created on, or removed from, the left [see comments]. ; Return values .: Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 The first parameter is not an array. ; |@error = 2 The requested array has more than 9 dimensions. ; |@error = 3 The original array has more than 9 dimensions. ; |@error = 4 Arrays must contain at least one element. ; Author.........: czardas ; Comments ......; This function works for up to 9 dimensions. ; By default, new dimensions are added to the right in a standard sequence: $aArray[7][6] ==> $aArray[7][6][1] ; Dimensions are removed in reverse sequence: $aArray[7][6] ==> $aArray[7] ; When the $bPush parameter is set to True, the original array will be pushed to higher dimensions: ; $aArray[7][6] ==> $aArray[1][7][6], or the process reversed: $aArray[7][6] ==> $aArray[6] ; ============================================================================================================================== Func _PreDim(ByRef $aArray, $iDimensions, $bPush = False) If Not IsArray($aArray) Then Return SetError(1) $iDimensions = Int($iDimensions) If $iDimensions < 1 Or $iDimensions > 9 Then Return SetError(2) Local $iPreDims = UBound($aArray, 0) ; current number of dimensions If $iPreDims = $iDimensions Then Return ; no change If $iPreDims > 9 Then Return SetError(3) ; too many dimensions Local $aBound = __GetBounds($aArray) ; get the size of each original dimension If @error Then Return SetError(4) ; $aArray must contain at least one element $aBound[0] = $iDimensions ; overwrite this value with the new number of dimensions Local $sTransfer = '[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]][$a[7]][$a[8]][$a[9]]' ; array syntax to be sent to the remote loop region If $bPush Then ; prefix dimensions, or delete from the left Local $iOffset = Abs($iDimensions - $iPreDims) If $iPreDims > $iDimensions Then ; lower dimensions get deleted For $i = 1 To $iDimensions ; shift elements to lower indices $aBound[$i] = $aBound[$i + $iOffset] Next $sTransfer = '$aSource' & StringLeft('[0][0][0][0][0][0][0][0]', $iOffset * 3) & StringLeft($sTransfer, $iDimensions * 7) Else ; lower dimensions are created ReDim $aBound[$iDimensions + 1] ; make space for more dimensions For $i = $iDimensions To $iOffset + 1 Step -1 ; shift elements to higher indices $aBound[$i] = $aBound[$i - $iOffset] Next For $i = 1 To $iOffset ; assign the size of each additional dimension [1][1][1]... etc... $aBound[$i] = 1 Next $sTransfer = '$aSource' & StringMid($sTransfer, 1 + $iOffset * 7, $iPreDims * 7) EndIf Else ; Default behaviour = append dimensions, or delete from the right ReDim $aBound[$iDimensions + 1] ; modify the number of dimensions [according to the new array] For $i = $iPreDims + 1 To $iDimensions ; assign the size of each new dimension ...[1][1][1] etc... $aBound[$i] = 1 Next $sTransfer = '$aSource' & StringLeft($sTransfer, $iPreDims * 7) EndIf ; add or remove dimensions Local $aNewArray = ___NewArray($aBound) For $i = 1 To $iDimensions $aBound[$i] -= 1 ; convert elements to the maximum index value within each dimension Next ; access the remote loop region Local $iSubIndex = 0, $aFloodFill = __FloodFunc() $aFloodFill[$iDimensions]($aNewArray, $aBound, 0, $iSubIndex, '', $aArray, $sTransfer) $aArray = $aNewArray EndFunc ;==>_PreDim ; #FUNCTION# =================================================================================================================== ; Name...........: _ReverseArray ; Description ...: Reverses regions within a multidimensional array. ; Syntax.........: _ReverseArray($aArray [, $iDimension = 1 [, $iStart = 0 [, $iEnd = -1]]]) ; Parameters.....; $aArray - The array to modify. ; $iDimension - [Optional] Integer value - the dimension in which the array sub-indices (regions) are reversed. ; $iStart - [Optional] The start sub-index within the defined dimension. Default value = 0 ; $iEnd - [Optional] Integer value - The end sub-index within the defined dimension. Default value = -1 ; Return values .: Success - Returns the reversed array [ByRef]. ; Failure sets @error as follows: ; |@error = 1 The 1st parameter is not a valid array. ; |@error = 2 The 1st parameter does not contain any elements. ; |@error = 3 The dimension does not exist. ; |@error = 4 Meaningless $iStart value. ; |@error = 5 Meaningless $iEnd value. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; ============================================================================================================================== Func _ReverseArray(ByRef $aArray, $iDimension = 1, $iStart = 0, $iEnd = -1) If Not IsArray($aArray) Or UBound($aArray, 0) > 9 Then Return SetError(1) ; not a valid array Local $aBound = __GetBounds($aArray) If @error Then Return SetError(2) ; array contains zero elements $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension < 1 Or $iDimension > $aBound[0] Then Return SetError(3) ; dimension does not exist $iStart = ($iStart = Default) ? 0 : Int($iStart) If $iStart < 0 Or $iStart > $aBound[$iDimension] - 2 Then Return SetError(4) ; meaningless $iStart value $iEnd = ($iEnd = -1 Or $iEnd = Default) ? $aBound[$iDimension] - 1 : Int($iEnd) If $iEnd <= $iStart Or $iEnd >= $aBound[$iDimension] Then Return SetError(5) ; meaningless $iEnd value If $aBound[0] = 1 Then ___Reverse1D($aArray, $iStart, $iEnd) Else $aBound[$iDimension] = 1 Local $aRegion = ___NewArray($aBound) ; to store extracted regions For $i = 1 To $aBound[0] $aBound[$i] -= 1 Next Local $sIndices = __HiddenIndices($aBound[0], $iDimension), $fnFloodFill = __FloodFunc()[$aBound[0]], _ $sTransfer = '$aSource' & $sIndices ; array syntax While $iEnd > $iStart $fnFloodFill($aRegion, $aBound, $iDimension, 0, $iStart, $aArray, $sTransfer) ; extract the current start region $sTransfer = '$aTarget' & $sIndices $fnFloodFill($aArray, $aBound, $iDimension, $iStart, $iEnd, '', $sTransfer) ; overwrite the current start region $sTransfer = '$aSource' & $sIndices $fnFloodFill($aArray, $aBound, $iDimension, $iEnd, 0, $aRegion, $sTransfer) ; overwrite the current end region $iStart += 1 $iEnd -= 1 WEnd EndIf EndFunc ;==>_ReverseArray ; #FUNCTION# =================================================================================================================== ; Name...........: _SearchArray ; Description ...: Searches through a multidimensional array. ; Syntax.........: _SearchArray($aArray, $vSearchTerm [, $bCaseSense = False [, $iDimension = 1]]) ; Parameters.....; $aArray - [ByRef] Target array to which the source array (or data) will be concatenated. ; $vSearchTerm - The string or data to search for. ; $bCaseSense - [Optional] Boolean value - case sensitive search. Default = False ; $iDimension - [Optional] Integer value - the dimension used to conduct the search. Default = 1st dimension ; $iAlgo - [Optional] Algorithm: 1 = exact match, 2 = find a string, 3 = find a word within text. [Default = 1] ; Return values .: Success - Returns an array of all regions containing the term searched for within the defined dimension. ; Failure sets @error as follows: ; |@error = 1 $aArray is not a valid array. ; |@error = 2 Arrays must contain a minimum of one element. ; |@error = 3 Dimension limit exceeded. ; |@error = 4 Bad algorithm. ; |@error = 5 No matches found. ; Author ........: czardas ; Comments ......; This function is limited to arrays of up to nine dimensions. ; Searching through a 2D array in the first dimension returns an array of all rows containing the search term. ; Searching a 2D array in the second dimension will return an array of all columns containing the search term. ; Searching any array will return an array of regions containing the search term within the defined dimension. ; [$iAlgo = 3] A word may contain any characters. Word boundaries are defined by non-alphanumeric characters. ; May return FP matches if $iAlgo = 3, $vSearchTerm contains '\E' and $aArray contains the code point U+E000. ; ============================================================================================================================== Func _SearchArray($aArray, $vSearchTerm, $bCaseSense = False, $iDimension = 1, $iAlgo = 1) ; [Exact Match] If Not IsArray($aArray) Then Return SetError(1) ; this parameter must be an array Local $aBound = __GetBounds($aArray) If @error Then Return SetError(2) ; arrays must contain at least one element If $aBound[0] > 9 Then Return SetError(3) ; dimension range exceeded $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension < 1 Or $iDimension > $aBound[0] Then Return SetError(3) $iAlgo = ($iAlgo = Default) ? 1 : Int($iAlgo) If $iAlgo < 1 Or $iAlgo > 3 Then Return SetError(4) If $aBound[0] = 1 Then ; 1D special case $aArray = ___Search1D($aArray, $vSearchTerm, $bCaseSense, $iAlgo) Return @error ? SetError(5) : $aArray EndIf For $i = 1 To $aBound[0] $aBound[$i] -= 1 ; maximum index value within each dimension Next Local $iItems = 0, $iMaxIndex = $aBound[$iDimension], $fnFloodFill = __FloodFunc()[$aBound[0]], $aFunc = ['__FindExact', '__FindString', '__FindWord'], _ $sTransfer = $aFunc[$iAlgo - 1] & ($bCaseSense ? 'Case' : '') & '($aTarget, $a, $aBound, $iFrom, "$aTarget' & _ __HiddenIndices($aBound[0], $iDimension) & '")' ; string to execute $aBound[0] = $vSearchTerm $aBound[$iDimension] = 0 ; this loop must only run once on each encounter For $iFrom = 0 To $iMaxIndex ; loop through sub-indices within the dimension $fnFloodFill($aArray, $aBound, $iDimension, $iItems, $iFrom, '', $sTransfer) ; overwrite the current region while searching If Not $g__ARRWSHOP_RESUME Then ; a match halted the search during the overwrite $iItems += 1 ; set to overwrite the next region $g__ARRWSHOP_RESUME = True ; resume searching on the next pass EndIf Next If $iItems = 0 Then Return SetError(5) ; no matches found $aBound[0] = UBound($aBound) - 1 ; reset all bounds For $i = 1 To $aBound[0] $aBound[$i] += 1 Next $aBound[$iDimension] = $iItems __ResetBounds($aArray, $aBound) ; remove mismatches Return $aArray EndFunc ;==>_SearchArray ; #FUNCTION# =================================================================================================================== ; Name...........: _ShuffleArray ; Description ...: Shuffles multidimensional regions within an array, or items within multidimensional regions. ; Syntax.........: _ShuffleArray($aArray [, $iDimension = 1 [, $bFruitMachineStyle = False]]) ; Parameters.....; $aArray - The original array. ; $iDimension - [Optional] - The dimension used to define the regions to be shuffled. Default = 1 ; $bFruitMachineStyle - [Optional] Shuffle all items within regions defined by the dimension. Default = False ; Return values .: Returns the modified array ByRef. ; Failure sets @error as follows: ; |@error = 1 The first parameter is not a valid array. ; |@error = 2 The first parameter contains the wrong number of dimensions. ; |@error = 3 The second parameter does not relate to any of the dimensions available. ; |@error = 4 Arrays must contain at least one element. ; Author.........: czardas ; Comments ......; This function works for arrays of up to 9 dimensions. ; Setting $iDimension = 0 overrides the 3rd parameter and shuffles everything - anywhere within the array. ; Example .......; _ShuffleArray($aArray, 2, True) ; ==> This is a fruit machine! ; ============================================================================================================================== Func _ShuffleArray(ByRef $aArray, $iDimension = 1, $bFruitMachineStyle = False) If Not IsArray($aArray) Then Return SetError(1) Local $aBound = __GetBounds($aArray) ; get the bounds of each dimension If @error Then Return SetError(4) ; $aArray must contain at least one element If $aBound[0] > 9 Then Return SetError(2) ; nine dimension limit $iDimension = ($iDimension = Default) ? 1 : Int($iDimension) If $iDimension > $aBound[0] Or $iDimension < 0 Then Return SetError(3) ; out of bounds dimension Local $aTemp = $aBound ; regional bounds For $i = 1 To $aBound[0] $aBound[$i] -= 1 Next Local $iSubIndex, $sTransfer, $aFloodFill = __FloodFunc() If $iDimension > 0 Then ; shuffle regions or elements within regions If $aBound[0] > 1 Then $aTemp[$iDimension] = 1 Local $aRegion = ___NewArray($aTemp) ; to store extracted regions Local $sIndices = __HiddenIndices($aBound[0], $iDimension) $aTemp = $aBound $aTemp[$iDimension] = 0 ; set to loop once [one region at a time] If $bFruitMachineStyle Then ; contents will be shuffled within each region $sTransfer = '$aSource' & $sIndices ; array syntax For $iSubIndex = 0 To $aBound[$iDimension] ; loop through all indices within the dimension $aFloodFill[$aBound[0]]($aRegion, $aTemp, $iDimension, 0, $iSubIndex, $aArray, $sTransfer) ; extract region __ShuffleXD($aRegion, $aTemp) ; shuffle the extracted region $aFloodFill[$aBound[0]]($aArray, $aTemp, $iDimension, $iSubIndex, 0, $aRegion, $sTransfer) ; reinsert the shuffled region Next Else ; regions will be shuffled within the dimension Local $iRandom $sTransfer = '$aSource' & $sIndices For $iSubIndex = 0 To $aBound[$iDimension] $aFloodFill[$aBound[0]]($aRegion, $aTemp, $iDimension, 0, $iSubIndex, $aArray, $sTransfer) ; extract each region $sTransfer = '$aTarget' & $sIndices $iRandom = Random(0, $aBound[$iDimension], 1) ; acquire a random index $aFloodFill[$aBound[0]]($aArray, $aTemp, $iDimension, $iSubIndex, $iRandom, $aArray, $sTransfer) ; replace the original region $sTransfer = '$aSource' & $sIndices $aFloodFill[$aBound[0]]($aArray, $aTemp, $iDimension, $iRandom, 0, $aRegion, $sTransfer) ; replace the extracted region at the acquired index Next EndIf Else ; not a multidimensional array __Shuffle1D($aArray) ; shuffle the contents EndIf Else ; totally random - ignoring dimension bounds __ShuffleXD($aArray, $aBound) EndIf EndFunc ;==>_ShuffleArray #Region - Miscellaneous ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with numeric sort. Decimal strings should first be formatted with ___FormatNum() ; Author ........: czardas ; ============================================================================================================================== Func ___AcquireExponent($vNum) Local $bString = IsString($vNum) If $bString Then $vNum = StringReplace($vNum, '-', '') ; the minus symbol must first be stripped Return $bString ? ((StringLeft($vNum, 1) = '.') ? StringLen(StringRegExpReplace($vNum, '\.0*', '')) - StringLen($vNum) : StringInStr($vNum, '.') - 2) : Number(StringRight(StringFormat('%.1e', $vNum / 1), 4)) EndFunc ;==>___AcquireExponent ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Description ...: Return an array with a range of assigned index values (used to track migration patterns in __QuickSortXD). ; Author ........: czardas ; =============================================================================================================================== Func __CreateTrac($iBound, $iStart, $iEnd) Local $aTracker[$iBound] For $i = $iStart To $iEnd $aTracker[$i] = $i ; fill the (tracking) range with indices Next Return $aTracker EndFunc ;==>__CreateTrac ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Extract a vector (list) from any dimension within a multidimensional array. ; ============================================================================================================================== Func __ExtractVector($aArray, $iDimension, $aIndices) $aIndices[$iDimension] = '$a[1]' ; $aIndices[$iDimension] is the remote loop count Local $sTransfer = '$aSource' ; the main array is the source For $i = 1 To $aIndices[0] $sTransfer &= '[' & $aIndices[$i] & ']' Next Local $iBound = UBound($aArray, $iDimension), $aVector[$iBound], $iSubIndex = 0, $aBound = ['', $iBound - 1] ___Flood1D($aVector, $aBound, $iDimension, $iSubIndex, '', $aArray, $sTransfer) Return $aVector EndFunc ;==>__ExtractVector ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - non-case-sensitive comparison. ; ============================================================================================================================== Func __FindExact($aTarget, $a, $aBound, $iFrom, $sSyntax) ; [default algorithm] $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And $aBound[0] = $sSyntax Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindExact ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - case-sensitive comparison. ; ============================================================================================================================== Func __FindExactCase($aTarget, $a, $aBound, $iFrom, $sSyntax) $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And $aBound[0] == $sSyntax Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindExactCase ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - search within strings - non-case-sensitive comparison. ; ============================================================================================================================== Func __FindString($aTarget, $a, $aBound, $iFrom, $sSyntax) $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And StringInStr($sSyntax, $aBound[0]) Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindString ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - search within strings - case-sensitive comparison. ; ============================================================================================================================== Func __FindStringCase($aTarget, $a, $aBound, $iFrom, $sSyntax) $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And StringInStr($sSyntax, $aBound[0], 1) Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindStringCase ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - search between word boundaries - non-case-sensitive comparison. ; ============================================================================================================================== Func __FindWord($aTarget, $a, $aBound, $iFrom, $sSyntax) $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And StringRegExp(StringReplace($sSyntax, '\E', $g__ARRWSHOP_SUB, 0, 1), '(*UCP)(?i)(\A|[^[:alnum:]])(\Q' & StringReplace($aBound[0], '\E', $g__ARRWSHOP_SUB, 0, 1) & '\E)(\z|[^[:alnum:]])') Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindWord ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - search between word boundaries - case-sensitive comparison. ; ============================================================================================================================== Func __FindWordCase($aTarget, $a, $aBound, $iFrom, $sSyntax) $sSyntax = Execute($sSyntax) If $g__ARRWSHOP_RESUME And StringRegExp(StringReplace($sSyntax, '\E', $g__ARRWSHOP_SUB, 0, 1), '(*UCP)(\A|[^[:alnum:]])(\Q' & StringReplace($aBound[0], '\E', $g__ARRWSHOP_SUB, 0, 1) & '\E)(\z|[^[:alnum:]])') Then $g__ARRWSHOP_RESUME = False Return $sSyntax #forceref $aTarget, $a, $iFrom EndFunc ;==>__FindWordCase ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Return an array of functions used for populating multidimensional array elements. ; ============================================================================================================================== Func __FloodFunc() Local $aFloodFunc = ['', ___Flood1D, ___Flood2D, ___Flood3D, ___Flood4D, ___Flood5D, ___Flood6D, ___Flood7D, ___Flood8D, ___Flood9D] Return $aFloodFunc EndFunc ;==>__FloodFunc ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with numeric sort. ['\A[\+\-]?(\d*\.?\d+|d+\.)\z' only ==> 1.0 .01 0.] ; Author ........: czardas ; ============================================================================================================================== Func ___FormatNum($sNum) If Not StringRegExp($sNum, '[1-9]') Then Return 0 $sNum = StringReplace($sNum, '+', '') ; get rid of plus symbol $sNum = StringRegExpReplace($sNum, "^-?\K(?=\.)", "0") ; add zeros [courtesy of jguinch] $sNum = StringRegExpReplace($sNum, "^-?\K0+(?=[1-9]|0\.?)|\.0*$|\.\d*[1-9]\K0+", "") ; strip zeros [courtesy of jguinch] If Execute($sNum) == $sNum Then Return Execute($sNum) ; return a number Return StringInStr($sNum, '.') ? StringRegExpReplace($sNum, '(\A\-?)(0)', '\1') : $sNum & '.' ; for fast comparison with floats [^^ StringCompare('1.', 1) > 0] EndFunc ;==>___FormatNum ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Get the bounds of each available dimension in a multidimensional array. ; ============================================================================================================================== Func __GetBounds($aArray, $iHypothetical = 0) Local $iMaxDim = UBound($aArray, 0) Local $aBound[($iHypothetical ? $iHypothetical : $iMaxDim) + 1] ; [or ==> Local $aBound[9]] $aBound[0] = $iMaxDim For $i = 1 To $iMaxDim $aBound[$i] = UBound($aArray, $i) If $aBound[$i] = 0 Then Return SetError(1) Next If $iHypothetical Then For $i = $iMaxDim + 1 To $iHypothetical $aBound[$i] = 1 ; imaginary dimensions Next EndIf Return $aBound EndFunc ;==>__GetBounds ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Return a fragment of code which is the format for the $sTransfer parameter in ___FloodXD. ; ============================================================================================================================== Func __HiddenIndices($iBound, $iDimension) Local $sSyntax = '' ; to access elements at their original indices For $i = 1 To $iBound If $i <> $iDimension Then $sSyntax &= '[$a[' & $i & ']]' ; default ==> '$aSource[$iFrom][$a[2]][$a[3]][$a[4]][$a[5]] etc...' Else $sSyntax &= '[$iFrom]' EndIf Next Return $sSyntax EndFunc ;==>__HiddenIndices ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Create an array of between one and nine dimensions using predefined bounds. ; ============================================================================================================================== Func ___NewArray($aBound) Switch $aBound[0] Case 1 Local $aArray[$aBound[1]] Case 2 Local $aArray[$aBound[1]][$aBound[2]] Case 3 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]] Case 4 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]] Case 5 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]] Case 6 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]] Case 7 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]] Case 8 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]][$aBound[8]] Case 9 Local $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]][$aBound[8]][$aBound[9]] EndSwitch Return $aArray EndFunc ;==>___NewArray ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _SortArray() numeric comparison [str > num(?), num > str(?), str > str(?)] ; Author ........: czardas ; ============================================================================================================================== Func ___NumCompare($vNum1, $vNum2) ; ($vNum1 > $vNum2) If IsNumber($vNum1) And IsNumber($vNum2) Then If $vNum1 = $vNum2 Then Return 0 Return ($vNum1 > $vNum2) ? 1 : -1 EndIf If $vNum1 == '1.#INF' Or $vNum2 == '-1.#INF' Then Return 1 ; these values interfere with the comparison below If $vNum1 == '-1.#INF' Or $vNum2 == '1.#INF' Then Return -1 ; ditto Local $bNeg1 = (StringLeft($vNum1, 1) = '-') If $bNeg1 <> (StringLeft($vNum2, 1) = '-') Then Return $bNeg1 ? -1 : 1 Local $iExp1 = ___AcquireExponent($vNum1), $iExp2 = ___AcquireExponent($vNum2) If $iExp1 <> $iExp2 Then Return (($iExp1 > $iExp2) ? 1 : -1) * ($bNeg1 ? -1 : 1) ; negative magnitude changes the result Local $bType1 = (VarGetType($vNum1) = 'Double'), $bType2 = (VarGetType($vNum2) = 'Double') If $bType1 Or $bType2 Then ; grab all 17 digits from the double $vNum1 = $bType1 ? StringLeft(StringReplace(StringFormat('%.17e', $vNum1), '.', ''), 17) : StringReplace($vNum1, '.', '') $vNum2 = $bType2 ? StringLeft(StringReplace(StringFormat('%.17e', $vNum2), '.', ''), 17) : StringReplace($vNum2, '.', '') EndIf Return StringCompare($vNum1, $vNum2) * ($bNeg1 ? -1 : 1) ; negative magnitude changes the result EndFunc ;==>___NumCompare ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __QuickSort1D [adaptation of __ArrayQuickSort1D] ; Description ...: Helper function for sorting 1D arrays ; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima ; Modified.......: czardas - replaced alphanumeric sort with separate lexical and numeric sorting algorithms. ; =============================================================================================================================== Func __QuickSort1D(ByRef $aArray, $iStart, $iEnd, $iAlgorithm = 0) If $iEnd <= $iStart Then Return Local $vTmp ; InsertionSort (faster for smaller segments) If ($iEnd - $iStart) < 15 Then Switch $iAlgorithm Case 0 ; lexical For $i = $iStart + 1 To $iEnd $vTmp = $aArray[$i] For $j = $i - 1 To $iStart Step -1 If StringCompare($vTmp, $aArray[$j]) >= 0 Then ExitLoop $aArray[$j + 1] = $aArray[$j] Next $aArray[$j + 1] = $vTmp Next Return Case 2 ; numeric strict For $i = $iStart + 1 To $iEnd $vTmp = $aArray[$i] If IsNumber($vTmp) Then For $j = $i - 1 To $iStart Step -1 If $vTmp >= $aArray[$j] And IsNumber($aArray[$j]) Then ExitLoop $aArray[$j + 1] = $aArray[$j] Next $aArray[$j + 1] = $vTmp EndIf Next Return EndSwitch Return EndIf ; QuickSort Local $L = $iStart, $R = $iEnd, $vPivot = $aArray[Int(($iStart + $iEnd) / 2)] Do If $iAlgorithm = 0 Then ; lexical While StringCompare($aArray[$L], $vPivot) < 0 $L += 1 WEnd While StringCompare($aArray[$R], $vPivot) > 0 $R -= 1 WEnd ElseIf $iAlgorithm = 2 Then ; numeric strict While $aArray[$L] < $vPivot $L += 1 WEnd While $aArray[$R] > $vPivot $R -= 1 WEnd EndIf If $L <= $R Then ; Swap $vTmp = $aArray[$L] $aArray[$L] = $aArray[$R] $aArray[$R] = $vTmp $L += 1 $R -= 1 EndIf Until $L > $R __QuickSort1D($aArray, $iStart, $R, $iAlgorithm) __QuickSort1D($aArray, $L, $iEnd, $iAlgorithm) EndFunc ;==>__QuickSort1D ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __QuickSortXD [adaptation of __ArrayQuickSort1D] ; Description ...: Helper function for sorting multidimensional arrays ; Author ........: Jos van der Zande, LazyCoder, Tylo, Ultima ; Modified.......: czardas - to sort indices of an X-dimensional array vector, instead of overwriting complete regions or rows. ; =============================================================================================================================== Func __QuickSortXD($aArray, ByRef $aTrac, $iStart, $iEnd, $iAlgorithm = 0) If $iEnd <= $iStart Then Return Local $iTmp ; InsertionSort (faster for smaller segments) If ($iEnd - $iStart) < 15 Then Switch $iAlgorithm Case 0 ; lexical For $i = $iStart + 1 To $iEnd $iTmp = $aTrac[$i] For $j = $i - 1 To $iStart Step -1 If (StringCompare($aArray[$iTmp], $aArray[$aTrac[$j]]) >= 0) Then ExitLoop $aTrac[$j + 1] = $aTrac[$j] Next $aTrac[$j + 1] = $iTmp Next Case 2 ; (or 4) numeric strict [also applies to algorithm 4] For $i = $iStart + 1 To $iEnd $iTmp = $aTrac[$i] For $j = $i - 1 To $iStart Step -1 If $aArray[$iTmp] >= $aArray[$aTrac[$j]] Then ExitLoop $aTrac[$j + 1] = $aTrac[$j] Next $aTrac[$j + 1] = $iTmp Next Case 256 ; numeric [preprocessed; decimal string formatting required, see ___FormatNum] For $i = $iStart + 1 To $iEnd $iTmp = $aTrac[$i] For $j = $i - 1 To $iStart Step -1 If ___NumCompare($aArray[$iTmp], $aArray[$aTrac[$j]]) >= 0 Then ExitLoop $aTrac[$j + 1] = $aTrac[$j] Next $aTrac[$j + 1] = $iTmp Next EndSwitch Return EndIf ; QuickSort Local $L = $iStart, $R = $iEnd, $vPivot = $aArray[$aTrac[Int(($iStart + $iEnd) / 2)]] Do If $iAlgorithm = 0 Then ; lexical While StringCompare($aArray[$aTrac[$L]], $vPivot) < 0 $L += 1 WEnd While StringCompare($aArray[$aTrac[$R]], $vPivot) > 0 $R -= 1 WEnd ElseIf $iAlgorithm = 2 Then ; numeric strict [strings not allowed] While $aArray[$aTrac[$L]] < $vPivot $L += 1 WEnd While $aArray[$aTrac[$R]] > $vPivot $R -= 1 WEnd ElseIf $iAlgorithm = 256 Then ; numeric greedy [includes decimal strings] While ___NumCompare($aArray[$aTrac[$L]], $vPivot) < 0 $L += 1 WEnd While ___NumCompare($aArray[$aTrac[$R]], $vPivot) > 0 $R -= 1 WEnd EndIf If $L <= $R Then ; Swap $iTmp = $aTrac[$L] $aTrac[$L] = $aTrac[$R] $aTrac[$R] = $iTmp $L += 1 $R -= 1 EndIf Until $L > $R __QuickSortXD($aArray, $aTrac, $iStart, $R, $iAlgorithm) __QuickSortXD($aArray, $aTrac, $L, $iEnd, $iAlgorithm) EndFunc ;==>__QuickSortXD ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: ReDim arrays of different dimensions. ; ============================================================================================================================== Func __ResetBounds(ByRef $aArray, $aBound) Switch $aBound[0] Case 1 ReDim $aArray[$aBound[1]] Case 2 ReDim $aArray[$aBound[1]][$aBound[2]] Case 3 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]] Case 4 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]] Case 5 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]] Case 6 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]] Case 7 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]] Case 8 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]][$aBound[8]] Case 9 ReDim $aArray[$aBound[1]][$aBound[2]][$aBound[3]][$aBound[4]][$aBound[5]][$aBound[6]][$aBound[7]][$aBound[8]][$aBound[9]] EndSwitch EndFunc ;==>__ResetBounds ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Description ...: Helper function: reverses a 1D array. ; Author ........: czardas ; =============================================================================================================================== Func ___Reverse1D(ByRef $aArray, $iStart, $iStop) Local $vTemp While $iStop > $iStart $vTemp = $aArray[$iStart] $aArray[$iStart] = $aArray[$iStop] $aArray[$iStop] = $vTemp $iStart += 1 $iStop -= 1 WEnd EndFunc ;==>___Reverse1D ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with _ArraySearch - searches through a 1D array. ; ============================================================================================================================== Func ___Search1D($aArray, $vSearchTerm, $bCaseSense, $iAlgo) Local $iItems = 0 Switch $iAlgo Case 1 ; find exact match If $bCaseSense Then ; case-sensitive For $i = 0 To UBound($aArray) - 1 If $aArray[$i] == $vSearchTerm Then $aArray[$iItems] = $aArray[$i] $iItems += 1 EndIf Next Else ; optimal [using a second loop avoids using a conditional within the loop] For $i = 0 To UBound($aArray) - 1 If $aArray[$i] = $vSearchTerm Then $aArray[$iItems] = $aArray[$i] $iItems += 1 EndIf Next EndIf Case 2 ; find a string within a string For $i = 0 To UBound($aArray) - 1 If StringInStr($aArray[$i], $vSearchTerm, $bCaseSense) Then $aArray[$iItems] = $aArray[$i] $iItems += 1 EndIf Next Case 3 ; find a word within text Local $sPattern = $bCaseSense ? '(*UCP)(\A|[^[:alnum:]])(\Q' : '(*UCP)(?i)(\A|[^[:alnum:]])(\Q' For $i = 0 To UBound($aArray) - 1 If StringRegExp(StringReplace($aArray[$i], '\E', $g__ARRWSHOP_SUB, 0, 1), $sPattern & StringReplace($vSearchTerm, '\E', $g__ARRWSHOP_SUB, 0, 1) & '\E)(\z|[^[:alnum:]])') Then $aArray[$iItems] = $aArray[$i] $iItems += 1 EndIf Next EndSwitch If Not $iItems Then Return SetError(1) ReDim $aArray[$iItems] Return $aArray EndFunc ;==>___Search1D ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with numeric sort. [numbers other than -1.#IND = OK] ; ============================================================================================================================== Func __Separate1D(ByRef $aArray, $iStart, $iEnd) Local $vTemp, $iItems = 0 For $i = $iStart To $iEnd If IsNumber($aArray[$i]) And Not ($aArray[$i] == '-1.#IND') Then $vTemp = $aArray[$iStart + $iItems] $aArray[$iStart + $iItems] = $aArray[$i] $aArray[$i] = $vTemp $iItems += 1 EndIf Next Return $iItems ; the number of numeric items EndFunc ;==>__Separate1D ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with numeric sort - separates numbers from strings. [numbers (!-1.#IND) = OK, decimal strings = OK] ; Author ........: czardas ; ============================================================================================================================== Func __Separate256(ByRef $aArray, ByRef $aTrac, $iStart, $iEnd) Local $vTemp, $iItems = 0 For $i = $iStart To $iEnd If (IsNumber($aArray[$aTrac[$i]]) And Not ($aArray[$aTrac[$i]] == '-1.#IND')) Or IsString($aArray[$aTrac[$i]]) * StringRegExp($aArray[$aTrac[$i]], '\A\h*[\+\-]?\h*(\d*\.?\d+|\d+\.)\h*\z') Then $vTemp = $aTrac[$iStart + $iItems] $aTrac[$iStart + $iItems] = $aTrac[$i] $aTrac[$i] = $vTemp If IsString($aArray[$aTrac[$iStart + $iItems]]) Then $aArray[$aTrac[$iStart + $iItems]] = ___FormatNum(StringStripWS($aArray[$aTrac[$iStart + $iItems]], 8)) ; format numeric strings ready for comparison $iItems += 1 EndIf Next Return $iItems ; the number of numeric items EndFunc ;==>__Separate256 ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: For use with numeric sort - separates numbers from strings. [numbers (! -1.#IND) = OK] ; Author ........: czardas ; ============================================================================================================================== Func __SeparateXD(ByRef $aArray, ByRef $aTrac, $iStart, $iEnd) Local $vTemp, $iItems = 0 For $i = $iStart To $iEnd If IsNumber($aArray[$aTrac[$i]]) And Not ($aArray[$aTrac[$i]] == '-1.#IND') Then $vTemp = $aTrac[$iStart + $iItems] $aTrac[$iStart + $iItems] = $aTrac[$i] $aTrac[$i] = $vTemp $iItems += 1 EndIf Next Return $iItems ; the number of numeric items EndFunc ;==>__SeparateXD ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Shuffle a one dimensional array. ; ============================================================================================================================== Func __Shuffle1D(ByRef $aArray) Local $vTemp, $iRandom, $iBound = UBound($aArray) - 1 For $i = 0 To $iBound $iRandom = Random(0, $iBound, 1) $vTemp = $aArray[$i] $aArray[$i] = $aArray[$iRandom] $aArray[$iRandom] = $vTemp Next EndFunc ;==>__Shuffle1D ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Description ...: Shuffle a multidimensional array. ; ============================================================================================================================== Func __ShuffleXD(ByRef $aArray, $aBound) ReDim $aBound[10] ; [could possibly be dealt with earlier by the call to _GetBounds] Local $vTemp, $R[10] ; [random indices] For $9 = 0 To $aBound[9] For $8 = 0 To $aBound[8] For $7 = 0 To $aBound[7] For $6 = 0 To $aBound[6] For $5 = 0 To $aBound[5] For $4 = 0 To $aBound[4] For $3 = 0 To $aBound[3] For $2 = 0 To $aBound[2] For $1 = 0 To $aBound[1] For $i = 1 To $aBound[0] $R[$i] = Random(0, $aBound[$i], 1) Next Switch $aBound[0] Case 1 $vTemp = $aArray[$1] $aArray[$1] = $aArray[$R[1]] $aArray[$R[1]] = $vTemp Case 2 $vTemp = $aArray[$1][$2] $aArray[$1][$2] = $aArray[$R[1]][$R[2]] $aArray[$R[1]][$R[2]] = $vTemp Case 3 $vTemp = $aArray[$1][$2][$3] $aArray[$1][$2][$3] = $aArray[$R[1]][$R[2]][$R[3]] $aArray[$R[1]][$R[2]][$R[3]] = $vTemp Case 4 $vTemp = $aArray[$1][$2][$3][$4] $aArray[$1][$2][$3][$4] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]] = $vTemp Case 5 $vTemp = $aArray[$1][$2][$3][$4][$5] $aArray[$1][$2][$3][$4][$5] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]] = $vTemp Case 6 $vTemp = $aArray[$1][$2][$3][$4][$5][$6] $aArray[$1][$2][$3][$4][$5][$6] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]] = $vTemp Case 7 $vTemp = $aArray[$1][$2][$3][$4][$5][$6][$7] $aArray[$1][$2][$3][$4][$5][$6][$7] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]] = $vTemp Case 8 $vTemp = $aArray[$1][$2][$3][$4][$5][$6][$7][$8] $aArray[$1][$2][$3][$4][$5][$6][$7][$8] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]][$R[8]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]][$R[8]] = $vTemp Case 9 $vTemp = $aArray[$1][$2][$3][$4][$5][$6][$7][$8][$9] $aArray[$1][$2][$3][$4][$5][$6][$7][$8][$9] = $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]][$R[8]][$R[9]] $aArray[$R[1]][$R[2]][$R[3]][$R[4]][$R[5]][$R[6]][$R[7]][$R[8]][$R[9]] = $vTemp EndSwitch Next Next Next Next Next Next Next Next Next EndFunc ;==>__ShuffleXD ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Description ...: Helper function for populating 1D arrays. [knight's tour type algorithm] ; Author ........: czardas ; =============================================================================================================================== Func __TagSortSwap(ByRef $aArray, ByRef $aTrac, $iStart, $iEnd) Local $vFirst, $i, $iNext For $iInit = $iStart To $iEnd ; initialize each swap sequence If $aTrac[$iInit] <> $iInit Then ; elements will now be swapped in a sequence $i = $iInit ; set the current index to the start of the sequence $vFirst = $aArray[$i] ; copy data [although we don't know where to put it yet] Do $aArray[$i] = $aArray[$aTrac[$i]] ; overwrite each element in the sequence $iNext = $aTrac[$i] ; get the next index in the sequence $aTrac[$i] = $i ; set to ignore overwritten elements on subsequent encounters $i = $iNext ; follow the trail as far as it goes [index could be higher or lower] Until $aTrac[$i] = $iInit ; all sequences end at this juncture $aArray[$i] = $vFirst ; now we know where to put the initial element we copied earlier $aTrac[$i] = $i ; set to ignore on subsequent encounters [as above] EndIf Next EndFunc ;==>__TagSortSwap ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Description ...: Helper function for populating 2D arrays. [knight's tour type algorithm] ; Author ........: czardas ; =============================================================================================================================== Func __TagSortSwapXD(ByRef $aArray, ByRef $aTrac, $iStart, $iEnd) Local $iCols = UBound($aArray, 2), $aFirst[$iCols], $i, $iNext For $iInit = $iStart To $iEnd ; initialize each potential overwrite sequence [separate closed system] If $aTrac[$iInit] <> $iInit Then ; rows will now be overwritten in accordance with tracking information $i = $iInit ; set the current row as the start of the sequence For $j = 0 To $iCols - 1 $aFirst[$j] = $aArray[$i][$j] ; copy the first row [although we don't know where to put it yet] Next Do For $j = 0 To $iCols - 1 $aArray[$i][$j] = $aArray[$aTrac[$i]][$j] ; overwrite each row [following the trail] Next $iNext = $aTrac[$i] ; get the index of the next row in the sequence $aTrac[$i] = $i ; set to ignore rows already processed [may be needed once, or not at all] $i = $iNext ; follow the trail as far as it goes [indices could be higher or lower] Until $aTrac[$i] = $iInit ; all tracking sequences end at this juncture For $j = 0 To $iCols - 1 $aArray[$i][$j] = $aFirst[$j] ; now we know where to put the initial row we copied earlier Next $aTrac[$i] = $i ; set to ignore rows already processed [as above] EndIf Next EndFunc ;==>__TagSortSwapXD #EndRegion - Miscellaneous #Region - Remote Loops ; #INTERNAL_USE_ONLY# ========================================================================================================== ; Name...........: ___Flood1D, ___Flood2D, ___Flood3D, ___Flood4D, ___Flood5D, ___Flood6D, ___Flood7D, ___Flood8D, ___Flood9D ; Description ...: Flood the region of a multidimensional array defined by the sub-index within the specified dimension. ; Syntax.........: ___FloodXD($aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) ; Parameters.....; $aTarget - Array to populate. ; $aBound - Array containing the bounds of each dimension. ; $iDimension - Integer value defining the dimension. ; $iSubIndex - Integer value of the sub-index within the dimension. ; $iFrom - [Hidden] Integer value defining a sub-index within $aSource. ; $aSource - [Hidden] Array containing data which may be used to populate the target array. ; $sTransfer - String of instructions used to acquire data. ; Return values .: [ByRef] The target array after the region has been flooded. ; Author ........: czardas ; Comments ......; Using remote loops cuts out several hundred lines of duplicated code. ; Recursively targeting a custom function, whichever dimension, is straight forward and reasonably optimal. ; The approach may introduce a small circumstantial speed deficit, which seems a fair trade-off. ; The sequence of loops runs backwards: optimized for higher dimensions with less bounds. ; Only the elements associated with the specified sub-index within the defined dimension are overwritten. ; Setting $iDimension to 0 causes the functions to overwrite all the elements within the target array. ; The $sTransfer parameter must be runnable code. ; Unused hidden parameters should be passed as empty strings. ; $aBound[0] can also be used as a wild card. ; ============================================================================================================================== Func ___Flood1D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) ; [still experimental] #forceref $iDimension, $iFrom, $aSource ; $iDimension would normally not apply here (special case) Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] ; loop iteration count [or indices of higher dimensions within the source array] For $a[1] = $iSubIndex To $aBound[1] ; from the start to the bounds of the 1st dimension (special case) ; only one operation is needed in this special case $aTarget[$a[1]] = Execute($sTransfer) ; hidden parameters may appear in the code being executed Next EndFunc ;==>___Flood1D ; ============================================================================================================================== ; the following functions are slightly different ; ============================================================================================================================== Func ___Flood2D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource ; hidden parameters Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] ; loop iteration count [or indices of higher dimensions within the source array] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex ; override the iteration count (fast method) - $a[0] has no influence $aTarget[$a[1]][$a[2]] = Execute($sTransfer) ; hidden parameters may appear in the code being executed Next Next EndFunc ;==>___Flood2D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood3D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource ; as above Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] ; as above For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex ; as above $aTarget[$a[1]][$a[2]][$a[3]] = Execute($sTransfer) ; as above Next Next Next EndFunc ;==>___Flood3D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood4D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]] = Execute($sTransfer) Next Next Next Next EndFunc ;==>___Flood4D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood5D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[5] = 0 To $aBound[5] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]] = Execute($sTransfer) Next Next Next Next Next EndFunc ;==>___Flood5D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood6D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[6] = 0 To $aBound[6] For $a[5] = 0 To $aBound[5] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]] = Execute($sTransfer) Next Next Next Next Next Next EndFunc ;==>___Flood6D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood7D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[7] = 0 To $aBound[7] For $a[6] = 0 To $aBound[6] For $a[5] = 0 To $aBound[5] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]][$a[7]] = Execute($sTransfer) Next Next Next Next Next Next Next EndFunc ;==>___Flood7D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood8D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[8] = 0 To $aBound[8] For $a[7] = 0 To $aBound[7] For $a[6] = 0 To $aBound[6] For $a[5] = 0 To $aBound[5] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]][$a[7]][$a[8]] = Execute($sTransfer) Next Next Next Next Next Next Next Next EndFunc ;==>___Flood8D ; ============================================================================================================================== ; see previous description and comments ; ============================================================================================================================== Func ___Flood9D(ByRef $aTarget, $aBound, $iDimension, $iSubIndex, $iFrom, $aSource, $sTransfer) #forceref $iFrom, $aSource Local $a[10] = ['', 0, 0, 0, 0, 0, 0, 0, 0, 0] For $a[9] = 0 To $aBound[9] For $a[8] = 0 To $aBound[8] For $a[7] = 0 To $aBound[7] For $a[6] = 0 To $aBound[6] For $a[5] = 0 To $aBound[5] For $a[4] = 0 To $aBound[4] For $a[3] = 0 To $aBound[3] For $a[2] = 0 To $aBound[2] For $a[1] = 0 To $aBound[1] $a[$iDimension] = $iSubIndex $aTarget[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]][$a[7]][$a[8]][$a[9]] = Execute($sTransfer) Next Next Next Next Next Next Next Next Next EndFunc ;==>___Flood9D #EndRegion - Remote Loops #Au3Stripper_On This UDF and all examples can be downloaded in the zip archive below. ArrayWorkshop.7z Related Topics : https://www.autoitscript.com/forum/topic/179779-_arraydeclarefromstring-_arraytodeclarationstring-_arrayshufflemultidim-_arraycompare-_arrayenumvalues-_arrayassign/ - some rather nice functions. Interesting link from @jchd : https://reference.wolfram.com/language/guide/ListManipulation.html - more stuff than I am ever likely to use.1 point
-
Here's a method: #include <Array.au3> ; Needed for _ArrayDisplay $sTest = "0,1,2|3,4,5,6|7,8,9" $a2D = StringTo2dArray($sTest, "|", ",") _ArrayDisplay($a2D) Func StringTo2dArray($sString, $sDelim4Rows, $sDelim4Cols) Local $aArray = StringSplit($sString, $sDelim4Rows, 2) ; Split to get rows Local $iBound = UBound($aArray) Local $aRet[$iBound][2], $aTemp, $iOverride = 0 For $i = 0 To $iBound -1 $aTemp = StringSplit($aArray[$i], $sDelim4Cols) ; Split to get row items If Not @error Then If $aTemp[0] > $iOverride Then $iOverride = $aTemp[0] ReDim $aRet[$iBound][$iOverride] ; Add columns to accomodate more items EndIf EndIf For $j = 1 To $aTemp[0] $aRet[$i][$j -1] = $aTemp[$j] ; Populate each row Next Next If $iOverride <= 1 Then $aRet = $aArray ; Array contains single row or column Return $aRet EndFunc ;==> StringTo2dArray1 point