udfBaseConvert
str udfBaseConvert (str, int, int)
;------------------------------------------------------------------------------------------------------------------------------------------
#DefineFunction udfBaseConvert (strNumFrom, intBaseFrom, intBaseTo)
; If any parameter contains invalid data, then return empty string.
If strNumFrom == "" || intBaseFrom > 36 || intBaseFrom < 2 || intBaseTo > 36 || intBaseTo < 2 Then Return ""
strNumFrom = StrUpper (strNumFrom)
If StrClean (strNumFrom, StrSub ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1, intBaseFrom), "", @TRUE, 1) != "" Then Return ""

arrTemp = ArrayFromStr (strNumFrom)
intElemsFrom = ArrInfo (arrTemp, 1)
intElemFromLast = intElemsFrom - 1
arrFrom = ArrDimension (intElemsFrom)
For intElem = 0 To intElemFromLast ; Decode characters to integer values and reverse direction.
   arrTemp[intElem] = Char2Num (arrTemp[intElem]) - 48
   arrFrom[intElemFromLast - intElem] = arrTemp[intElem] - 7 * (arrTemp[intElem] > 9)
Next
Drop (arrTemp)

intElemsTo = Int (1 + (intElemsFrom * Log10 (intBaseFrom) / Log10 (intBaseTo))) ; How many digits to expect?
intElemToLast = intElemsTo - 1

arrCum = ArrDimension (intElemsTo)
arrTo = ArrDimension (intElemsTo)
ArrInitialize (arrCum, 0)
ArrInitialize (arrTo, 0)
arrTo[0] = 1

For intElemFrom = 0 To intElemFromLast ; For each input digit.

   For intElemTo = 0 To intElemToLast  ; Add the input digit times ToBase to the cumulator.
      arrCum[intElemTo] = arrCum[intElemTo] + (arrTo[intElemTo] * arrFrom[intElemFrom])
      If arrCum[intElemTo] == 0 Then Continue

      intTemp = arrCum[intElemTo]
      intRemainder = 0
      intPow = intElemTo

      While intTemp >= intBaseTo ; Fix up any remainders in ToBase.
         intRemainder = intTemp / intBaseTo
         arrCum[intPow] = intTemp - intRemainder * intBaseTo
         intPow = intPow + 1
         arrCum[intPow] = arrCum[intPow] + intRemainder
         intTemp = arrCum[intPow]
      EndWhile
   Next

   ; Calculate the next power in ToBase format.
   For intElemTo = 0 To intElemToLast
      arrTo[intElemTo] = arrTo[intElemTo] * intBaseFrom
   Next

   For intElemTo = 0 To intElemToLast ; Check for any remainders.
      If arrTo[intElemTo] == 0 Then Continue
      intTemp = arrTo[intElemTo]
      intRemainder = 0
      intPow = intElemTo
      While intTemp >= intBaseTo ; Fix up any remainders.
         intRemainder = intTemp / intBaseTo
         arrTo[intPow] = intTemp - intRemainder * intBaseTo
         intPow = intPow + 1
         arrTo[intPow] = arrTo[intPow] + intRemainder
         intTemp = arrTo[intPow]
      EndWhile
   Next

Next

strNumTo = ""
While arrCum[intElemToLast] == 0 ; Skip leading zeroes.
   intElemToLast = intElemToLast - 1
EndWhile
For intElemTo = intElemToLast To 0 By -1 ; Encode integer values to characters and reverse direction.
   strNumTo = strNumTo : Num2Char (arrCum[intElemTo] + 48 + 7 * (arrCum[intElemTo] > 9))
Next
Drop (arrTo, arrCum, arrFrom)
Return strNumTo
;..........................................................................................................................................
; This UDF "udfBaseConvert" converts a given number string from one number base to another number base.
; Valid number bases are Base 2 to Base 36.
; The number string can be of any length.
;
; Parameters:
; strNumFrom    ... String of numbers and/or valid letters. Letters can be in upper or lower case.
; intBaseFrom   ... Integer number (2..36), the number base of the given number string.
; intBaseTo     ... Integer number (2..36), the number base to convert the given number string into.
;
; Return value:
; strNumTo      ... String of numbers and/or valid letters. Letters are in upper case.
;                   On parameter failure the return value is an empty string.
;..........................................................................................................................................
; The basic algorithm has been adapted from article
; "Base Conversion of Very Long Positive Integers", Andrew Jonkers, 20061019, Australia.
; http://www.codeproject.com/KB/recipes/BaseConverter.aspx
;
; Because of the many loops, the algorithm is a bit slow on large numbers, when interpreted in WinBatch language.
;..........................................................................................................................................
; (c)Detlev Dalitz.20100911.
;..........................................................................................................................................
#EndFunction
;------------------------------------------------------------------------------------------------------------------------------------------


; Test.

; Prepare Test Data.
strCSV = '"NumFrom","BaseFrom","BaseTo","NumTo","Comment"'
strCSV = strCSV : @CRLF : '"221",10,16,"DD",""'
strCSV = strCSV : @CRLF : '"CAFE",16,10,"51966",""'
strCSV = strCSV : @CRLF : '"CaFe",16,10,"51966",""'
strCSV = strCSV : @CRLF : '"11001011011001110111001000111001000010000",2,36,"MANYBITS",""'
strCSV = strCSV : @CRLF : '"1IAHEB54638829348494387383AD12",19,7,"136615251021020315364261540624105412221316016",""'
strCSV = strCSV : @CRLF : '"GHI",16,10,"","Invalid NumFrom in relation to BaseFrom."'
strCSV = strCSV : @CRLF : '"123",1,10,"","Invalid BaseFrom."'
strCSV = strCSV : @CRLF : '"123",10,37,"","Invalid BaseTo."'


; Write test data to temp file.
strFileTemp = FileCreateTemp ("WBT")
intBytesWritten = FilePut (strFileTemp, strCSV)
Drop (strCSV)

; Read test data into array.
arrTest = ArrayFileGetCSV (strFileTemp, 0)
intTestLast = ArrInfo (arrTest, 1) - 1
intTestFirst = 1 ; Skip header line.

; Do the test loop.
For intT = intTestFirst To intTestLast

   strNumTo = udfBaseConvert (arrTest[intT, 0], arrTest[intT, 1], arrTest[intT, 2])

   ; Prepare message output.
   If arrTest[intT, 4] != ""
      strComment = arrTest[intT, 4] ; Use comment from CSV file if any.
   Else ; Compare UDF result with given NumTo value from CSV file.
      strComment = "UDF result is invalid."
      If strNumTo == arrTest[intT, 3] Then strComment = "UDF result is valid."
   EndIf

   strMsgTitle = "Test " : intT : " of " : intTestLast : "|udfBaseConvert (strNumFrom, intBaseFrom, intBaseTo)"
   strMsgText = "From Number (Base " : arrTest[intT, 1] : "): " : @LF : arrTest[intT, 0]
   strMsgText = strMsgText : @LF : "To Number (Base " : arrTest[intT, 2] : "): " : @LF : strNumTo
   strMsgText = strMsgText : @LF : @LF : "Comment: " : @LF : strComment
   Pause (strMsgTitle, strMsgText)

Next

:CANCEL
If FileExist (strFileTemp) == 1 Then blnResult = FileDelete (strFileTemp)
Exit