<?php

/**
 * Math::Rpn
 *
 * Purpose:
 *
 *     Change Expression To RPN (Reverse Polish Notation), Evaluate RPN Expression
 *
 * Example:
 *
 *     $expression = "(2^3)+sin(30)-(!4)+(3/4)";
 *
 *     $rpn = new Math_Rpn();
 *     echo $rpn->calculate($expression,'deg',false);
 *
 *
 * @author   Maciej Szczytowski <admin@e-rower.pl>
 * @lector   Thomas Schmieder, 2009-07-14 02:48 <http://bitworks.de>
 -           replaced functions _stringToArray() and _keyExists against() 
 +           function tokenize()
 *           due to problem with contained shorter tokens in longer ones
 *           sample: e^pi throwed an error
 *
 +           Recognition of hexadecimal Numbers added in token recombiner
 *
 * @version  1.2.1
 * @package  math
 */

require_once 'PEAR.php';

#===================================================================================
# Classes
#===================================================================================
class Math_Rpn
{
    /**
     * Version Information
     *
     * @var    string
     * @access private
     */
    private $_version = '1.2.1';

    /**
     * Input expression
     *
     * @var    string
     * @access private
     */
    private $_input = '';

    /**
     * Array with input expression
     *
     * @var    array
     * @access private
     */
    private $_input_array = array();

    /**
     * Array with output expression in RPN
     *
     * @var    array
     * @access private
     */
    private $_output = array();

    /**
     * Temporary stack
     *
     * @var    array
     * @access private
     */
    private $_stack = array();

    /**
     * Value of expression
     *
     * @var    float
     * @access private
     */
    private $_value = 0.0;

    /**
     * Angle's unit: rad - true, deg - false
     *
     * @var    boolean
     * @access private
     */
    private $_angle = true;

    /**
     * PEAR Error
     *
     * @var    object PEAR
     * @access private
     */
    private $_error = null;

    /**
     * Timer
     *
     * @var    float
     * @access private
     */
    private $_timer = 0.0;

    /**
     * Array of operators whit priority and math function
     * operator => (name, priority, number of arguments, function)
     *
     * @var    array
     * @access private
     */
    private $_operation = array (
        '('    => array ('left bracket', 0),
        ')'    => array ('right bracket', 1),
        '+'    => array ('sum', 1, 2, '_sum'),
        '-'    => array ('difference', 1, 2, '_difference'),
        '*'    => array ('multiplication', 2, 2, '_multiplication'),
        '/'    => array ('division', 2, 2, '_division'),
        'r'    => array ('root', 3, 2, '_root'),
        '^'    => array ('power', 3, 2, '_power'),
        'sin'  => array ('sine', 3, 1, '_sin'),
        'cos'  => array ('cosine', 3, 1, '_cos'),
        'tan'  => array ('tangent', 3, 1, '_tan'),
        'asin' => array ('asine', 3, 1, '_asin'),
        'acos' => array ('acosine', 3, 1, '_acos'),
        'atan' => array ('atangent', 3, 1, '_atan'),
        'sqrt' => array ('square root', 3, 1, '_sqrt'),
        'exp'  => array ('power of e', 3, 1, '_exp'),
        'log'  => array ('logarithm', 3, 1, '_log'),
        'ln'   => array ('natural logarithm', 3, 1, '_ln'),
        'E'    => array ('power of 10', 3, 1, '_E'),
        'abs'  => array ('absolute value', 3, 1, '_abs'),
        '!'    => array ('factorial', 3, 1, '_factorial'),
        'pi'   => array ('value of pi', 4, 0, '_const_pi'),
        'e'    => array ('value of e', 4, 0, '_const_e'),
        'mod'  => array ('modulo', 3, 2, '_mod'),
        'div'  => array ('integer division', 3, 2, '_div')
    );

#-----------------------------------------------------------------------------------

    /**
     * Return the version information
     *
     * @return string
     * @access public
     */

    public function get_version()
    {
        return $this->_version;
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a PEAR error
     *
     * @return object PEAR error
     * @access private
     */

    private function _raiseError ($error) 
    {
        return PEAR::raiseError($error);
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a operator's array
     *
     * @return array Array with operator's name, priority, arguments, function's name and syntax
     * @access public
     */

    public function getOperators () 
    {
        $return = array();
        while(list($key, $val) = each($this->_operation)) 
        {

            if (array_key_exists (2, $val) && $val[2] == 2) 
            {
                $syntax = 'A ' . $key . ' B';
                $arguments = 2;
            } 
            elseif (array_key_exists (2, $val) && $val[2] == 1) 
            {
                $syntax = $key . ' A';
                $arguments = 1;
            } 
            else 
            {
                $syntax = $key;
                $arguments = 0;
            }

            if(array_key_exists (3, $val)) $function = $val[3]; else $function = '';

            $return[] = array (
                'operator' => $key,
                'name' => $val[0],
                'priority' => $val[1],
                'arguments' => $arguments,
                'function' => $function,
                'syntax' => $syntax
            );
        }

        return $return;
    }

#-----------------------------------------------------------------------------------

    /**
     * Add new operator
     *
     * @param string $operator New operator
     * @param string $function Function name
     * @param integer $priority New operator's priority
     * @param integer $no_of_arg Number of function's arguments
     * @param string $text New operator's description
     * @access public
     */

    public function addOperator ($operator, $function_name, $priority = 3, $no_of_arg = 0, $text = '') 
    {
        if(preg_match("/^([\W\w]+)\:\:([\W\w]+)$/",$function_name,$match)) 
        {
            $class = $match[1];
            $method = $match[2];
            $function = array (
               'type' => 'userMethod',
               'class' => $class,
               'method' => $method
            );
        } 
        else 
        {
            $function = array (
               'type' => 'userFunction',
               'function' => $function_name
            );
        }

        $this->_operation[$operator] = array ($text, $priority, $no_of_arg, $function);
    }

#-----------------------------------------------------------------------------------

    /**
     * Calculate the $input expression
     *
     * @param mixed $input Infix expression string or RPN expression string
     * @param string $angle Angle's unit - 'rad' or 'deg'
     * @param boolean $is_rpn True if $input is RPN expression or false if $input is infix expression
     * @return mixed Value of $input expression or a PEAR error
     * @access public
     */

    public function calculate($input = '', $angle = 'rad', $is_rpn = true) 
    {

        $this->_angle = (boolean) ($angle == 'rad');

        if($input == '') 
        {
            $this->_error = $this->_raiseError('Empty input expression');
            return $this->_error;
        }

        if(!$is_rpn) 
        {
            $this->_input = $input;
            $this->_input_array = $this->tokenize($this->_input);            
            
            if($this->_error <> null) return $this->_error;

            $this->_arrayToRpn();
            if($this->_error <> null) return $this->_error;
        } 
        else 
        {
            if (is_array($input)) 
            {
                $input = implode(' ', $input);
            }

            $this->_input = $input;
            $this->_input_array = explode(' ',$input);
            $this->_output = explode(' ',$input);
        }

        $this->_rpnToValue();
        if($this->_error <> null) return $this->_error;

        return $this->_value;
    }

#-----------------------------------------------------------------------------------

    /**
     * Calculate the $input expression (alias of calculate())
     *
     * @param mixed $input Infix expression string or RPN expression array
     * @param string $angle Angle's unit - 'rad' or 'deg'
     * @param boolean $is_rpn True if $input is RPN expression or false if $input is infix expression
     * @return mixed Value of $input expression or a PEAR error
     * @access public
     */

    public function evaluate($input = '', $angle = 'rad', $is_rpn = false) 
    {
        return $this-> calculate($input, $angle, $is_rpn);
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a input array
     *
     * @return array Input array
     * @access public
     */

    function getInputArray() 
    {
        return $this->_input_array;
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a RPN array
     *
     * @return array RPN array
     * @access public
     */

    function getRpnArray() 
    {
        return $this->_output;
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a counting time in second
     *
     * @return float Counting time in seconds
     * @access public
     */

    public function getTimer() 
    {
        return $this->_timer;
    }

#-----------------------------------------------------------------------------------

    /**
     * Check that $value is nan (conformity to php<4.2.0)
     *
     * @param float $value checking value
     * @return boolean true when $value is nan, or false
     * @access private
     */

    private function _isNan($value) 
    {
        if(function_exists('is_nan')) 
        {
            return is_nan($value);
        } 
        else 
        {
            if((substr($value,-3) == 'IND') || (substr($value,-3) == 'NAN')) return true;
            else return false;
        }
    }

#-----------------------------------------------------------------------------------

    /**
     * Check that $value is infinite (conformity to php<4.2.0)
     *
     * @param float $value checking value
     * @return boolean true when $value is infinite, or false
     * @access private
     */

    private function _isInfinite($value) 
    {
        if(function_exists('is_finite')) 
        {
            return !is_finite($value);
        } 
        else 
        {
            if(substr($value,-3) == 'INF') return true;
            else return false;
        }
    }

#-----------------------------------------------------------------------------------
    /**
     * Tokenize input expression into array
     *
     * @return array Input expression changed into array
     * @access public 
     */

    public function tokenize($text, $_tokens=NULL)
    {
        if (is_null($_tokens)) $_tokens = array_keys($this->_operation);
        $text = str_replace(' ', '', $text);

        $opbuf = NULL;   ## Sammelbuffer für erkanntes Token
        $textbuf  = NULL;   ## Sammelbuffer für Zwischentext
        $checkbuf = NULL;

        $_result['token']     = array();
        $_result['tokentype'] = array();
        $resultindex = 0;
        $checklen = 0;
        
        while (strlen($text) > 0)
        {
            ## längstens zum Textanfang passendes Token finden
            $checklen = 0;

            foreach($_tokens as $key => $token)
            {            
                if ((strlen($token) > $checklen) and strpos($text, $token) === 0)
                {
                    $opbuf = $token;
                    $checklen = strlen($token);
                }
            }                 

            ## Token in Ausgabearray überführen und Text abschneiden

            if(!is_null($opbuf))         ## Operator gefunden
            {
                ## wenn Zwischentext vorhanden ist, erst wegschreiben
                if (!is_null($textbuf))     
                {
                    $_result['token'][$resultindex] = $textbuf;
                    $_result['tokentype'][$resultindex] = 'tx';
                    $resultindex++;
                    $textbuf = NULL;
                }

                $_result['token'][$resultindex] = $opbuf;
                $_result['tokentype'][$resultindex] = 'op';
                $resultindex++;
                $text = substr($text, $checklen);
                $opbuf = NULL;  
            }
            else                            ## Zwischentext sammeln
            {
                $textbuf .= substr($text, 0,1);
                $text = substr($text, 1);
            }
        }

        ## Rattenschwanz, der kein Token mehr ist, auch übertragen

        if (!is_null($textbuf))
        {
            $_result['token'][$resultindex] = $textbuf;
            $_result['tokentype'][$resultindex] = 'tx';            
            $resultindex++;
            $textbuf = NULL;
        }

        #--------------------------------------
        # token recombiner
        #--------------------------------------
        ## Vorzeichen-Minus korrigieren und unerkannte Terme anmeckern
        ## Hexadezimale Darstellung erlauben

        $_return = array(); 
        $returnindex = 0;

        foreach($_result['token'] as $key => $token)
        {

            ## Hexadezimalzahlen erlauben

            if( $token == '0x')
            {
                $_return[$returnindex++] = $token; 
                continue;                
            }
            elseif( isset($_return[$returnindex-1]) 
                and  (strpos($_return[$returnindex-1],'0x') === 0)
                and preg_match('~^[0123456789abcdef]+$~i', $token)
              )  
            {             
                $_return[$returnindex-1] .= $token;
                continue;
            }
            elseif($_result['tokentype'][$key] == 'tx' and !is_numeric($token))
            {
                $this->_raiseError("Not recognized term '$token'");
            }

            if(
                  ($token == '-') and
                    ( 
                      ( 
                        isset($_result['token'][$key-1])
                        and  ($_result['tokentype'][$key-1] == 'op') 
                        and  ($_result['token'][$key-1] != ')' )
                      )
                      or ($key == 0) 
                    )
              )    
            {
               $_return[$returnindex++] = '(';
               $_return[$returnindex++] = '-1';
               $_return[$returnindex++] = ')';
               $_return[$returnindex++] = '*';
            }
            else
            {
               $_return[$returnindex++] = $token; 
            }
        }
        
        return $_return;
    }

#----------------------------------------------------------------------------------------

    /**
     * Check input array and return correct array or a PEAR Error
     *
     * @return object Null or a PEAR Error
     * @access private
     */

    private function _testInput() 
    {
        if (!count($this->_input_array)) 
        {
            $this->_input_array = null;
            $this->_error = $this->_raiseError('Undefined input array');
            return $this->_error;
        }

        $bracket = 0;
        for($i = 0; $i < count($this->_input_array); $i++) if ($this->_input_array[$i] == '(') $bracket++;
        for($i = 0; $i < count($this->_input_array); $i++) if ($this->_input_array[$i] == ')') $bracket--;

        if ($bracket <> 0) 
        {
                $this->_input_array = null;
                $this->_error = $this->_raiseError('Syntax error');
                return $this->_error;
        }

        for($i = 0; $i < count($this->_input_array); $i++) 
        {
            if ((!is_numeric($this->_input_array[$i])) && (!$this->_keyExists($this->_input_array[$i], $this->_operation, 0))) {
                $error_operator = $this->_input_array[$i];
                $this->_input_array = null;
                $this->_error = $this->_raiseError('Undefined operator \''. $error_operator.'\'');
                return $this->_error;
            }
        }

        $this->_error = null;
        return $this->_error;
    }

#-----------------------------------------------------------------------------------

    /**
     * Add value to the end of stack
     *
     * @param string $value Value to add into stack
     * @access private
     */

    private function _stackAdd($value) 
    {
        array_push($this->_stack, $value);
    }

#-----------------------------------------------------------------------------------

    /**
     * Delete and return value from the end of stack
     *
     * @return string Value deleted from stack
     * @access private
     */

    private function _stackDelete() 
    {
        return array_pop($this->_stack);
    }

#-----------------------------------------------------------------------------------

    /**
     * Return priority of value
     *
     * @param string $value Value to get priority
     * @return integer Priority
     * @access private
     */

    private function _priority($value) 
    {
        return $this->_operation[$value][1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Return priority of value from the end of stack
     *
     * @return integer Priority of operator from stack's top
     * @access private
     */

    private function _stackPriority() 
    {
        $value = $this->_stackDelete();
        $this->_stackAdd($value);
        return $this->_priority($value);
    }

#-----------------------------------------------------------------------------------

    /**
     * Return true whene the stack is empty
     *
     * @return boolean Stack is empty (true) or not (false)
     * @access private
     */

    private function _stackEmpty() 
    {
        if (count($this->_stack)) 
        {
            return false;
        }
        else return true;
    }

#-----------------------------------------------------------------------------------

    /**
     * Add value into output array
     *
     * @param string $value Value to add into output array
     * @access private
     */

    private function _outputAdd($value) 
    {
        if ($value<>'(') 
        {
            array_push($this->_output, $value);
        }
    }

#-----------------------------------------------------------------------------------

    /**
     * Change input array into RPN array
     *
     * @return array Array with RPN expression
     * @access private
     */

    private function _arrayToRpn() 
    {

        if ($this->_error <> null) 
        {
            $this->_output = array();
            return $this->_output;
        }

        for($i = 0; $i < count($this->_input_array); $i++) 
        {

            $temp = $this->_input_array[$i];

            if (is_numeric($temp)) 
            {
                $this->_outputAdd($temp);
            } 
            else 
            {
                if ($temp == ')') 
                {
                    while(!$this->_stackEmpty() && ($this->_stackPriority() >= 1)) 
                    {
                        $this->_outputAdd($this->_stackDelete());
                    }
                    
                    if (!$this->_stackEmpty()) 
                    {
                        $this->_stackDelete();
                    }

                } 
                elseif ($temp=='(') 
                {
                    $this->_stackAdd($temp);
                } 
                elseif (($this->_stackEmpty()) || (($this->_priority($temp) > $this->_stackPriority()))) 
                {
                   $this-> _stackAdd($temp);
                } 
                else 
                {
                    while(!$this->_stackEmpty() && ($this->_priority($temp) <= $this->_stackPriority())) 
                    {
                        $this->_outputAdd($this->_stackDelete());
                    }
                    
                    $this->_stackAdd($temp);
                }

            }

        }

        while(!$this->_stackEmpty()) 
        {
            $this->_outputAdd($this->_stackDelete());
        }

        return $this->_output;
    }

#-----------------------------------------------------------------------------------

    /**
     * Return position of the first operator in array
     *
     * @param array $array Temporary array
     * @return integer Position of the first operator
     * @access private
     */

    private function _nextOperator($array) 
    {
        $pos = 0;
        while(is_numeric($array[$pos])) 
        {
            $pos++;
            if ($pos >= count($array)) 
            {
                return -1;
            }
        }
        return $pos;

    }

#-----------------------------------------------------------------------------------

    /**
     * Delete from array operator [posision $pos] and its argument and insert new value
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of the last operator
     * @param integer $arg Number of last operator's arguments
     * @param float $result Last operation result
     * @return array New temporary array
     * @access private
     */

    private function _refresh($temp, $pos, $arg, $result) 
    {
        $temp1 = array_slice($temp, 0, $pos-$arg);
        $temp1[] = $result;
        $temp2 = array_slice($temp, $pos+1);
        return array_merge($temp1, $temp2);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _sum($temp, $pos) 
    {
        return $temp[$pos-2]+$temp[$pos-1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _difference($temp, $pos) 
    {
        return $temp[$pos-2]-$temp[$pos-1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _multiplication($temp, $pos) 
    {
        return $temp[$pos-2]*$temp[$pos-1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _division($temp, $pos) 
    {
        if ($temp[$pos-1]==0) 
        {
            $this->_error = $this->_raiseError('Division by 0');
            $this->_value = null;
            return $this->_value;
        }
        
        return $temp[$pos-2]/$temp[$pos-1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _root($temp, $pos) 
    {
        return pow($temp[$pos-1], (1/$temp[$pos-2]));
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _power($temp, $pos) 
    {
        return pow($temp[$pos-2], $temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _sin($temp, $pos) 
    {
        if ($this->_angle) 
        {
            $angle = $temp[$pos-1];
        } 
        else 
        {
            $angle = deg2rad($temp[$pos-1]);
        }
        return sin($angle);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _cos($temp, $pos) 
    {
        if ($this->_angle) 
        {
            $angle = $temp[$pos-1];
        } 
        else 
        {
            $angle = deg2rad($temp[$pos-1]);
        }
        
        return cos($angle);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _tan($temp, $pos) 
    {
        if ($this->_angle) 
        {
            $angle = $temp[$pos-1];
        } 
        else 
        {
            $angle = deg2rad($temp[$pos-1]);
        }
        
        return tan($angle);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _asin($temp, $pos) 
    {
        $angle = asin($temp[$pos-1]);
        if (!$this->_angle) 
        {
            $angle = rad2deg($angle);
        }
        
        return $angle;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _acos($temp, $pos) 
    {
        $angle = acos($temp[$pos-1]);
        if (!$this->_angle) 
        {
            $angle = rad2deg($angle);
        }
        return $angle;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _atan($temp, $pos) 
    {
        $angle = atan($temp[$pos-1]);
        if (!$this->_angle) 
        {
            $angle = rad2deg($angle);
        }
        
        return $angle;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _sqrt($temp, $pos) 
    {
        return sqrt($temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _exp($temp, $pos) 
    {
        return exp($temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _log($temp, $pos) 
    {
        return log10($temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _ln($temp, $pos) 
    {
        return log($temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _const_pi($temp, $pos) 
    {
        return M_PI;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _const_e($temp, $pos) 
    {
        return M_E;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */
  

    private function _E($temp, $pos) 
    {
        return pow(10, $temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _factorial($temp, $pos) 
    {
        $factorial = 1;
        for($i=1;$i<=$temp[$pos-1];$i++) 
        {
            $factorial *= $i;
        }
        return $factorial;
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _abs($temp, $pos) 
    {
        return abs($temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _mod($temp, $pos) 
    {
        return $temp[$pos-2]%$temp[$pos-1];
    }

#-----------------------------------------------------------------------------------

    /**
     * Math function
     *
     * @param array $temp Temporary array
     * @param integer $pos Position of operator
     * @return float Function's relult
     * @access private
     */

    private function _div($temp, $pos) 
    {
        return floor($temp[$pos-2]/$temp[$pos-1]);
    }

#-----------------------------------------------------------------------------------

    /**
     * Calculate RPN Expression and return value
     *
     * @return float Result of input expression
     * @access private
     */

    private function _rpnToValue() 
    {
        $time1 = $this->_getMicroTime();

        if ($this->_error <> null) 
        {
            $this->_value = null;
            return $this->_value;
        }

        $this->_value = 0;
        $temp = $this->_output;

        do 
        {
            $pos = $this->_nextOperator($temp);

            if ($pos == -1) 
            {
                $this->_error = $this->_raiseError('Syntax error');
                $this->_value = null;
                return $this->_value;
            }

            $operator = $this->_operation[$temp[$pos]];
            $arg = $operator[2];
            $function = $operator[3];

            if ( ($arg==2) && 
                 (
                   !isset($temp[$pos-1]) 
                   || !is_numeric($temp[$pos-1]) 
                   || !isset($temp[$pos-2]) 
                   || !is_numeric($temp[$pos-2])
                 )
               ) 
            {
                $this->_error = $this->_raiseError('Syntax error');
                $this->_value = null;
                return $this->_value;
            } 
            elseif (($arg==1) && (!isset($temp[$pos-1]) || !is_numeric($temp[$pos-1]))) 
            {
                $this->_error = $this->_raiseError('Syntax error');
                $this->_value = null;
                return $this->_value;
            }

            if(is_array($function)) 
            {

                if($arg==2) $arg_array = array($temp[$pos-2],$temp[$pos-1]);
                elseif($arg==1) $arg_array = array($temp[$pos-1]);
                else $arg_array = array();

                if($function['type'] == 'userFunction') 
                {
                    $this->_value = call_user_func_array($function['function'], $arg_array);
                } 
                else 
                {
                    $function_array = array(&$function['class'], $function['method']);
                    $this->_value = call_user_func_array($function_array, $arg_array);
                }
            } 
            else 
            {
                $this->_value = $this->$function($temp, $pos);
            }

            if ($this->_isNan($this->_value)) 
            {
                $this->_error = $this->_raiseError('NAN value');
                $this->_value = null;
                return $this->_value;
            }
            elseif ($this->_isInfinite($this->_value)) 
            {
                $this->_error = $this->_raiseError('Infinite value');
                $this->_value = null;
                return $this->_value;
            } 
            elseif (is_null($this->_value)) 
            {
                return $this->_value;
            }

            $temp = $this->_refresh($temp, $pos, $arg, $this->_value);
        
        } while(count($temp) > 1);

        $this->_value = $temp[0];
        $time2 = $this->_getMicroTime();
        $this->_timer = $time2 - $time1;

        return $this->_value;
    }

#-----------------------------------------------------------------------------------

    /**
     * Return a time in second
     *
     * @return float Current time in seconds
     * @access private
     */

    private function _getMicroTime() 
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

#-----------------------------------------------------------------------------------

}

?>