#ifndef MATHFUNC_H
#define MATHFUNC_H


#include <cmath>
#include <complex>
using namespace std;

#include <rpn.h>






class rpnfcos: public rpnfunction
{
public:

  string const name() const
    { return string("cos"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = cos(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = cos(n.num); }

  rpnfcos() {}
  rpnfcos(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfcos() {} 
  rpnbase* copy() const 
    { return new rpnfcos(); } 

};


class rpnfsin: public rpnfunction
{
public:

  string const name() const
    { return string("sin"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = sin(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = sin(n.num); }

  rpnfsin() {}
  rpnfsin(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfsin() {} 
  rpnbase* copy() const 
    { return new rpnfsin(); } 

};



class rpnftan: public rpnfunction
{
public:

  string const name() const
    { return string("tan"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = tan(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = tan(n.num); }

  rpnftan() {}
  rpnftan(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnftan() {} 
  rpnbase* copy() const 
    { return new rpnftan(); } 

};




class rpnflog: public rpnfunction
{
public:

  string const name() const
    { return string("log"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = log(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = log(n.num); }

  rpnflog() {}
  rpnflog(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnflog() {} 
  rpnbase* copy() const 
    { return new rpnflog(); } 

};


class rpnflog10: public rpnfunction
{
public:

  string const name() const
    { return string("log10"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = log10(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = log10(n.num); }

  rpnflog10() {}
  rpnflog10(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnflog10() {} 
  rpnbase* copy() const 
    { return new rpnflog10(); } 

};

class rpnfexp: public rpnfunction
{
public:

  string const name() const
    { return string("exp"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = exp(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = exp(n.num); }

  rpnfexp() {}
  rpnfexp(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfexp() {} 
  rpnbase* copy() const 
    { return new rpnfexp(); } 

};

class rpnfsqrt: public rpnfunction
{
public:

  string const name() const
    { return string("sqrt"); }

  void visit(deque<rpnbase*>& ds,rpninteger& n); 
  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = sqrt(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = sqrt(n.num); }

  rpnfsqrt() {}
  rpnfsqrt(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfsqrt() {} 
  rpnbase* copy() const 
    { return new rpnfsqrt(); } 

};

class rpnfsinh: public rpnfunction
{
public:

  string const name() const
    { return string("sinh"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = sinh(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = sinh(n.num); }

  rpnfsinh() {}
  rpnfsinh(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfsinh() {} 
  rpnbase* copy() const 
    { return new rpnfsinh(); } 

};

class rpnfcosh: public rpnfunction
{
public:

  string const name() const
    { return string("cosh"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = cosh(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = cosh(n.num); }

  rpnfcosh() {}
  rpnfcosh(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfcosh() {} 
  rpnbase* copy() const 
    { return new rpnfcosh(); } 

};

class rpnftanh: public rpnfunction
{
public:

  string const name() const
    { return string("tanh"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = tanh(n.num); }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
    { n.num = tanh(n.num); }

  rpnftanh() {}
  rpnftanh(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnftanh() {} 
  rpnbase* copy() const 
    { return new rpnftanh(); } 

};


class rpnffloor: public rpnfunction
{
public:

  string const name() const
    { return string("floor"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = floor(n.num); }

  rpnffloor() {}
  rpnffloor(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnffloor() {} 
  rpnbase* copy() const 
    { return new rpnffloor(); } 
};

class rpnfceil: public rpnfunction
{
public:

  string const name() const
    { return string("ceil"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = ceil(n.num); }

  rpnfceil() {}
  rpnfceil(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfceil() {} 
  rpnbase* copy() const 
    { return new rpnfceil(); } 
};


class rpnfacos: public rpnfunction
{
public:

  string const name() const
    { return string("acos"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = acos(n.num); }

  rpnfacos() {}
  rpnfacos(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfacos() {} 
  rpnbase* copy() const 
    { return new rpnfacos(); } 
};

class rpnfasin: public rpnfunction
{
public:

  string const name() const
    { return string("asin"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = asin(n.num); }

  rpnfasin() {}
  rpnfasin(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfasin() {} 
  rpnbase* copy() const 
    { return new rpnfasin(); } 
};

class rpnfatan: public rpnfunction
{
public:

  string const name() const
    { return string("atan"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { n.num = atan(n.num); }

  rpnfatan() {}
  rpnfatan(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfatan() {} 
  rpnbase* copy() const 
    { return new rpnfatan(); } 

};

class rpnfabs: public rpnfunction
{
public:

  string const name() const
    { return string("abs"); }

  void visit(deque<rpnbase*>& ds,rpninteger& n) 
    { if(n.num<0) n.num *= -1; }
  void visit(deque<rpnbase*>& ds,rpnreal& n) 
    { if(n.num<0) n.num *= -1; }
  void visit(deque<rpnbase*>& ds,rpncomplex& n)
  {
    rpnbase* x0 = ds.front();
    ds.pop_front();
    new rpnreal(ds,abs(n.num)); 
    x0->dec();
  }

  rpnfabs() {}
  rpnfabs(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfabs() {} 
  rpnbase* copy() const 
    { return new rpnfabs(); } 
};

class rpnfarg: public rpnfunction
{
public:

  string const name() const
    { return string("arg"); }

  void visit(deque<rpnbase*>& ds,rpncomplex& n) 
  {
    rpnbase* x0 = ds.front();
    ds.pop_front();
    new rpnreal(ds,arg(n.num)); 
    x0->dec();
  }

  rpnfarg() {}
  rpnfarg(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfarg() {} 
  rpnbase* copy() const 
    { return new rpnfarg(); } 
};

class rpnfnorm: public rpnfunction
{
public:

  string const name() const
    { return string("norm"); }

  void visit(deque<rpnbase*>& ds,rpncomplex& n) 
  {
    rpnbase* x0 = ds.front();
    ds.pop_front();
    new rpnreal(ds,norm(n.num)); 
    x0->dec();
  }

  rpnfnorm() {}
  rpnfnorm(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfnorm() {} 
  rpnbase* copy() const 
    { return new rpnfnorm(); } 
};

class rpnfimag: public rpnfunction
{
public:

  string const name() const
    { return string("imag"); }

  void visit(deque<rpnbase*>& ds,rpncomplex& n) 
  {
    rpnbase* x0 = ds.front();
    ds.pop_front();
    new rpnreal(ds,imag(n.num)); 
    x0->dec();
  }

  rpnfimag() {}
  rpnfimag(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfimag() {} 
  rpnbase* copy() const 
    { return new rpnfimag(); } 
};

class rpnrealconvert : public rpnfunction
{
public:

  rpnrealconvert() {}
  rpnrealconvert(deque<rpnbase*>& ds, bool const evaluate=true);
  ~rpnrealconvert() {}
  rpnbase* copy() const;

  void visit(deque<rpnbase*>& ds,rpnstring& s);
  void visit(deque<rpnbase*>& ds,rpninteger& n);
  void visit(deque<rpnbase*>& ds,rpncomplex& n);

  string const name() const { return string("real"); }

  void eval( deque<rpnbase*>& ds );
};

class rpnfconj: public rpnfunction
{
public:

  string const name() const
    { return string("conj"); }

  void visit(deque<rpnbase*>& ds,rpncomplex& n) 
    { n.num = conj(n.num); }

  rpnfconj() {}
  rpnfconj(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnfconj() {} 
  rpnbase* copy() const 
    { return new rpnfconj(); } 
};


class rpnpow: public rpnfunction
{
public:

  rpnpow() {}
  rpnpow(deque<rpnbase*>& ds, bool const evaluate=true);
  ~rpnpow() {}
  rpnbase* copy() const;

  void visit(deque<rpnbase*>& ds,rpninteger& n);
  void visit(deque<rpnbase*>& ds,rpnreal& n);
  void visit(deque<rpnbase*>& ds,rpncomplex& n);

  string const name() const { return string("^"); }

  void eval( deque<rpnbase*>& ds );
};




class rpnr_to_d: public rpnfunction
{
public:

  string const name() const
    { return string("r->d"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n); 

  rpnr_to_d() {}
  rpnr_to_d(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnr_to_d() {} 
  rpnbase* copy() const 
    { return new rpnr_to_d(); } 

};


class rpnd_to_r: public rpnfunction
{
public:

  string const name() const
    { return string("d->r"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n); 

  rpnd_to_r() {}
  rpnd_to_r(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnd_to_r() {} 
  rpnbase* copy() const 
    { return new rpnd_to_r(); } 

};



class rpnp_to_c: public rpnfunction
{
public:

  string const name() const
    { return string("p->c"); }

  void visit(deque<rpnbase*>& ds,rpnreal& n); 

  rpnp_to_c() {}
  rpnp_to_c(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnp_to_c() {} 
  rpnbase* copy() const 
    { return new rpnp_to_c(); } 

  void eval( deque<rpnbase*>& ds );
};



class rpnr_to_c: public rpnfunction
{
public:

  string const name() const
    { return string("complex"); }

  void visit(deque<rpnbase*>& ds,rpninteger& n); 
  void visit(deque<rpnbase*>& ds,rpnreal& n); 

  rpnr_to_c() {}
  rpnr_to_c(deque<rpnbase*>& ds, bool const evaluate=true)
    {  
      if (evaluate)
        eval(ds);
      else
        ds.push_front(this);
    }
  ~rpnr_to_c() {} 
  rpnbase* copy() const 
    { return new rpnr_to_c(); } 

  void eval( deque<rpnbase*>& ds );
};




class mathconstants
{
public:

  static long double pi;
  static long double r_to_d;
  static long double d_to_r;
};


#endif



