#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Version 1.1 12/05/2010

# ***************************************************************************
# *   Copyright (C) 2010, Paul Lutus                                        *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU General Public License as published by  *
# *   the Free Software Foundation; either version 2 of the License, or     *
# *   (at your option) any later version.                                   *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU General Public License for more details.                          *
# *                                                                         *
# *   You should have received a copy of the GNU General Public License     *
# *   along with this program; if not, write to the                         *
# *   Free Software Foundation, Inc.,                                       *
# *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
# ***************************************************************************

import sys, re, readline, types
from math import *
from operator import *

class RPNCalc:

  def fmtprint(self,a,b): print("%6s | %s" % (a,b))

  def helptxt(self):
    self.fmtprint("Enter","For")
    print("-" * 20)
    for tup in (self.com2args,self.com1arg,self.coms):
      for item in tup:
        self.fmtprint(item[0],item[2])

  def found(self,s,tup):
    for item in tup:
      if(s == item[0]):
        self.node = item
        return True
    return False

  def __init__(self):
    self.stack = []

    self.com2args = (
      ("+"  , add,"y + x"),
      ("-"  , sub,"y - x"),
      ("*"  , mul,"y * x"),
      ("/"  , div,"y / x"),
      ("%"  , fmod,"y % x"),
      ("^"  , pow,"y ^ x"),
      ("hyp", hypot,"hypot(x,y)"),
    )

    self.com1arg = (
      ("sin"  , sin,       "sin(x)"),
      ("cos"  , cos,       "cos(x)"),
      ("tan"  , tan,       "tan(x)"),
      ("asin" , asin,     "asin(x)"),
      ("acos" , acos,     "acos(x)"),
      ("atan" , atan,     "atan(x)"),
      ("sinh" , sinh,     "sinh(x)"),
      ("cosh" , cosh,     "cosh(x)"),
      ("tanh" , tanh,     "tanh(x)"),
      ("asinh", asinh,   "asinh(x)"),
      ("acosh", acosh,   "acosh(x)"),
      ("atanh", atanh,   "atanh(x)"),
      ("sqrt" , sqrt,     "sqrt(x)"),
      ("log"  , log,       "log(x)"),
      ("exp"  , exp,       "exp(x)"),
      ("ceil" , ceil,     "ceil(x)"),
      ("floor", floor,   "floor(x)"),
      ("erf"  , erf,       "erf(x)"),
      ("erfc" , erfc,     "erfc(x)"),
      ("!"    , factorial,     "x!"),
      ("abs"  , fabs,         "|x|"),
      ("deg"  , degrees,"degree(x)"),
      ("rad"  , radians,"radian(x)"),
    )

    self.coms = (
      ("pi", lambda: self.stack.insert(0,pi),"Pi"),
      ("e" , lambda: self.stack.insert(0,e),"e (base of natural logarithms)"),
      ("d" , lambda: self.stack.pop(0),"Drop x"),
      ("x" , lambda: self.stack.insert(0,self.stack[0]),"Enter x"),
      (""  , lambda: self.stack.insert(0,self.stack[0]),"Enter x (press Enter)"),
      ("s" , lambda: self.stack.insert(0,self.stack.pop(1)),"Swap x <-> y"),
      ("h" , self.helptxt,"Help"),
      ("q" , lambda: 0,"Quit"),
    )

  def process(self):
    spregex = re.compile("\s+")
    stacklbl = "tzyx"
    line = ""

    while (line != "q"):
      while(len(self.stack) < 4): self.stack.append(0.0)
      for i, n in enumerate(self.stack[3::-1]):
        if(type(n) == int):
          try:
            n = float(n)
          except:
            n = float('inf')
        s = "f" # default display format
        an  = abs(n)
        if(an > 1e10 or an < 1e-10 and an != 0.0):
          s = "e" # switch to scientific notation
        fs  = ("%.16" + s) % n
        # line up decimal points
        p = 10 + len(fs) - fs.find('.')
        print("%s: %*s" % (stacklbl[i],p,fs))
      line = input("Entry (h = help, q = quit): ")
      for tok in spregex.split(line):
        try: # parsing a number
          self.stack.insert(0,float(tok))
        except: # it's not a number
          try: # look for command
            if (self.found(tok, self.com2args)):
              self.stack.insert(0,self.node[1](self.stack.pop(1),self.stack.pop(0)))
            elif (self.found(tok, self.com1arg)):
              self.stack.insert(0,self.node[1](self.stack.pop(0)))
            elif (self.found(tok, self.coms)):
              self.node[1]()
            else:
              print("Error: token \"%s\" not found!" % tok)
          except:
            print("Error:", sys.exc_info()[0])

RPNCalc().process()
