Sign in to follow this  
Followers 0
SumTingWong

_CalcRPN UDF

19 posts in this topic

#1 ·  Posted (edited)

I saw a discussion a few days ago about a Calc function to evaluate string expressions and thought it would be perfect to do an RPN calculator implementation in AutoIT so here it is:

This UDF supports the following operators:

+ - * / ^

and formulas:

MOD SIN COS TAN ASIN ACOS ATAN SQRT LOG EXP

It doesn't check for well-formed expressions. That means it's up to you to check for things such as matching brackets... in your infix expression before passing it to _CalcRPN

For more information on Reverse Polish Notation, look here

EDIT1: added some comments

EDIT2: added some more comments

EDIT3: added support for formulas

EDIT4: streamlined the operand/formula parsing section to support non decimal values so ezzetabi can add his functions :idiot:

EDIT5: Fixed a bug with number conversion. Now we convert to number, if required, at eval time. This way we preserve non-decimal characters such as hex strings. However, this means that it's now up to you to convert operand(s) to a format that's compatible with your formula or operator.

Dim $n1, $n2

; usage example
; calculate using AutoIT
$n1 = (Log(1000+5*6)*Exp(ACos(5))/Sqrt(8))*(Sin(1)*Cos(1)*Tan(1))/(5*(20-7+Mod(20,35)))
; calculate using _CalcRPN
$n2 = _CalcRPN("(Log(1000+5*6)*Exp(ACos(5))/Sqrt(8))*(Sin(1)*Cos(1)*Tan(1))/(5*(20-7+Mod(20,35)))")
; both results should match
MsgBox(4096, "Test" , "AutoIT =  " & $n1 & @LF & _
                      "_CalcRPN = " & $n2);### Debug MSGBOX 

; remove value from stack
Func _PopStack(ByRef $vaStack)
   Local $vValue
  
   If Not IsArray($vaStack) Then 
      SetError(1)  ; invalid stack
   Else
      $vValue = $vaStack[UBound($vaStack)-1]
      If UBound($vaStack) > 1 Then 
         ReDim $vaStack[UBound($vaStack)-1]
         $vaStack[0] = UBound($vaStack)-1
      Else
         $vaStack[0] = 0
      EndIf
   EndIf
   Return $vValue
EndFunc

; add value to stack
Func _PushStack(ByRef $vaStack, $vValue)
   If Not IsArray($vaStack) Then 
      SetError(1)  ; invalid stack
      Return 0
   EndIf   
   ReDim $vaStack[UBound($vaStack)+1]
   $vaStack[UBound($vaStack)-1] = $vValue
  ; first element contains stack count
   $vaStack[0] = UBound($vaStack)-1
   Return 1
EndFunc

; Reverse Polish Notation calculator
Func _CalcRPN($sInfix)
   Local $sOps = "+-*/^"
   Local $sFormulas = "MOD SIN COS TAN ASIN ACOS ATAN SQRT LOG EXP"
   Local $vaStack1[1]
   Local $vaStack2[1]
   Local $sChar
   Local $sNextChar
   Local $n, $i
   Local $nOperand1
   Local $nOperand2
   Local $sOp

   $sInfix = StringStripWS($sInfix, 8)
   For $n = 1 To StringLen($sInfix)
      $sChar = StringMid($sInfix, $n, 1)
      If (Asc($sChar) >= 48 And Asc($sChar) <= 57) Or _
         (Asc($sChar) >= 65 And Asc($sChar) <= 90) Or _
         (Asc($sChar) >= 97 And Asc($sChar) <= 122) Or _
         $sChar = "." Then
        ; if alphanumeric character, assume operand or formula
        ; get remaining characters
         While $n <= StringLen($sInfix)
            $sNextChar = StringMid($sInfix, $n+1, 1)
            If (Asc($sNextChar) >= 48 And Asc($sNextChar) <= 57) Or _
               (Asc($sNextChar) >= 65 And Asc($sNextChar) <= 90) Or _
               (Asc($sNextChar) >= 97 And Asc($sNextChar) <= 122) Or _
               $sNextChar = "." Then
               $sChar = $sChar & $sNextChar
               $n = $n+1
            Else
               ExitLoop
            EndIf
         WEnd
         If StringInStr($sFormulas, $sChar) Then
          ; push formula name into stack2
            _PushStack($vaStack2, $sChar)
         Else
          ; push operand into stack1
            _PushStack($vaStack1, $sChar)
         EndIf
      ElseIf $sChar = "(" Then
        ; push left bracket into stack2
         _PushStack($vaStack2, $sChar)
      ElseIf $sChar = ")" Then
        ; pop operators from stack2 and push them into stack1 
        ; until left bracket is reached in stack2
         While $vaStack2[0] > 0 And $vaStack2[$vaStack2[0]] <> "("
            _PushStack($vaStack1, _PopStack($vaStack2))
         WEnd
        ; pop left bracket from stack2
         If $vaStack2[$vaStack2[0]] = "(" Then _PopStack($vaStack2)
      Else
        ; get position of operator. Higher position = higher precedence
         $i = StringInStr($sOps, $sChar)
         If $i > 0 Then
          ; pop higher precedence operators and formulas from stack2
          ; and push them into stack1
            While $vaStack2[0] > 0 And _
                  StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) > 0 Or _
                  StringInStr($sOps, $vaStack2[$vaStack2[0]]) >= $i
               _PushStack($vaStack1, _PopStack($vaStack2))
            WEnd
          ; push current operator into stack2
            _PushStack($vaStack2, $sChar)
         EndIf
      EndIf
   Next
  ; pop remaining operators from stack2 and push them into stack1
   While $vaStack2[0] > 0
      _PushStack($vaStack1, _PopStack($vaStack2))
   WEnd

  ; clear stack2
   Redim $vaStack2[1]
  ; copy stack1 to stack2 so the stack order is reversed
   For $n = $vaStack1[0] To 1 Step -1
      _PushStack($vaStack2, $vaStack1[$n])
   Next

  ; clear stack1
   ReDim $vaStack1[1]
   
   While $vaStack2[0] > 0
      If StringInStr($sOps, $vaStack2[$vaStack2[0]]) Or _
         StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) Then
        ; if top item is an operator or formula then pop it
         $sOp = _PopStack($vaStack2)
         Select
         Case $sOp = "+"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, $nOperand1+$nOperand2)
         Case $sOp = "-"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, $nOperand1-$nOperand2)
         Case $sOp = "*"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, $nOperand1*$nOperand2)
         Case $sOp = "/"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, $nOperand1/$nOperand2)
         Case $sOp = "^"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, $nOperand1^$nOperand2)
         Case $sOp = "MOD"
          ; pop the second operand
            $nOperand2 = Number(_PopStack($vaStack1))
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Mod($nOperand1, $nOperand2))
         Case $sOp = "SIN"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Sin($nOperand1))
         Case $sOp = "COS"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Cos($nOperand1))
         Case $sOp = "TAN"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Tan($nOperand1))
         Case $sOp = "ASIN"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, ASin($nOperand1))
         Case $sOp = "ACOS"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, ACos($nOperand1))
         Case $sOp = "ATAN"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, ATan($nOperand1))
         Case $sOp = "SQRT"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Sqrt($nOperand1))
         Case $sOp = "LOG"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Log($nOperand1))
         Case $sOp = "EXP"
          ; pop the first operand
            $nOperand1 = Number(_PopStack($vaStack1))
          ; perform calculation and push result into stack1
            _PushStack($vaStack1, Exp($nOperand1))
         EndSelect
      Else
        ; pop operand from stack2 and push into stack1
         _PushStack($vaStack1, _PopStack($vaStack2))
      EndIf
   WEnd
  ; the last remaining item in stack1 is the final result
   Return _PopStack($vaStack1)
EndFunc

Attached is the source file if you don't want to copy/paste

Rpn.au3

Edited by pacman

Share this post


Link to post
Share on other sites

Hmmm .. Reverse Polish! Exciting looks, this does :idiot:

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Thanks PacMan, as usual you are the best. :idiot:

Great program!

Can I use this code in Run! ?

Edited by ezzetabi

Share this post


Link to post
Share on other sites

Thanks PacMan, as usual you are the best. :idiot:

Great program!

Can I use this code in Run! ?

<{POST_SNAPBACK}>

Of course, yours to do with as you please.

Share this post


Link to post
Share on other sites

hmmm, a little problem.. in my mathematics program, u recomend me using this little neat script, except it's like teh same thing as doing

$lala= GUIRead ($something)
$hehe= Number ($lala)
MsgBox (0, "lala", $hehe)

if i use it, it still returns weird numbers, i use it like this:

Case $get=$button_26
Dim $n

$n = GUIRead ($edit_23)

$n = _CalcRPN($edit_23)
GUICtrlSetData ($edit_23, $n, 1)

lets just say all those undeclared variables in there are declared, it still returns the very wrong answer...

suggestions?


FootbaG

Share this post


Link to post
Share on other sites

Duhh!

Use this:

Case $get=$button_26
$sum = GUIRead ($edit_23)

$result = _CalcRPN($sum)
GUICtrlSetData ($edit_23, $result, 1)

I changed the variables because that's the only way to avoid these bugs.

Share this post


Link to post
Share on other sites

tanks slim :idiot: im just a little tired today...


FootbaG

Share this post


Link to post
Share on other sites

pacman.

How do you think is the best way of adding operators that act over a single number?

E.g. sin, cos, !, ect...

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

pacman.

How do you think is the best way of adding operators that act over a single number?

E.g. sin, cos, !, ect...

<{POST_SNAPBACK}>

@ezzetabi - see my updated code above.

I was bored at work yesterday so I started on coding a Reversi/Othello game in AutoIT. Got the UI done and been researching on a decent algorithm that I can implement in AutoIT but I can't decide.

So far, I have looked at c sources for a combination of weighted board, alpha-beta, min-max but it's a pain trying to unravel and translate to AutoIT. Can anyone suggest a decent source for an average Reversi/Othello algo that can be implemented in AutoIT?

Edited by pacman

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

I added the toHex, toOct and toBin operators that converts decimal numbers to hex, oct and bin bases.

I'd like to add also their opposites, like frOct and frBin that would convert non decimal values in decimal... But I can't find a way to detect if the letters are digits of hex numbers or operators... :idiot:

PacMan, do you have any suggestion?

MsgBox(0, '', _CalcRPN('fat(7)'))

Func _PopStack(ByRef $vaStack)
   Local $vValue
   
   If Not IsArray($vaStack) Then
      SetError(1); invalid stack
   Else
      $vValue = $vaStack[UBound($vaStack) - 1]
      If UBound($vaStack) > 1 Then
         ReDim $vaStack[UBound($vaStack) - 1]
         $vaStack[0] = UBound($vaStack) - 1
      Else
         $vaStack[0] = 0
      EndIf
   EndIf
   Return $vValue
EndFunc  ;==>_PopStack

; Add value to stack
Func _PushStack(ByRef $vaStack, $vValue)
   If Not IsArray($vaStack) Then
      SetError(1); invalid stack
      Return 0
   EndIf
   ReDim $vaStack[UBound($vaStack) + 1]
   $vaStack[UBound($vaStack) - 1] = $vValue
  ; first element contains stack count
   $vaStack[0] = UBound($vaStack) - 1
   Return 1
EndFunc  ;==>_PushStack

; Reverse Polish Notation calculator
Func _CalcRPN($sInfix)
   Local $sOps = "+-*/^"
   Local $sFormulas = "MOD SIN COS TAN ASIN ACOS ATAN LOG EXP ToHex ToBin ToOct Fat"
   Local $vaStack1[1]
   Local $vaStack2[1]
   Local $sChar
   Local $sNextChar
   Local $n, $i
   Local $nOperand1
   Local $nOperand2
   Local $sOp
   Local $bNum = 0
   
   $sInfix = StringStripWS($sInfix, 8)
   For $n = 1 To StringLen($sInfix)
      $sChar = StringMid($sInfix, $n, 1)
      If (Asc($sChar) >= 48 And Asc($sChar) <= 57) Or $sChar = "." Then
        ; if character is a digit or a dot, assume operand
        ; get operand
         While $n <= StringLen($sInfix)
            $sNextChar = StringMid($sInfix, $n + 1, 1)
            If (Asc($sNextChar) >= 48 And Asc($sNextChar) <= 57) Or $sNextChar = "." Then
               $sChar = $sChar & $sNextChar
               $n = $n + 1
            Else
               ExitLoop
            EndIf
         Wend
        ; push operand into stack1
         _PushStack($vaStack1, Number($sChar))
      ElseIf (Asc($sChar) >= 65 And Asc($sChar) <= 90) Or (Asc($sChar) >= 97 And Asc($sChar) <= 122) Then
        ; if character is a letter, assume formula
        ; get formula name
         While $n <= StringLen($sInfix)
            $sNextChar = StringMid($sInfix, $n + 1, 1)
            If (Asc($sNextChar) >= 65 And Asc($sNextChar) <= 90) Or (Asc($sNextChar) >= 97 And Asc($sNextChar) <= 122) Then
               $sChar = $sChar & $sNextChar
               $n = $n + 1
            Else
               ExitLoop
            EndIf
         Wend
        ; push formula name into stack2
         _PushStack($vaStack2, $sChar)
      ElseIf $sChar = "(" Then
        ; push left bracket into stack2
         _PushStack($vaStack2, $sChar)
      ElseIf $sChar = ")" Then
        ; pop operators from stack2 and push them into stack1
        ; until left bracket is reached in stack2
         While $vaStack2[0] > 0 And $vaStack2[$vaStack2[0]] <> "("
            _PushStack($vaStack1, _PopStack($vaStack2))
         Wend
        ; pop left bracket from stack2
         If $vaStack2[$vaStack2[0]] = "(" Then _PopStack($vaStack2)
      Else
        ; get position of operator. Higher position = higher precedence
         $i = StringInStr($sOps, $sChar)
         If $i > 0 Then
          ; pop higher precedence operators and formulas from stack2
          ; and push them into stack1
            While $vaStack2[0] > 0 And _
                  StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) > 0 Or _
                  StringInStr($sOps, $vaStack2[$vaStack2[0]]) >= $i
               _PushStack($vaStack1, _PopStack($vaStack2))
            Wend
          ; push current operator into stack2
            _PushStack($vaStack2, $sChar)
         EndIf
      EndIf
   Next
  ; pop remaining operators from stack2 and push them into stack1
   While $vaStack2[0] > 0
      _PushStack($vaStack1, _PopStack($vaStack2))
   Wend
   
  ; clear stack2
   ReDim $vaStack2[1]
  ; copy stack1 to stack2 so the stack order is reversed
   For $n = $vaStack1[0] To 1 Step - 1
      _PushStack($vaStack2, $vaStack1[$n])
   Next
   
  ; clear stack1
   ReDim $vaStack1[1]
   
   While $vaStack2[0] > 0
      If StringInStr($sOps, $vaStack2[$vaStack2[0]]) Or _
            StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) Then
        ; if top item is an operator or formula then pop it
         $sOp = _PopStack($vaStack2)
         Select
            Case $sOp = "+"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 + $nOperand2)
            Case $sOp = "-"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 - $nOperand2)
            Case $sOp = "*"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 * $nOperand2)
            Case $sOp = "/"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 / $nOperand2)
            Case $sOp = "^"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 ^ $nOperand2)
            Case $sOp = "MOD"
              ; pop the second operand
               $nOperand2 = _PopStack($vaStack1)
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Mod($nOperand1, $nOperand2))
            Case $sOp = "SIN"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Sin($nOperand1))
            Case $sOp = "COS"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Cos($nOperand1))
            Case $sOp = "TAN"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Tan($nOperand1))
            Case $sOp = "ASIN"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ASin($nOperand1))
            Case $sOp = "ACOS"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ACos($nOperand1))
            Case $sOp = "ATAN"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ATan($nOperand1))
            Case $sOp = "LOG"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Log($nOperand1))
            Case $sOp = "EXP"
              ; pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Exp($nOperand1))
            Case $sOp = "ToHex"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 16))
            Case $sOp = "ToBin"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 2))
            Case $sOp = "ToOct"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 8))
            Case $sOp = "Fat"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Fat($nOperand1))
         EndSelect
      Else
        ; pop operand from stack2 and push into stack1
         _PushStack($vaStack1, _PopStack($vaStack2))
      EndIf
   Wend
  ; the last remaining item in stack1 is the final result
   Return _PopStack($vaStack1)
EndFunc  ;==>_CalcRPN
#region;_Any2Dec, _Dec2Any
Func _Any2Dec($sNum, $iBase)
   If $iBase < 2 Or $iBase > 36 Then Return 0
   
   $sNum = StringReplace($sNum, ' ', '')
   Local $iSep, $fOutPut = 0, $iDigit, $c, $iL, $bNeg = 0, $sValidDigits
   
   If StringLeft($sNum, 1) = '-' Then
      $bNeg = 1
      $sNum = StringTrimLeft($sNum, 1)
   EndIf
   
   If $iBase <= 10 Then
      For $c = 0 to ($iBase - 1)
         $sValidDigits = $sValidDigits & '|' & $c
      Next
      $sValidDigits = StringTrimLeft($sValidDigits, 1)
   Else
      $sValidDigits = '0|1|2|3|4|5|6|7|8|9'
      For $c = 10 To $iBase - 1
         $sValidDigits = $sValidDigits & '|' & Chr(55 + $c)
      Next
   EndIf
   
   If Not _Only($sNum, $sValidDigits & '|.') Then
      Return 0
   EndIf
   
   $iSep = StringInStr($sNum, '.')
   If $iSep <> 0 Then
      $sNum = StringReplace($sNum, '.', '')
      $iSep = (StringLen($sNum) - $iSep + 1) * - 1
   EndIf
   
   $iL = StringLen($sNum)
   For $c = 1 To StringLen($sNum)
      $iDigit = StringMid($sNum, $iL - $c + 1, 1)
      If Asc($iDigit) >= 65 Then
         $iDigit = Asc($iDigit) - 55
      Else
         $iDigit = Int($iDigit)
      EndIf
      $fOutPut = $fOutPut + $iDigit * $iBase^ ($iSep + $c - 1)
   Next
   
   If $bNeg = 1 Then $fOutPut = $fOutPut * - 1
   Return $fOutPut
EndFunc  ;==>_Any2Dec

Func _Only($string, $aItems)
   $aItems = StringSplit($aItems, '|')
   Local $bOk, $c, $c2, $char
   
   For $c = 1 To StringLen($string)
      $char = StringMid($string, $c, 1)
      $bOk = 0
      For $c2 = 1 To $aItems[0]
         If $aItems[$c2] = $char Then
            $bOk = 1
            ExitLoop
         EndIf
      Next
      
      If $bOk = 0 Then
         Return 0
      EndIf
   Next
   
   Return 1
EndFunc  ;==>_Only

Func _Dec2Any($sNum, $iBase)
   If $iBase < 2 Or $iBase > 36 Then Return 0
   
   $sNum = StringReplace($sNum, ' ', '')
   Local $aParts[3], $iDigit, $sOutPut, $bNeg = 0
   
   If StringLeft($sNum, 1) = '-' Then
      $bNeg = 1
      $sNum = StringTrimLeft($sNum, 1)
   EndIf
   
   If StringInStr($sNum, '.') Then
      $aParts = StringSplit($sNum, '.')
      $aParts[1] = Int($aParts[1])
      $aParts[2] = $aParts[2] / 10 ^ StringLen($aParts[2])
   Else
      $aParts[1] = Int($sNum)
      $aParts[2] = 0
   EndIf
   
   While $aParts[1] <> 0
      $iDigit = Mod($aParts[1], $iBase)
      If $iDigit >= 10 Then
         $iDigit = Chr(55 + $iDigit);65 is A
      EndIf
      $sOutPut = $iDigit & $sOutPut
      $aParts[1] = Int($aParts[1] / $iBase)
   Wend
   If $aParts[2] <> 0 Then
      $sOutPut = $sOutPut & '.'
      While $aParts[2] <> 0
         $aParts[2] = $aParts[2] * $iBase
         $iDigit = Int($aParts[2])
         $aParts[2] = $aParts[2] - $iDigit
         If $iDigit >= 10 Then
            $iDigit = Chr(55 + $iDigit);65 is A
         EndIf
         $sOutPut = $sOutPut & $iDigit
      Wend
   EndIf
   
   If $bNeg = 1 Then $sOutPut = '-' & $sOutPut
   Return $sOutPut
EndFunc  ;==>_Dec2Any
#endregion

Func _Fat($iNum)
   If $iNum<=0 Then Return 0
   
   $iNum = Int($iNum)
   
   Local $c, $iOutPut = 1
   
   For $c = 2 to $iNum
      $iOutPut = $iOutPut * $c
   Next
   
   Return $iOutPut
EndFunc
Edited by ezzetabi

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

@ezzetabi

See my updated code above. I have re-uploaded the attachment as well if you don't want to copy/paste

Good to see you are extending it with more functions. :idiot:

Edited by pacman

Share this post


Link to post
Share on other sites

Oh dear... You are indeed the best.

If I'll ever go to London I'll drop a message, you deserve a gift...

(The same in the case you come here to Italy, eh?)

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

Oh dear... You are indeed the best.

If I'll ever go to London I'll drop a message, you deserve a gift...

(The same in the case you come here to Italy, eh?)

<{POST_SNAPBACK}>

@ezzetabi

Have a look at my EDIT5 above for a small update which will affect you.

My wife and I are going to Rome for a "romantic" weekend this coming weekend but no gifts are neccessary. I will just enjoy your beautiful country instead. :idiot:

Edited by pacman

Share this post


Link to post
Share on other sites

About edit5. I fixed the problem before reading it, but thanks about the 4th... I could not do it alone.

About Rome, a fine city indeed... but a little too away from where am I for confortable gift giving... Well. I hope you enjoy your stay, welcome.

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

And here the PacManEz Calculator!

(Notice how the PacMan name prevales.... :idiot: )

Next post of mine.

Edited by ezzetabi

Share this post


Link to post
Share on other sites

Cool.... :idiot:

Have a look at my updated code because I think you need to do number conversion for at least some of the core ops and formulas. I couldn't get a correct value with my example expression when I took the number conversion out.

Share this post


Link to post
Share on other sites

#18 ·  Posted (edited)

AHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAH

I had all needed Number() but I think it was not the problem...

The main problem is that I removed Sqrt()... :idiot:

I dislike Sqrt since it is just a bad copy of ^

Dim $n1, $n2, $n3

; usage example
; calculate using AutoIT
$n1 = (Log(1000+5*6)*Exp(ACos(5))/8)*(Sin(1)*Cos(1)*Tan(1))/(5*(20-7+Mod(20,35)))
; calculate using _CalcRPN
$n2 = _CalcRPN("(Log(1000+5*6)*Exp(ACos(5))/8)*(Sin(1)*Cos(1)*Tan(1))/(5*(20-7+Mod(20,35)))")
; both results should match
$n3 = _CalcRPN('ToHex('&$n2&')')

MsgBox(4096, "Test" , "AutoIT       "&@tab&"= " & $n1 & @LF & _
                      "_CalcRPN"&@TAB&"= " & $n2 & @lf & _
                      'In hex:      '& @tab &'=' &$n3);### Debug MSGBOX

#region;PacManEz Calculator
; Reverse Polish Notation calculator
Func _CalcRPN($sInfix)
   Local $sOps = "+-*/^"
   Local $sFormulas = "MOD SIN COS TAN ASIN ACOS ATAN LOG EXP FAT ToHex ToBin ToOct FrHex FrOct FrBin"
   Local $vaStack1[1]
   Local $vaStack2[1]
   Local $sChar
   Local $sNextChar
   Local $n, $i
   Local $nOperand1
   Local $nOperand2
   Local $sOp
   Local $bNum = 0
   
   $sInfix = StringStripWS($sInfix, 8)
   For $n = 1 To StringLen($sInfix)
      $sChar = StringMid($sInfix, $n, 1)
      If (Asc($sChar) >= 48 And Asc($sChar) <= 57) Or _
         (Asc($sChar) >= 65 And Asc($sChar) <= 90) Or _
         (Asc($sChar) >= 97 And Asc($sChar) <= 122) Or _
         $sChar = "." Then
      ; if alphanumeric character, assume operand or formula
      ; get remaining characters
         While $n <= StringLen($sInfix)
            $sNextChar = StringMid($sInfix, $n+1, 1)
            If (Asc($sNextChar) >= 48 And Asc($sNextChar) <= 57) Or _
               (Asc($sNextChar) >= 65 And Asc($sNextChar) <= 90) Or _
               (Asc($sNextChar) >= 97 And Asc($sNextChar) <= 122) Or _
               $sNextChar = "." Then
               $sChar = $sChar & $sNextChar
               $n = $n+1
            Else
               ExitLoop
            EndIf
         WEnd
         If StringInStr($sFormulas, $sChar) Then
        ; push formula name into stack2
            _PushStack($vaStack2, $sChar)
         Else
        ; push operand into stack1
          ;_PushStack($vaStack1, Number($sChar))
            _PushStack($vaStack1, $sChar)
         EndIf
      ElseIf $sChar = "(" Then
      ; push left bracket into stack2
         _PushStack($vaStack2, $sChar)
      ElseIf $sChar = ")" Then
      ; pop operators from stack2 and push them into stack1 
      ; until left bracket is reached in stack2
         While $vaStack2[0] > 0 And $vaStack2[$vaStack2[0]] <> "("
            _PushStack($vaStack1, _PopStack($vaStack2))
         WEnd
      ; pop left bracket from stack2
         If $vaStack2[$vaStack2[0]] = "(" Then _PopStack($vaStack2)
      Else
      ; get position of operator. Higher position = higher precedence
         $i = StringInStr($sOps, $sChar)
         If $i > 0 Then
        ; pop higher precedence operators and formulas from stack2
        ; and push them into stack1
            While $vaStack2[0] > 0 And _
                  StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) > 0 Or _
                  StringInStr($sOps, $vaStack2[$vaStack2[0]]) >= $i
               _PushStack($vaStack1, _PopStack($vaStack2))
            WEnd
        ; push current operator into stack2
            _PushStack($vaStack2, $sChar)
         EndIf
      EndIf
   Next

  ; pop remaining operators from stack2 and push them into stack1
   While $vaStack2[0] > 0
      _PushStack($vaStack1, _PopStack($vaStack2))
   Wend
   
  ; clear stack2
   ReDim $vaStack2[1]
  ; copy stack1 to stack2 so the stack order is reversed
   For $n = $vaStack1[0] To 1 Step - 1
      _PushStack($vaStack2, $vaStack1[$n])
   Next
   
  ; clear stack1
   ReDim $vaStack1[1]
   
   While $vaStack2[0] > 0
      If StringInStr($sOps, $vaStack2[$vaStack2[0]]) Or _
            StringInStr($sFormulas, $vaStack2[$vaStack2[0]]) Then
        ; if top item is an operator or formula then pop it
         $sOp = _PopStack($vaStack2)
         Select
            Case $sOp = "+"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 + $nOperand2)
            Case $sOp = "-"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 - $nOperand2)
            Case $sOp = "*"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 * $nOperand2)
            Case $sOp = "/"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 / $nOperand2)
            Case $sOp = "^"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, $nOperand1 ^ $nOperand2)
            Case $sOp = "MOD"
              ; pop the second operand
               $nOperand2 = Number(_PopStack($vaStack1))
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Mod($nOperand1, $nOperand2))
            Case $sOp = "SIN"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Sin($nOperand1))
            Case $sOp = "COS"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Cos($nOperand1))
            Case $sOp = "TAN"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Tan($nOperand1))
            Case $sOp = "ASIN"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ASin($nOperand1))
            Case $sOp = "ACOS"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ACos($nOperand1))
            Case $sOp = "ATAN"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, ATan($nOperand1))
            Case $sOp = "LOG"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Log($nOperand1))
            Case $sOp = "EXP"
              ; pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, Exp($nOperand1))
            Case $sOp = "ToHex"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 16))
            Case $sOp = "ToBin"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 2))
            Case $sOp = "ToOct"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Dec2Any($nOperand1, 8))
            Case $sOp = "FrHex"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Any2Dec($nOperand1, 16))
            Case $sOp = "FrBin"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Any2Dec($nOperand1, 2))
            Case $sOp = "FrOct"
              ;pop the first operand
               $nOperand1 = _PopStack($vaStack1)
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Any2Dec($nOperand1, 8))
            Case $sOp = "Fat"
              ;pop the first operand
               $nOperand1 = Number(_PopStack($vaStack1))
              ; perform calculation and push result into stack1
               _PushStack($vaStack1, _Fat($nOperand1))
         EndSelect
      Else
        ; pop operand from stack2 and push into stack1
         _PushStack($vaStack1, _PopStack($vaStack2))
      EndIf
   Wend
  ; the last remaining item in stack1 is the final result
   Return _PopStack($vaStack1)
EndFunc  ;==>_CalcRPN

Func _PopStack(ByRef $vaStack)
   Local $vValue
   
   If Not IsArray($vaStack) Then
      SetError(1); invalid stack
   Else
      $vValue = $vaStack[UBound($vaStack) - 1]
      If UBound($vaStack) > 1 Then
         ReDim $vaStack[UBound($vaStack) - 1]
         $vaStack[0] = UBound($vaStack) - 1
      Else
         $vaStack[0] = 0
      EndIf
   EndIf
   Return $vValue
EndFunc  ;==>_PopStack

; Add value to stack
Func _PushStack(ByRef $vaStack, $vValue)
   If Not IsArray($vaStack) Then
      SetError(1); invalid stack
      Return 0
   EndIf
   ReDim $vaStack[UBound($vaStack) + 1]
   $vaStack[UBound($vaStack) - 1] = $vValue
  ; first element contains stack count
   $vaStack[0] = UBound($vaStack) - 1
   Return 1
EndFunc  ;==>_PushStack
#endregion

#region;Fatt func...
Func _Fat($iNum)
   If $iNum<=0 Then Return 0
   
   $iNum = Int($iNum)
   
   Local $c, $iOutPut = 1
   
   For $c = 2 to $iNum
      $iOutPut = $iOutPut * $c
   Next
   
   Return $iOutPut
EndFunc
#endregion

#region;_Any2Dec, _Dec2Any
Func _Any2Dec($sNum, $iBase)
   If $iBase < 2 Or $iBase > 36 Then Return 0
   
   $sNum = StringReplace($sNum, ' ', '')
   Local $iSep, $fOutPut = 0, $iDigit, $c, $iL, $bNeg = 0, $sValidDigits
   
   If StringLeft($sNum, 1) = '-' Then
      $bNeg = 1
      $sNum = StringTrimLeft($sNum, 1)
   EndIf
   
   If $iBase <= 10 Then
      For $c = 0 to ($iBase - 1)
         $sValidDigits = $sValidDigits & '|' & $c
      Next
      $sValidDigits = StringTrimLeft($sValidDigits, 1)
   Else
      $sValidDigits = '0|1|2|3|4|5|6|7|8|9'
      For $c = 10 To $iBase - 1
         $sValidDigits = $sValidDigits & '|' & Chr(55 + $c)
      Next
   EndIf
   
   If Not _Only($sNum, $sValidDigits & '|.') Then
      Return 0
   EndIf
   
   $iSep = StringInStr($sNum, '.')
   If $iSep <> 0 Then
      $sNum = StringReplace($sNum, '.', '')
      $iSep = (StringLen($sNum) - $iSep + 1) * - 1
   EndIf
   
   $iL = StringLen($sNum)
   For $c = 1 To StringLen($sNum)
      $iDigit = StringMid($sNum, $iL - $c + 1, 1)
      Select
         Case Asc($iDigit) >= 97
         $iDigit = Asc($iDigit) - 87
      CAse Asc($iDigit) >= 65 
        $iDigit = Asc($iDigit) - 55
      Case Else
         $iDigit = Int($iDigit)
      EndSelect
      
      $fOutPut = $fOutPut + $iDigit * $iBase^ ($iSep + $c - 1)
   Next
   
   If $bNeg = 1 Then $fOutPut = $fOutPut * - 1
   Return $fOutPut
EndFunc;==>_Any2Dec

Func _Only($string, $aItems)
   $aItems = StringSplit($aItems, '|')
   Local $bOk, $c, $c2, $char
   
   For $c = 1 To StringLen($string)
      $char = StringMid($string, $c, 1)
      $bOk = 0
      For $c2 = 1 To $aItems[0]
         If $aItems[$c2] = $char Then
            $bOk = 1
            ExitLoop
         EndIf
      Next
      
      If $bOk = 0 Then
         Return 0
      EndIf
   Next
   
   Return 1
EndFunc;==>_Only

Func _Dec2Any($sNum, $iBase)
   If $iBase < 2 Or $iBase > 36 Then Return 0
   
   $sNum = StringReplace($sNum, ' ', '')
   Local $aParts[3], $iDigit, $sOutPut, $bNeg = 0
   
   If StringLeft($sNum, 1) = '-' Then
      $bNeg = 1
      $sNum = StringTrimLeft($sNum, 1)
   EndIf
   
   If StringInStr($sNum, '.') Then
      $aParts = StringSplit($sNum, '.')
      $aParts[1] = Int($aParts[1])
      $aParts[2] = $aParts[2] / 10 ^ StringLen($aParts[2])
   Else
      $aParts[1] = Int($sNum)
      $aParts[2] = 0
   EndIf
   
   While $aParts[1] <> 0
      $iDigit = Mod($aParts[1], $iBase)
      If $iDigit >= 10 Then
         $iDigit = Chr(55 + $iDigit);65 is A
 &nbsp;    EndIf
      $sOutPut = $iDigit & $sOutPut
      $aParts[1] = Int($aParts[1] / $iBase)
   Wend
   If $aParts[2] <> 0 Then
      $sOutPut = $sOutPut & '.'
      While $aParts[2] <> 0
         $aParts[2] = $aParts[2] * $iBase
         $iDigit = Int($aParts[2])
         $aParts[2] = $aParts[2] - $iDigit
         If $iDigit >= 10 Then
            $iDigit = Chr(55 + $iDigit);65 is A
         EndIf
         $sOutPut = $sOutPut & $iDigit
      Wend
   EndIf
   
   If $bNeg = 1 Then $sOutPut = '-' & $sOutPut
   Return $sOutPut
EndFunc;==>_Dec2Any
#endregion
Edited by ezzetabi

Share this post


Link to post
Share on other sites

I love this script!

Is there a way to support scientific notation?

For example, 2e-1 currently gives the same result as 2-1.

Can it be modified to ignore the "e/E"?

Thank you kindly for the script. It is excellent as it stands.


Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0