jvanegmond Posted December 21, 2010 Posted December 21, 2010 No offense at all. That's why we do these tests; To find the fastest and most correct method. I found a some small errors in the fastest method and that led me to rewrite it like this by doing simple things like putting the checks in the right order: Func _ArrayModalSpin(ByRef $av_Array, $iClicks) If $iClicks = 0 Then Return Local $iElements = UBound($av_Array) If $iClicks >= $iElements Then $iClicks = Mod($iClicks, $iElements) If $iClicks < 0 Then $iClicks *= -1 $iStart = $iElements -$iClicks $iResume = $iClicks Else $iStart = $iClicks $iResume = $iElements -$iClicks EndIf Local $av_Temp[$iElements] For $i = 0 To $iResume -1 $av_Temp[$i] = $av_Array[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Temp[$i] = $av_Array[$i -$iResume] Next $av_Array = $av_Temp EndFunc ( Didn't test this version ) I had a look at when the large arrays are created and destroyed again and those are of course these lines: Local $av_Temp[$iElements] For $i = 0 To $iResume -1 $av_Temp[$i] = $av_Array[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Temp[$i] = $av_Array[$i -$iResume] Next $av_Array = $av_Temp but you can rewrite this part like this: $av_Temp = $av_Array For $i = 0 To $iResume -1 $av_Array[$i] = $av_Temp[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Array[$i] = $av_Temp[$i -$iResume] Next so you essentially do the same thing but you save 1 operator: Defining a large empty array. This is still done in the new version but it happens on the inside of AutoIt. This brings us this new version: Func _ArrayModalSpin(ByRef $av_Array, $iClicks) If $iClicks = 0 Then Return Local $iElements = UBound($av_Array) If $iClicks >= $iElements Then $iClicks = Mod($iClicks, $iElements) If $iClicks < 0 Then $iClicks *= -1 $iStart = $iElements -$iClicks $iResume = $iClicks Else $iStart = $iClicks $iResume = $iElements -$iClicks EndIf $av_Temp = $av_Array For $i = 0 To $iResume -1 $av_Array[$i] = $av_Temp[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Array[$i] = $av_Temp[$i -$iResume] Next EndFunc Which gives us roughly a 20% speed increase. 8) Not much, but noticeable enough. github.com/jvanegmond
czardas Posted December 21, 2010 Author Posted December 21, 2010 (edited) That's pretty cool, although I thought a problem might occur (I realize now that it still works). I believe the first check belongs after the Mod function. It may slow it down slightly otherwise. When $iClicks turns out to be either a multiple of $iElements or equal to zero, we want to quit the function at that point. It seems a bit silly to say spin the array zero clicks (or 360 degrees), so it's probably more useful to do it this way. Func _ArrayModalSpin(ByRef $av_Array, $iClicks) Local $iElements = UBound($av_Array) $iClicks = Mod($iClicks, $iElements) If $iClicks = 0 Then Return If $iClicks > 0 Then $iStart = $iClicks $iResume = $iElements -$iClicks Else $iClicks *= -1 $iStart = $iElements -$iClicks $iResume = $iClicks EndIf $av_Temp = $av_Array For $i = 0 To $iResume -1 $av_Array[$i] = $av_Temp[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Array[$i] = $av_Temp[$i -$iResume] Next EndFunc I never thought that copying an array would be faster than declaring an empty array of the same size, but if it's done internally, then I see why. 20% is a significant increase in speed. Edited November 5, 2011 by czardas operator64 ArrayWorkshop
jvanegmond Posted December 22, 2010 Posted December 22, 2010 Makes sense to reverse checks. I think this is complete. github.com/jvanegmond
czardas Posted December 22, 2010 Author Posted December 22, 2010 (edited) I have removed the conditional statement at line 3 (post 24). There was still one problem. In this particular script, it seems to be 'universally' better to go for using the Mod function every time (without the checks). Otherwise an additional conditional statement would be required, however the extra code offers no clear advantage. Running the same speed test (post 22) with the new version (post 24) gives 2960.22036320259. That's a big improvement from the original. Edited December 23, 2010 by czardas operator64 ArrayWorkshop
MvGulik Posted December 23, 2010 Posted December 23, 2010 Also, I wanted to make a post why using only one array is not possible. Are your sure about that "not possible" bit? Granted this is only a test case. No actually spinning is done. But still, it looks possible. CodeDump:TestCase:expandcollapse popup#AutoIt3Wrapper_AU3Check_Stop_OnWarning=y #AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 6 -q #cs Func test_X($av_Array, Const $iStep) ;; test case. (touch every array position one's, using given step value as base step.) Func CaseLoop() ;; walk trough different sized array's. Func SpinLoop($av_Array) ;; walk trough possible step values for given array. Func DebugOut($1 = '', $2 = '') ;; print to console. Func _DB_ArrayToString($av_Array) ;; 1D array only. Func _NumberArray($iSize) ;; [' 1',...,'99'] #ce CaseLoop() Exit Func test_X($av_Array, Const $iStep) DebugOut('+ $av_Array', $av_Array) ;### Debug DebugOut. DebugOut('+ $iStep', $iStep) ;### Debug DebugOut. ;; target: Keep looping around a array untill all entries are processed. (one time only) ;; - assumes positive $iStep values only. (test case) ;; using absolute array position value. ;; (-1 them "when" modifing array elements. Array only intended/used for Debug output purpuse.) Local Const $iFirst = 1 Local Const $iLast = UBound($av_Array, 1) If Not Mod($iStep, $iLast) Then DebugOut('> nothing to do') ;### Debug DebugOut. Return EndIf Local $iOffset1 = 0 ;; inside set-looping offset. Local $iOffset2 = 0 ;; global pre set-looping offset. Local $sLog = '|' ;; just to get the thing working. Local $iConter1 = 0 ;; [debug][array modivication feedback] While 1 For $i = $iFirst + $iOffset1 + $iOffset2 To $iLast Step $iStep $sLog &= $i & '|' ;~ DebugOut('$sLog', $sLog) ;### Debug DebugOut. $av_Array[$i - $iFirst] = Chr(65 + $iConter1) & Chr(65 + $iConter1) $iConter1 += 1 DebugOut('> $av_Array', $av_Array) ;### Debug DebugOut. Next $iOffset1 = $i - $iLast - 1 ;; set walked of the given ranges. loop it back to start. If StringInStr($sLog, '|' & String($iFirst + $iOffset1)) Then ;; set has looped back on itself. jump to next (posible) set. $iOffset2 += 1 $iOffset1 = 0 If StringInStr($sLog, '|' & String($iFirst + $iOffset1 + $iOffset2)) Then ExitLoop ;; next set allready processed, exit main loop. EndIf WEnd EndFunc Func CaseLoop() Local $av_Array For $i = 2 To 6 ;~ DebugOut('> CaseLoop: $i', $i) ;### Debug DebugOut. $av_Array = _NumberArray($i) SpinLoop($av_Array) Next EndFunc Func SpinLoop($av_Array) Local $result For $i = 1 To UBound($av_Array, 1) - 1 ;~ DebugOut('> SpinLoop: $i', $i) ;### Debug DebugOut. test_X($av_Array, $i) Next EndFunc Func DebugOut($1 = '', $2 = '') If IsString($2) And $2 Then $2 = '"' & StringReplace($2, "'", "''") & '"' If IsArray($2) Then $2 = _DB_ArrayToString($2) If $2 Or Not IsString($2) Then $1 &= ' = ' & String($2) ConsoleWrite($1 & @CRLF) EndFunc Func _DB_ArrayToString($av_Array) Local $sOut = '' For $e In $av_Array ;~ If IsString($e) Then $e = "'" & StringReplace($e, "'", "''") & "'" $sOut &= $e & ',' Next Return '[' & StringTrimRight($sOut, 1) & ']' EndFunc Func _NumberArray($iSize) Local $aOut[$iSize] For $i = 1 To $iSize $aOut[$i - 1] = StringFormat('%2d', $i) Next Return $aOut EndFuncOutputSample:+ $aTemp = [ 1, 2, 3] + $iStep = 2 > $aTemp = [AA, 2, 3] > $aTemp = [AA, 2,BB] > $aTemp = [AA,CC,BB] + $aTemp = [ 1, 2, 3, 4] + $iStep = 1 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA,BB, 3, 4] > $aTemp = [AA,BB,CC, 4] > $aTemp = [AA,BB,CC,DD] + $aTemp = [ 1, 2, 3, 4] + $iStep = 2 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA, 2,BB, 4] > $aTemp = [AA,CC,BB, 4] > $aTemp = [AA,CC,BB,DD] + $aTemp = [ 1, 2, 3, 4] + $iStep = 3 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA, 2, 3,BB] > $aTemp = [AA, 2,CC,BB] > $aTemp = [AA,DD,CC,BB] "Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions.""The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014) "Believing what you know ain't so" ... Knock Knock ...
czardas Posted December 28, 2010 Author Posted December 28, 2010 (edited) Are your sure about that "not possible" bit? Granted this is only a test case. No actually spinning is done. But still, it looks possible. + $aTemp = [ 1, 2, 3] + $iStep = 2 > $aTemp = [AA, 2, 3] > $aTemp = [AA, 2,BB] > $aTemp = [AA,CC,BB] + $aTemp = [ 1, 2, 3, 4] + $iStep = 1 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA,BB, 3, 4] > $aTemp = [AA,BB,CC, 4] > $aTemp = [AA,BB,CC,DD] + $aTemp = [ 1, 2, 3, 4] + $iStep = 2 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA, 2,BB, 4] > $aTemp = [AA,CC,BB, 4] > $aTemp = [AA,CC,BB,DD] + $aTemp = [ 1, 2, 3, 4] + $iStep = 3 > $aTemp = [AA, 2, 3, 4] > $aTemp = [AA, 2, 3,BB] > $aTemp = [AA, 2,CC,BB] > $aTemp = [AA,DD,CC,BB] I wish I knew what you were doing. Sure reversing the final output gives a spin of -1 (by my original definition), but I don't understand why $aTemp is not a modified copy of $av_Array. Perhaps the format is slightly different. It seems like an interesting technique none the less. $aTemp = [AA,DD,CC,BB] Edited December 28, 2010 by czardas operator64 ArrayWorkshop
MvGulik Posted December 28, 2010 Posted December 28, 2010 (edited) 1) "$aTemp":Erm ... not sure what you main with $aTemp, although this might explain it.local code:Func testloop($aTemp, $iStep) ... $aTemp[$i - $iFirst] = Chr(65 + $iConter1) & Chr(65 + $iConter1) $iConter1 += 1 DebugOut('> $aTemp', $aTemp) ;### Debug DebugOut.Seems I forgot to update the output to reflect the cleaned up posted version. So $aTemp == $av_Array.2) "not updated":Well its a test case and I did not ByRef the $av_Array parameter in Func test_X($av_Array, Const $iStep) so I could keep feeding it the original $av_Array from SpinLoop(). That also made me not do anything else but updating the used array with debug information. 3) "I wish I knew what you were doing."Just playing around with a idea.I did do a array processing function where I first took out one value/data to make a empty entry, so other data could be shuffled around whiteout having to temporary save one when swapping two data entries. (Think it was some randomize-array function.) Did the same thing here. At leased thats the base idea of the whole process. The rest is to fix the shortcomings and/or limitations of that process.-a flip back to begin or array when the loop crosses the end of the array.-b switch to next(unprocessed) entry in array when the loop loops back on the entry from which it started.)-c continue until -b fails. Note:This will never be faster that your original code that uses 2 array's. Du(?) to the additional code needed to keep the looping under controle.So the only real benefit is less memory use (for the duration of the spinning) ... but with a array so big that it needs this ... I will question the user for using such a big array. Hope it make some sens. Edited December 28, 2010 by MvGulik "Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions.""The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014) "Believing what you know ain't so" ... Knock Knock ...
czardas Posted December 29, 2010 Author Posted December 29, 2010 (edited) @ MvGulik Ah Okay! In sound recording they call using an extra track 'bouncing' (or at least they used to). That's something similar to the empty array element you are using. Now we have the modal string version. ; #FUNCTION# ============================================================================ ; Name...........: _StringModalSpin ; Description ...: Rotates a string as if the string were a continuous modal loop. ; Syntax.........: _StringModalSpin($sString, $iShift) ; Parameters ....: $sString - Original string ; $iShift - The number of characters to shift right (negative values shift characters to the left) ; Return values .: Success - Returns the new modified string ; Failure - Returns an empty string and sets @error to the following values: ; |@error = 1 : Invalid source string ; |@error = 2 : Invalid shift value ; Author ........: czardas ; Example .......: Yes ; ======================================================================================== Func _StringModalSpin($sString, $iShift) If $sString = "" Or (Not IsString($sString)) Then Return SetError(1, 0, "") ; Source string empty / not a string If Not IsInt($iShift) Then Return SetError(2, 0, "") ; Shift value is not an integer. Local $iStringLen = StringLen($sString) $iShift = Mod($iShift, $iStringLen) If $iShift = 0 Then Return $sString If $iShift > 0 Then Return StringRight($sString, $iShift) & StringLeft($sString, $iStringLen -$iShift) Else Return StringRight($sString, $iStringLen +$iShift) & StringLeft($sString, -$iShift) EndIf EndFunc ; Example Local $sScale = "CDEFGAB", $as_Mode[7] = ["Ionian","Dorian","Phrygian","Lydian","Mixolydian","Aeolian","Locrian"] For $i = 0 To 6 ConsoleWrite(_StringModalSpin($sScale, -$i) & StringMid($sScale, $i +1, 1) & " - " & $as_Mode[$i] & @LF) NextEdit To demonstrate the use of this function, I have replaced the original example with one which some of you will find more familiar. If you are uncertain what the example is about, you may wish to refer to the following article: http://en.wikipedia.org/wiki/Musical_mode#Modern. Edited November 5, 2011 by czardas operator64 ArrayWorkshop
jvanegmond Posted December 29, 2010 Posted December 29, 2010 MvGulik, you're probably right. You can spin the array by 1 or -1 without using 2 arrays: You only need one extra variable (not array) to hold the object being shifted (in an a=b and b=a sense you also need c). If you repeat that process several times you are still only using one array, but spinning the array multiple times. Good thinking. github.com/jvanegmond
czardas Posted January 3, 2011 Author Posted January 3, 2011 (edited) In addition to the spin effects, I have added two more modal functions. I have changed the definition of positive spin in _StringModalSpin (post 30) to comply with the other modal functions._ArrayModalSelect makes the job of looping round an array very simple. The beauty of this function is that you never have to worry about exceeding the number of array elements (error message) because the array is treated as having an infinite number of elements._StringModalSelect is similar to _StringModalSpin, however the return string can have any length.Modal.au3expandcollapse popup; #FUNCTION# ============================================================================ ; Name...........: _ArrayModalSpin ; Description ...: Rotates a one dimensional array as if it were a continuous modal loop. ; Syntax.........: _ArrayModalSpin(ByRef $av_Array, $iShift) ; Parameters ....: $av_Array - Original array ; $iShift - The number of elements to shift up (negative values shift elements down) ; Return values .: Success - Returns a new modified array ; Failure - Sets @error to the following values: ; |@error = 1 : The first parameter is not a one dimensional array ; |@error = 2 : Invalid shift value ; Author ........: czardas, Manadar ; Related .......: __ArrayModalSelect ; Example .......: Yes ; ======================================================================================== Func _ArrayModalSpin($av_Array, $iShift) If (Not IsArray($av_Array)) Or UBound($av_Array, 2) Then Return SetError(1) If Not IsInt($iShift) Then Return SetError(2) Local $iElements = UBound($av_Array) $iShift = Mod($iShift, $iElements) If $iShift = 0 Then Return $av_Array If $iShift > 0 Then $iStart = $iShift $iResume = $iElements -$iShift Else $iShift *= -1 $iStart = $iElements -$iShift $iResume = $iShift EndIf $av_Temp = $av_Array For $i = 0 To $iResume -1 $av_Array[$i] = $av_Temp[$iStart +$i] Next For $i = $iResume To $iElements -1 $av_Array[$i] = $av_Temp[$i -$iResume] Next Return $av_Array EndFunc ;==> _ArrayModalSpin ; #FUNCTION# ============================================================================ ; Name...........: _StringModalSpin ; Description ...: Rotates a string as if the string were a continuous modal loop. ; Syntax.........: _StringModalSpin($sString, $iShift) ; Parameters ....: $sString - Original string ; $iShift - The number of characters to shift left (negative values shift characters to the right) ; Return values .: Success - Returns the new modified string ; Failure - Returns an empty string and sets @error to the following values: ; |@error = 1 : Invalid source string ; |@error = 2 : Invalid shift value ; Author ........: czardas ; Related .......: _StringModalSelect ; Example .......: Yes ; ======================================================================================== Func _StringModalSpin($sString, $iShift) If $sString = "" Or (Not IsString($sString)) Then Return SetError(1, 0, "") If Not IsInt($iShift) Then Return SetError(2, 0, "") Local $iStringLen = StringLen($sString) $iShift = Mod($iShift, $iStringLen) If $iShift = 0 Then Return $sString If $iShift > 0 Then Return StringRight($sString, $iStringLen -$iShift) & StringLeft($sString, $iShift) Else Return StringRight($sString, -$iShift) & StringLeft($sString, $iStringLen +$iShift) EndIf EndFunc ;==> _StringModalSpin ; #FUNCTION# ============================================================================ ; Name...........: _ArrayModalSelect ; Description ...: Selects any element from a continuous modal array loop. ; Syntax.........: _ArrayModalSelect($av_Array, $iElement) ; Parameters ....: $av_Array - Infinite modal array ; $iElement - Element to select from the infinite array (negative values loop backwards) ; Return values .: Success - Returns the array element ; Failure - Sets @error to the following values: ; |@error = 1 : Parameter 1 is not a one dimensional array ; |@error = 2 : Element is not an integer ; Author ........: czardas ; Related .......: __ArrayModalSpin ; Example .......: Yes ; ======================================================================================== Func _ArrayModalSelect($av_Array, $iElement) If (IsArray($av_Array) = 0) Or UBound($av_Array, 2) Then Return SetError(1) If Not IsInt($iElement) Then Return SetError(2) $iElement = Mod($iElement, UBound($av_Array)) If $iElement >= 0 Then Return $av_Array[$iElement] Else Return $av_Array[UBound($av_Array) +$iElement] EndIf EndFunc ;==> _ArrayModalSelect ; #FUNCTION# ============================================================================ ; Name...........: _StringModalSelect ; Description ...: Selects a number of characters from a continuous modal string loop. ; Syntax.........: _StringModalSelect( $sString [, $iShift [, $iChars]]) ; Parameters ....: $sString - Original string ; $iShift - The number of characters to shift left prior to selection (negative values shift characters right) ; Return values .: Success - Returns the new string ; Failure - Returns an empty string and sets @error to the following values: ; |@error = 1 : Invalid source string ; |@error = 2 : Invalid shift value ; |@error = 3 : Invalid number of characters ; Author ........: czardas ; Remarks .......: Return string can be any length ; Related .......: _StringModalSpin ; Example .......: Yes ; ======================================================================================== Func _StringModalSelect($sString, $iShift = 0, $iChars = 1) If $sString = "" Or (Not IsString($sString)) Then Return SetError(1, 0, "") If Not IsInt($iShift) Then Return SetError(2, 0, "") If (Not IsInt($iChars)) Or $iChars <= 0 Then Return SetError(3, 0, "") ; Get the current position of the first character for the new string Local $iStringLen = StringLen($sString) $iShift = Mod($iShift, $iStringLen) If $iShift >= 0 Then $iStart = $iShift +1 Else $iStart = $iStringLen +$iShift +1 EndIf Local $iEnd = $iStart +$iChars -1 ; Get the projected end character position If $iEnd <= $iStringLen Then Return StringMid($sString, $iStart, $iChars) ; The return string is contained within the original string Else $sTemp = $sString For $i = 1 To Int($iEnd/$iStringLen) -1 $sTemp &= $sString ; repeat the modal string loop as required Next $sTemp = StringTrimLeft($sTemp, $iStart -1) ; remove surplus leading chars Return $sTemp & StringLeft($sString, Mod($iEnd, $iStringLen)) ; Add the final character sequence EndIf EndFunc ;==> _StringModalSelectExamples#include 'Modal.au3' ConsoleWrite("Example 1" & @LF & _StringModalSpin("round the other way ", -14) & @LF) ConsoleWrite(@LF & "Example 2 - Every other day" & @LF) Local $as_Days[7] = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"] For $i = -6 To 6 Step 2 ConsoleWrite(_ArrayModalSelect($as_Days, $i) &@LF) ; Every other day Next ConsoleWrite(@LF & "Example 3 - Greek Modes" & @LF) Local $as_Mode[7] = ["Aeolian","Locrian","Ionian","Dorian","Phrygian","Lydian","Mixolydian"] $as_Mode = _ArrayModalSpin($as_Mode, 2) ; Spin the array Local $scale = "ABCDEFG" ; Each output string will contain eight musical notes For $i = 0 To 6 ConsoleWrite(_StringModalSelect($scale, $i -5, 8) & " - " & $as_Mode[$i] & @LF) Next_StringModalSelect("test", 1, 4) == _StringModalSpin("test", 1)At first I wasn't sure if I should have made the first post in help and support. But I think the discussion here has turned out to be a good example of finding ways to improve a script (which was already working). Whether anyone else will find these functions useful is unknown to me. I will definately make use of them myself. Please let me know your thoughts. Edited January 5, 2011 by czardas operator64 ArrayWorkshop
czardas Posted August 26, 2011 Author Posted August 26, 2011 (edited) I started this thread for minor applications, or bits of code that may be of potential interest to anyone (perhaps someone just starting out will find something useful here). My more important projects seem to take for ever and occasionally I feel the need to write something really simple. So here's Log Dog - a very simple application which I made a while back. It's nothing more than an edit control which appends the time and date every time you press the enter key. The text is saved automatically in a file called logdog.log. You can use the enter key normally when you hold down the control key - don't know why though! The code also includes hotfixes for two (edit control) bugs which are known to affect some users of AutoIt. These issues could be related to each other, but the cause is a bit of a mystery. Thanks to the forum members who explained to me how to fix these issues using accelerators.LogDog.au3expandcollapse popup#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiEdit.au3> _RunLogDog() Func _RunLogDog() Local $hLogDog = GUICreate(" Log Dog", 600, 400, Default, Default, $WS_OVERLAPPEDWINDOW) Local $hEdit = GUICtrlCreateEdit("", 0, 0, 600, 400, BitOr($GUI_SS_DEFAULT_EDIT, $ES_NOHIDESEL)), _ $hEnter = GUICtrlCreateDummy(), _ $hTab = GUICtrlCreateDummy(), _ $hSelectAll = GUICtrlCreateDummy(), _ $AccelKeys[3][2] = [["{ENTER}", $hEnter],[ @TAB, $hTab],["^a", $hSelectAll]] GUICtrlSetFont($hEdit, 12, 400, 1, "Lucida Console") GUISetAccelerators($AccelKeys) GUISetState() While 1 $msg = GUIGetMsg() Switch $msg Case $GUI_EVENT_CLOSE _ExitLogDog($hEdit) Case $hEnter _LogEntry($hEdit) Case $hTab _TAB($hEdit) Case $hSelectAll _SelectAll($hEdit) EndSwitch WEnd EndFunc ;==> _RunLogDog Func _LogEntry($hEdit) ; Appends time and date on pressing enter Local $sLogEntry = @CRLF & "Logged on " & _GetDescriptiveDate() & _ " at " & @HOUR & ":" & @MIN & ":" & @SEC & @CRLF & @CRLF _GUICtrlEdit_ReplaceSel($hEdit, $sLogEntry) EndFunc ;==> _LogEntry Func _GetDescriptiveDate() Local $WeekDay[7] = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], _ $Month[12] = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], _ $MonthDay = Number(@MDAY), $sTag = "th" Switch Number(StringRight($MonthDay, 1)) Case 1 $sTag = "st" Case 2 $sTag = "nd" Case 3 $sTag = "rd" EndSwitch Return $WeekDay[@WDAY -1] &" " & $MonthDay & $sTag & " " & $Month[@MON -1] & " " & @YEAR EndFunc ;==> _GetDescriptiveDate Func _TAB($hEdit) ; Bug Fix - unknown cause _GUICtrlEdit_ReplaceSel($hEdit, @TAB) EndFunc ;==> _TAB Func _SelectAll($hEdit) ; Bug Fix - unknown cause _GUICtrlEdit_SetSel($hEdit, 0, -1) EndFunc ;==> _SelectAll Func _ExitLogDog($hEdit) Local $EditText = GUICtrlRead($hEdit) If StringLen($EditText) Then FileWriteLine(@ScriptDir & "\logdog.log", $EditText) Exit EndFunc ;==> _ExitLogDog Edited August 29, 2011 by czardas operator64 ArrayWorkshop
czardas Posted August 29, 2011 Author Posted August 29, 2011 (edited) logdog logs logdog.log logs in the last 53 hours. Try saying it quickly (including the dot). Sample Output Snippet Dump views = 2317 Logged on Sat 27th Aug 2011 at 00:30:59 Snippet Dump views = 2367 Logged on Sat 27th Aug 2011 at 06:46:12 Snippet Dump views = 2399 Logged on Sat 27th Aug 2011 at 10:47:34 Snippet Dump views = 2418 Logged on Sat 27th Aug 2011 at 20:19:53 Snippet Dump views = 2443 Logged on Sun 28th Aug 2011 at 10:57:17 Snippet Dump views = 2464 Logged on Mon 29th Aug 2011 at 05:35:29 More than 100 hits within 24 hours. Funny how something so simple can make you happy. Edited August 29, 2011 by czardas operator64 ArrayWorkshop
monoscout999 Posted August 29, 2011 Posted August 29, 2011 so here is the place to dump some snippets?
MvGulik Posted August 29, 2011 Posted August 29, 2011 (edited) Well, AutoIt Snippets Database might also be a option. Not sure if its still working. But its still up. (But seems more suitable for final snippets.)---PS: Think sharing a snippets topic with multiple users is not such a good idea. As there is no general topic subject compared to- Snippets + One user.- Multiple users + Same code subject. Edited August 29, 2011 by iEvKI3gv9Wrkd41u "Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions.""The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014) "Believing what you know ain't so" ... Knock Knock ...
czardas Posted August 29, 2011 Author Posted August 29, 2011 (edited) There are some interesting examples there, including _StringRotate - which is pretty much the same as Funny coincidence!Think sharing a snippets topic with multiple users is not such a good idea.I didn't want to say don't post stuff in here, but indeed you are right about what you say. Edited August 29, 2011 by czardas operator64 ArrayWorkshop
czardas Posted August 30, 2011 Author Posted August 30, 2011 (edited) If it doesn't relate to anything in this thread then, I suggest you post it elsewhere. Edited August 30, 2011 by czardas operator64 ArrayWorkshop
czardas Posted September 4, 2011 Author Posted September 4, 2011 (edited) Fuzzy Logic Date Extraction Many people prefer to have a date written out in full, but that can mean a number of different formats. This code is intended to recognize a date in a variety of commonly used formats. The written date is converted to DD/MM/YYYY or DD/MM/YY, depending on input. While it may never be perfect, it's perhaps something that could be useful for some applications. Requires revisionexpandcollapse popup_Run() Func _Run() ; Convert a date to standard digit date format $sDate = "Monday the 31st of October 2011" $ret = _ExtractDateInfo($sDate, "/") If @error Then MsgBox(0, "ERROR", "Error occured on line " & @extended) Else MsgBox(0, $sDate, "Converts to" & @CRLF & @CRLF & $ret) EndIf EndFunc Func _ExtractDateInfo($sDate, $sDelim) If (Not IsString($sDate)) Or (Not IsString($sDelim)) Then Return SetError(1, 14, "") While StringRegExp($sDate, "[^[:alnum:]\s]| in | the | year | of | our | lord ") ; Remove these $sDate = StringRegExpReplace($sDate, "[^[:alnum:]\s]| in | the | year | of | our | lord ", " ") WEnd $sDate = StringStripWS($sDate, 7) $sDate = _RemoveWeekDay($sDate) ; Remove the weekday (sun, mon etc...) if present If @error Then Return SetError(2, @extended, "") $sDate = _RemoveDigitSuffix($sDate) ; Remove 'st', 'nd', 'rd', 'th' If @error Then Return SetError(3, @extended, "") $sArray = StringRegExp($sDate , _ ; This regexp is for personal reference (See next comment) "(?i)(\d+\s)(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[[:alpha:]]*(\s\d+)", 1) ; A different solution is to just use StringSplit => $sArray = StringSplit($sDate, " ", 2) If UBound($sArray) <> 3 Then Return SetError(3, 26, "") $sArray[0] = _PrefixZero(StringStripWS($sArray[0], 2)) ; Day minimum digits = 2 If @error Then Return SetError(4, @extended, "") $sArray[1] = _MonthToDigits($sArray[1]) ; Replace the name of the month with 2 digits $sArray[2] = _PrefixZero(StringStripWS($sArray[2], 1)) ; Year minimum digits = 2 If @error Then Return SetError(5, @extended, "") Return $sArray[0] & $sDelim & $sArray[1] & $sDelim & $sArray[2] EndFunc Func _RemoveWeekDay($string) If (Not IsString($string)) Or (StringLen($string) < 3) Then Return SetError(1, 36, $string) $string = StringRegExpReplace($string, "(?i)(Sun|Mon|Tue|Wed|Thu|Fri|Sat)[[:alpha:]]*", "") Return StringStripWS($string, 1) EndFunc Func _RemoveDigitSuffix($string) If (Not IsString($string)) Or (StringLen($string) < 3) Then Return SetError(1, 42, $string) Return StringRegExpReplace($string, "(\d)(st|nd|rd|th)", "$1") EndFunc Func _MonthToDigits($string) If (Not IsString($string)) Or (StringLen($string) < 3) Then Return SetError(1, 47, "") Local $ret, $aMonth[12] = _ ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] For $ret = 0 To 11 If StringLeft($string, 3) = $aMonth[$ret] Then ExitLoop Next If $ret = 12 Then Return SetError(2, 53, "") $ret +=1 Return _PrefixZero($ret) EndFunc Func _PrefixZero($sDigit, $iLen = 2) If Not IsInt(Number($sDigit)) Then Return SetError(1, 59, $sDigit) While StringLen(String($sDigit)) < $iLen $sDigit = "0" & $sDigit WEnd Return $sDigit EndFuncHere are some example date formats it will successfully convert.mon|31|oct|2011 monday 31/october/2011 31st-oct-2011 31st october 11 mon the 31st october 2011 Monday the 31st of October in the year 2011 Monday the 31st of October in the year of our lord 2011 Monkey the 31nd of Octopus ^_^ in the year of our lord 2011 :) Edited October 21, 2011 by czardas operator64 ArrayWorkshop
czardas Posted October 2, 2011 Author Posted October 2, 2011 (edited) Three new functions to be added to Modal.au3, which incidentally I could no longer live without. These are simple and somewhat limited numeric base conversion functions: _DecToNumBase_NumBaseToDec_NumBaseToNumBase Negative numeric input values values are supported. The following list gives the decimal equivalent max value limitations on all supported numeric bases Binary - 32767Ternary - 14348906Quaternary - 1073741823Quinary - 30517578124Senary - 470184984575Septenary. - 4747561509942Octal. - 35184372088831Nonary - 205891132094648Decimal - 999999999999999 expandcollapse popup; #FUNCTION# ============================================================================ ; Name...........: _DecToNumBase ; Description ...: Converts a decimal integer to a new numeric base ; Syntax.........: _DecToNumBase($iDecimal, $iBase) ; Parameters ....: $iDecimal - Decimal input value ; $iBase - The numeric base to convert to ; Return values .: Success - Returns the numeric base equivalent of the decimal ; Failure - Sets @error to the following values: ; |@error = 1 Invalid base value ; |@error = 2 Invalid decimal value ; |@error = 3 Float output limitation ; Author ........: czardas, iEvKI3gv9Wrkd41u ; Comments ......; Max input of 15 digits, and numeric base limitations from number bases 2 through 10 ; Related .......: _NumBaseToDec, _NumBaseToNumBase ; Example .......: Yes ; ======================================================================================== Func _DecToNumBase($iDecimal, $iBase) If IsInt($iBase) = 0 Or $iBase < 2 Or $iBase > 10 Then Return SetError(1, 0, 0) ; invalid base value If IsInt($iDecimal) = 0 Then Return SetError(2, 0, 0) ; invalid decimal value Local $iSign = 1, $iRet = 0 If $iDecimal < 0 Then $iSign = -1 $iDecimal *= $iSign ; force positive value If $iDecimal < $iBase Or $iBase = 10 Then Return $iDecimal*$iSign ; nothing to do If Floor(Log($iDecimal)/Log($iBase)) > 15 Then Return SetError(2, 0, 0) ; float output limitation __DecToNumBase($iDecimal, $iBase, $iRet) Return $iRet*$iSign EndFunc ;=> _DecToNumBase ; #FUNCTION# ============================================================================ ; Name...........: _NumBaseToDec ; Description ...: Converts a numeric base value to its decimal equivalent ; Syntax.........: _DecToNumBase($iDecimal, $iBase) ; Parameters ....: $iNumber - Numeric input ; $iBase - The numeric base to convert from ; Return values .: Success - Returns the decimal equivalent of the numeric base input value ; Failure - Sets @error to the following values: ; |@error = 1 Invalid base value ; |@error = 2 Invalid numeric input ; |@error = 3 Float input limitation ; Author ........: czardas ; Comments ......; Max input of 15 digits, and numeric base limitations from number bases 2 through 10 ; Related .......: _NumBaseToDec , _NumBaseToNumBase ; Example .......: Yes ; ======================================================================================== Func _NumBaseToDec($iNumber, $iBase) If IsInt($iBase) = 0 Or $iBase < 2 Or $iBase > 10 Then Return SetError(1, 0, 0) ; invalid base value If IsInt($iNumber) = 0 Then Return SetError(2, 0, 0) ; invalid numeric input If $iBase = 10 Then Return $iNumber ; nothing to do _ValidateNumBase($iNumber, $iBase) If @error Then Return SetError(2, 0, 0) ; invalid digits Local $aBaseDigit, $iExponent, $iSign = 1, $iRet = 0 If $iNumber < 0 Then $iSign = -1 $iNumber *= $iSign ; force positive value $iExponent = StringLen($iNumber) -1 If $iExponent > 14 Then Return SetError(3, 0, 0) ; float input limitation $aBaseDigit = StringSplit($iNumber, "", 2) For $i = 0 To $iExponent $iRet += $aBaseDigit[$i]*($iBase^($iExponent - $i)) Next Return $iRet*$iSign EndFunc ;==> _NumBaseToDec ; #FUNCTION# ============================================================================ ; Name...........: _NumBaseToNumBase ; Description ...: Converts a numeric expression from one base to another ; Syntax.........: _NumBaseToNumBase($iNumber, $iBase, $iNewBase) ; Parameters ....: $iNumber - Numeric input ; $iBase - The numeric base to convert from ; $iNewBase - The numeric base to convert to ; Return values .: Success - Returns the new numeric base equivalent of the input value ; Failure - Sets @error to the following values: ; |@error = 1 Invalid base value ; |@error = 2 Invalid numeric input ; |@error = 3 Float input limitation ; |@error = 4 Invalid new base value ; Author ........: czardas ; Comments ......; Max input of 15 digits, and numeric base limitations from number bases 2 through 10 ; Related .......: _NumBaseToDec , _DecToNumBase ; Example .......: Yes ; ======================================================================================== Func _NumBaseToNumBase($iNumber, $iBase, $iNewBase) _ValidateNumBase($iNumber, $iBase) If @error Then Return SetError(2, 0, 0) $iNumber = _NumBaseToDec($iNumber, $iBase) If @error Then Return SetError(@error, 0, 0) $iNumber = _DecToNumBase($iNumber, $iNewBase) If @error Then Return SetError(@error +3, 0, 0) Return $iNumber EndFunc ;==> _NumBaseToNumBase ; #FUNCTION# ============================================================================ ; Name...........: __DecToNumBase ; Description ...: Internal function to convert from decimal to a new numeric base ; Syntax.........: __DecToNumBase($iDecimal, $iBase, ByRef $iRet) ; Parameters ....: $iDecimal - Decimal input value ; $iBase - The numeric base to convert to ; $iRet - Recursive return value ; Return values .: Returns the numeric base value equivalent of the decimal integer ; Author ........: czardas, iEvKI3gv9Wrkd41u ; ======================================================================================== Func __DecToNumBase($iDecimal, $iBase, ByRef $iRet) Local $iPower = Floor(Log($iDecimal) / Log($iBase)) ; excellent suggestion from iEvKI3gv9Wrkd41u Local $iDigit = Int($iDecimal/($iBase^$iPower)) $iRet += $iDigit*(10^$iPower) $iDecimal -= $iDigit*($iBase^$iPower) If $iDecimal < $iBase Then $iRet += $iDecimal Else __DecToNumBase($iDecimal, $iBase, $iRet) EndIf EndFunc ;==> __DecToNumBase ; #FUNCTION# ============================================================================ ; Name...........: _ValidateNumBase ; Description ...: Internal function to check input only contains digits within the numeric mode ; Syntax.........: _ValidateNumBase($iNumber, $iBase) ; Parameters ....: $iNumber - Numeric input value ; $iBase - The numeric base to check ; Return values .: Sets @error to 1 if validation fails ; Author ........: czardas ; ======================================================================================== Func _ValidateNumBase($iNumber, $iBase) If $iBase = 10 Then Return For $i = $iBase To 9 If StringInStr($iNumber, $i) Then Return SetError(1) Next EndFunc ; _ValidateNumBaseExamples$ret = _DecToNumBase(7687,3) MsgBox(0, "Ternary to Decimal", $ret &@LF & _NumBaseToDec($ret, 3)) $ret = _NumBaseToNumBase(71543, 8, 7) MsgBox(0, "71543 Octal to Septenary", $ret) Edited October 2, 2011 by czardas operator64 ArrayWorkshop
czardas Posted October 18, 2011 Author Posted October 18, 2011 (edited) Creating an Array from CSV This has been done thousands of times before, but here's my take on it anyway, CSVSplit.au3expandcollapse popup; #FUNCTION# ============================================================================ ; Name...........: _CSVSplit ; Description ...: Converts a string in CSV format to a two dimensional array (see comments) ; Syntax.........: _ArrayToCSV ( $aArray [, $sDelim ] ) ; Parameters ....: $aArray - The array to convert ; $sDelim - Optional - Delimiter set to comma by default (see 2nd comment) ; Return values .: Success - Returns a two dimensional array or a one dimensional array (see 1st comment) ; Failure - Sets @error to: ; |@error = 1 - First parameter is not a valid string ; |@error = 2 - Second parameter is not a valid string ; |@error = 3 - Could not find suitable delimiter replacements ; Author ........: czardas ; Comments ......; Returns a one dimensional array if the input string does not contain the delimiter string ; ; Some CSV formats use semicolon as a delimiter instead of a comma ; ; Set the second parameter to @TAB To convert to TSV ; ======================================================================================== Func _CSVSplit($string, $sDelim = ",") ; Parses csv string input and returns a one or two dimensional array If (Not IsString($string)) Or ($string = "") Then Return SetError(1, 0, 0) ; Invalid string If (Not IsString($sDelim)) Or ($sDelim = "") Then Return SetError(2, 0, 0) ; Invalid string Local $iOverride = 255, $asDelim[3] ; $asDelim => replacements for comma, new line and double quote For $i = 0 To 2 $asDelim[$i] = _GetSubstitute($string, $iOverride, $sDelim) ; Choose a suitable substitution character If @error Then Return SetError(3, 0, 0) ; String contains too much variety Next $iOverride = 0 Local $aArray = StringRegExp($string, '\A[^"]+|("+[^"]+)|"+\z', 3) ; Split string using double quotes delim ... largest match $string = "" Local $iBound = UBound($aArray) For $i = 0 To $iBound -1 $iOverride += StringInStr($aArray[$i], '"', 0, -1) ; Increment by the number of adjacent double quotes per element If Mod ($iOverride +2, 2) = 0 Then ; Acts as an on/off switch $aArray[$i] = StringReplace($aArray[$i], $sDelim, $asDelim[0]) ; Replace comma delimeters $aArray[$i] = StringRegExpReplace($aArray[$i], "(\r\n)|[\r\n]", $asDelim[1]) ; Replace new line delimeters EndIf $aArray[$i] = StringReplace($aArray[$i], '""', $asDelim[2]) ; Replace double quote pairs $aArray[$i] = StringReplace($aArray[$i], '"', '') ; Delete enclosing double quotes - not paired $aArray[$i] = StringReplace($aArray[$i], $asDelim[2], '"') ; Reintroduce double quote pairs as single characters $string &= $aArray[$i] ; Rebuild the string, which includes two different delimiters Next $iOverride = 0 $aArray = StringSplit($string, $asDelim[1], 2) ; Split to get rows $iBound = UBound($aArray) Local $aCSV[$iBound][2], $aTemp For $i = 0 To $iBound -1 $aTemp = StringSplit($aArray[$i], $asDelim[0]) ; Split to get row items If Not @error Then If $aTemp[0] > $iOverride Then $iOverride = $aTemp[0] ReDim $aCSV[$iBound][$iOverride] ; Add columns to accomodate more items EndIf EndIf For $j = 1 To $aTemp[0] If StringLen($aTemp[$j]) Then If Not StringRegExp($aTemp[$j], '[^"]') Then ; Field only contains double quotes $aTemp[$j] = StringTrimLeft($aTemp[$j], 1) ; Delete enclosing double quote single char EndIf $aCSV[$i][$j -1] = $aTemp[$j] ; Populate each row EndIf Next Next If $iOverride > 1 Then Return $aCSV ; Multiple Columns Else For $i = 0 To $iBound -1 If StringLen($aArray[$i]) And (Not StringRegExp($aArray[$i], '[^"]')) Then ; Only contains double quotes $aArray[$i] = StringTrimLeft($aArray[$i], 1) ; Delete enclosing double quote single char EndIf Next Return $aArray ; Single column EndIf EndFunc ;==> _CSVSplit ; #FUNCTION# ============================================================================ ; Name...........: _ArrayToCSV ; Description ...: Converts a two dimensional array to CSV format ; Syntax.........: _ArrayToCSV ( $aArray [, $sDelim [, $sNewLine ]] ) ; Parameters ....: $aArray - The array to convert ; $sDelim - Optional - Delimiter set to comma by default (see comments) ; $sNewLine - Optional - New Line set to @LF by default (see comments) ; Return values .: Success - Returns a string in CSV format ; Failure - Sets @error to: ; |@error = 1 - First parameter is not a valid array ; |@error = 2 - Second parameter is not a valid string ; |@error = 3 - Third parameter is not a valid string ; Author ........: czardas ; Comments ......; One dimensional arrays are returned as multiline text (without delimiters) ; ; Some users may need to set the second parameter to semicolon to return the prefered CSV format ; ; To convert to TSV use @TAB for the second parameter ; ; Some users may wish to set the third parameter to @CRLF ; ======================================================================================== Func _ArrayToCSV($aArray, $sDelim = ",", $sNewLine = @LF) If (Not IsArray($aArray)) Or (Ubound($aArray, 0) > 2) Then Return SetError(1, 0 ,"") If Not IsString($sDelim) Then Return SetError(2, 0 ,"") If Not IsString($sNewLine) Then Return SetError(3, 0 ,"") Local $iRows = UBound($aArray), $string = "" If Ubound($aArray, 0) = 2 Then ; Check if the array has two dimensions Local $iCols = UBound($aArray, 2) For $i = 0 To $iRows -1 For $j = 0 To $iCols -1 If StringRegExp($aArray[$i][$j], '["' & $sDelim & ']') Then $aArray[$i][$j] = '"' & StringReplace($aArray[$i][$j], '"', '""') & '"' EndIf $string &= $aArray[$i][$j] & $sDelim Next $string = StringTrimRight($string, StringLen($sDelim)) & $sNewLine Next Else ; The delimiter is not needed For $i = 0 To $iRows -1 If StringInStr($aArray[$i], '"') Then $aArray[$i] = '"' & StringReplace($aArray[$i], '"', '""') & '"' EndIf $string &= $aArray[$i] & $sNewLine Next EndIf Return StringTrimRight($string, StringLen($sNewLine)) ; Delete any newline characters added to the end of the string EndFunc ;==> _ArrayToCSV ; #FUNCTION# ============================================================================ ; Name...........: _GetSubstitute ; Description ...: Searches for an AscII char to be used for substitution, ie one not contained within the string ; Syntax.........: _GetSubstitute($string, ByRef $iCountdown) ; Parameters ....: $string - The string of characters to avoid ; $iCountdown - The AscII number to begin checking => Best set to 255 on the first run ; $sAvoid - Optional string of characters to avoid ; Return values .: Success - Returns a suitable substitution character not found within the first parameter ; Failure - Sets @error to 1 => No substitution character available ; Author ........: czardas ; Comments ......; This function is connected to the function _CSVSplit and was not intended for general use ; ; $iCountdown is returned ByRef to avoid selecting the same character on subsequent calls ; ======================================================================================== Func _GetSubstitute($string, ByRef $iCountdown, $sAvoid = "") If $iCountdown < 1 Then Return SetError(1, 0, "") ; Out of options Local $sTestChar For $i = $iCountdown To 1 Step -1 $sTestChar = Chr($i) $iCountdown -= 1 If Not StringInStr($string, $sTestChar, 2) Then ; Some characters may interfere with parsing => If ($i = 34) Or ($i = 13) Or ($i = 10) Or StringInStr($sAvoid, $sTestChar) Then ContinueLoop Return $sTestChar EndIf Next Return SetError(1, 0, "") ; Out of options EndFunc ;==> _GetSubstitute Example#include <CSVSplit.au3> #include <Array.au3> ; For _ArrayDisplay _Example() Func _Example() Local $aCSV = _CSVSplit( _ ; csv example taken from en.wikipedia.org/wiki/Comma-separated_values#Example 'Year,Make,Model,Description,Price' & @LF & _ '1997,Ford,E350,"ac, abs, moon",3000.00' & @LF & _ '1999,Chevy,"Venture ""Extended Edition""","",4900.00' & @LF & _ '1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00' & @LF & _ '1996,Jeep,Grand Cherokee,"MUST SELL!' & @LF & _ 'air, moon roof, loaded",4799.00') If Not @error Then _ArrayDisplay($aCSV) MsgBox(0, "Converted back to CSV", _ArrayToCSV($aCSV)) ElseIf @error = 1 Then MsgBox(0, "Error Code " & @error, "1st parameter - Invalid string") ElseIf @error = 2 Then MsgBox(0, "Error Code " & @error, "2nd parameter - Delimiter not a valid string") ElseIf @error = 3 Then MsgBox(0, "Error Code " & @error, "Failed to find suitable delimiter replacements") EndIf EndFuncYou have the option to set the delimiter, so you can convert from CSV to TSV etc... The function should be able to handle all csv formats. Make sure your CSV, (or TSV) is correctly formatted. NOTEWhen converting from array to CSV, double quotes will not be added to any fields unless absolutely necessary. This means that double quotes enclosing an empty field in an original CSV will dissapear after conversion. I didn't see it as being necessary to enclose all fields, but it's easy enough to make such changes when required. Edited November 5, 2011 by czardas operator64 ArrayWorkshop
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now