#include<iostream>
#include <cstdlib>
#include <string>
#include <cctype>
#include <fstream>
#include <sstream>
#include <cmath>

using namespace std;

enum { CTE, VAR, SUM, RES, CHS, MUL, DIV, EXPO, 
       SIN, COS };
//
//  Nodo:  Clase abstracta
//      Es la clase de la que se desprenden los nodos de los
//      arboles que representan expresiones.
//
 
class Nodo {
    public:
      virtual double Ev(void) const = 0;         // evaluacion
      virtual ostream& Pr(ostream&) const = 0;	 // impresion
      virtual Nodo* Dv(void) const = 0;		 // derivada
      virtual Nodo* Sm(void) const = 0;		 // simplificacion
      virtual int   Tipo(void) const = 0;	 // tipo de nodo
      virtual Nodo* Cp(void) const = 0;          // copia
      virtual Nodo* Izq(void) const;             // hijo izquierdo
      virtual Nodo* Der(void) const;             // hijo derecho
};

ostream& operator << (ostream& out, const Nodo& n);

//
// Cte:   Clase de las constantes.
//    guarda un valor double
//
class Cte : public Nodo {
    private:
      double val;
    public:
      Cte(double v=0);
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

//
//   Var:  Clase de la variable "x".
//       Estas clases deben hacer referencia hacia una cierta varibla x
//       y debe ser la misma para todos las instancias de la clase.
//
class Var : public Nodo {
    private:
      double* val;
    public:
      Var(double* v);
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

//
// Funcion:   Clase abstracta de la que se derivan las funciones
//
class Funcion : public Nodo {
    protected:
       Nodo* der; 
    public:
      Funcion(Nodo*);
      virtual ~Funcion(void);
      virtual double Ev(void) const = 0;
      virtual ostream& Pr(ostream&) const = 0;
      virtual Nodo* Dv(void) const = 0;
      virtual Nodo* Sm(void) const = 0;
      virtual int   Tipo(void) const = 0;
      virtual Nodo* Cp(void) const = 0;
      virtual Nodo* Der(void) const { return der; };  // hijo derecho
};

class Chs : public Funcion {
    public:
      Chs(Nodo* d) : Funcion(d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo*  Dv(void) const;
      virtual Nodo*  Sm(void) const;
      virtual int    Tipo(void) const;
      virtual Nodo*  Cp(void) const;
};

class Sin : public Funcion {
    public:
      Sin(Nodo* d) : Funcion(d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

class Cos : public Funcion {
    public:
      Cos(Nodo* d) : Funcion(d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};


// BinOp:  Clase abstracta que representa a los operadores binarios.
//    Define los apuntadores hacia los hijos de los operadores binarios.
//    Su constructor da valores iniciales a dichos apuntadores.
//
class BinOp : public Nodo {
    protected:
      Nodo* izq;
      Nodo* der;
    public:
      BinOp(Nodo*, Nodo*);
      virtual ~BinOp(void);
      virtual ostream& Pr(ostream&) const = 0;
      virtual Nodo* Dv(void) const = 0;
      virtual Nodo* Sm(void) const = 0;
      virtual int   Tipo(void) const = 0;
      virtual Nodo* Cp(void) const = 0;
      virtual Nodo* Izq(void) const { return izq; };  // hijo izquierdo
      virtual Nodo* Der(void) const { return der; };  // hijo derecho
};

//  Sum:   Clase del operador suma.

class Sum : public BinOp {
    public:
      Sum(Nodo* i, Nodo* d) : BinOp(i,d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

//  Resta
//
class Res : public BinOp {
    public:
      Res(Nodo* i, Nodo* d) : BinOp(i,d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

class Mul : public BinOp {
    public:
      Mul(Nodo* i, Nodo* d) : BinOp(i,d) {};
      virtual double Ev(void) const;
      virtual ostream& Pr(ostream&) const;
      virtual Nodo* Dv(void) const;
      virtual Nodo* Sm(void) const;
      virtual int   Tipo(void) const;
      virtual Nodo* Cp(void) const;
};

class Pila {
    private:
       Nodo* P[32];
       int   cnt;

    public:
       Pila(void) { cnt= 0; };
       ~Pila(void);
       Nodo* pop(void)      {  return P[--cnt]; };
       void push(Nodo* x)   {  P[cnt++]= x; };
       bool empty()         {  return cnt==0; };
};

Pila::~Pila(void)
{
    while (!empty())
       delete pop();
}

//
//   Analizador del lexico
//

string lex(istream& inp)
{
   int c;
   do {
     c= inp.get();
   } while (c!= EOF && isspace(c));

   string sim="";

   if (c==EOF)
     sim= "#EOF";
   else
   if (isalpha(c))
     {
      do {
         sim+= char(c);
         c= inp.get();
      }  while (isalpha(c));
      inp.putback(c);
     }
   else
   if (isdigit(c))
     {
      inp.putback(c);
      double x;
      inp >> x;
      ostringstream out;
      out << x;
      sim= out.str();
     }
   else
     switch (c) {
       case '+':
       case '-':
       case '*':
       case '/':
       case '^':
       case ';':  sim+= char(c);
                  break;
       default:   sim= "#MAL";
                  break;
      }
   return sim;
}


Nodo* Leer(istream& inp, double* x, Nodo* izq=NULL, Nodo* der=NULL )
{
   Pila p;
   string s;
   do {
      s= lex(inp);

      if (s=="#MAL")
        {
         cerr << "Simbolo desconocido: " << s <<endl;
         exit(1);
        }
      else
      if (isdigit(s[0]))
        {
         double c;
         istringstream str(s);
         str >> c;
         p.push( new Cte(c) );
        }
      else
      if (s=="x" || s=="X")
         p.push(new Var(x));
      else
      if (s=="der")
         p.push(der->Cp());
      else
      if (s=="izq")
         p.push(izq->Cp());
      else
      if (s=="+")
        {
         Nodo* b= p.pop();
         Nodo* a= p.pop();
         p.push( new Sum(a,b) );
        } 
      else
      if (s=="-")
        {
         Nodo* b= p.pop();
         Nodo* a= p.pop();
         p.push( new Res(a,b) );
        } 
      else
      if (s=="*")
        {
         Nodo* b= p.pop();
         Nodo* a= p.pop();
         p.push( new Mul(a,b) );
        } 
      else
      if (s=="sin" || s=="sen")
         p.push( new Sin( p.pop()) );
      else
      if (s=="cos")
         p.push( new Cos( p.pop() ) );
      else
      if (s=="chs")
         p.push( new Chs( p.pop() ) );
      else
      if (s=="dv")
        {
         Nodo* r= p.pop();
         p.push( r->Dv() );
         delete r;
        }
   }  while(s!=";" && s!="#EOF");  

   return p.pop();
}

//
// Metodos de los nodos.
//
Nodo* Nodo::Izq(void) const
{
    return NULL;
}

Nodo* Nodo::Der(void) const
{
    return NULL;
}

//
//  Para simplificar la forma de imprimir un nodo,
//  definimos el operador << para escribir sobre streams.
//
ostream& operator << (ostream& out, const Nodo& n)
{
   n.Pr(out); 
   return out;
}

//
//  Metodos de las constantes
//

Cte::Cte(double v)
{
   val= v;
}

ostream& Cte::Pr(ostream& out) const
{
   return out << val;
}

double Cte::Ev(void) const
{
   return val;
}

//
// la derivada de una constante es la constante 0
//
Nodo* Cte::Dv(void) const
{
   return new Cte(0);
}

// las constantes no pueden simplificarse
// solo se copian.
//
Nodo* Cte::Sm(void) const
{
   return new Cte(val);
}

// copia de una constante
//
Nodo* Cte::Cp(void) const
{
   return new Cte(val);
}

int Cte::Tipo(void) const
{
   return CTE;
}


//
//   Metodos de las variables
//

Var::Var(double* v)
{
   val= v;
}

//  El nombre de la variable es "x" 
//
ostream& Var::Pr(ostream& out) const
{
   return out << "x";
}

//  La evaluacion depende del valor de la variable x.
//
double Var::Ev(void) const
{
   return *val;
}

//  La derivada de la variable es la constante 1
// 
Nodo* Var::Dv(void) const
{
   return new Cte(1);
}

// Las variables no pueden simplificarse
//
Nodo* Var::Sm(void) const
{
   return new Var(val);
}

Nodo* Var::Cp(void) const
{
   return new Var(val);
}

int Var::Tipo(void) const
{
   return VAR;
}

//
//  Metodos de las funciones
//

Funcion::Funcion(Nodo* d)
{
   der= d;
}

Funcion::~Funcion(void)
{
   if (der != NULL) 
     delete der;
}

//
//  Metodos de Chs
//

double Chs::Ev(void) const
{
   return - (der->Ev());
}

ostream& Chs::Pr(ostream& out) const
{
    int td= der->Tipo();
    if (td==SUM || td==RES || td==CHS)
       out << "- (" << *der << ")";
    else
       out << "-" << *der;
    return out;
}


Nodo* Chs::Dv(void) const
{
   return new Chs( der->Dv() );
}

Nodo* Chs::Sm(void) const
{
    return Cp();
}

int Chs::Tipo(void) const
{
   return CHS;
}

Nodo* Chs::Cp(void) const
{
   return new Chs( der->Cp() );
}

//
//  Metodos de Sin
//

double Sin::Ev(void) const
{
   return sin(der->Ev());
}

ostream& Sin::Pr(ostream& out) const
{
   return out << "sin(" << *der << ")";
}


Nodo* Sin::Dv(void) const
{
   return new Mul(new Cos( der->Cp() ), der->Dv() );
}

Nodo* Sin::Sm(void) const
{
    return Cp();
}

int Sin::Tipo(void) const
{
   return SIN;
}

Nodo* Sin::Cp(void) const
{
   return new Sin( der->Cp() );
}


//
//  Metodos de Cos
//

double Cos::Ev(void) const
{
   return cos(der->Ev());
}

ostream& Cos::Pr(ostream& out) const
{
    out << "cos(" << *der << ")";
    return out;
}


Nodo* Cos::Dv(void) const
{
   istringstream dv("der sin der dv * chs");
   return Leer( dv, NULL, NULL, der );
}


Nodo* Cos::Sm(void) const
{
    return Cp();
}

int Cos::Tipo(void) const
{
   return COS;
}

Nodo* Cos::Cp(void) const
{
   return new Cos( der->Cp() );
}




//
//  Metodos de BinOp
//

BinOp::BinOp(Nodo* i, Nodo* d)
{
   izq= i;
   der= d;
}

//  El destructor elimina a ambos hijos
//
BinOp::~BinOp(void)
{
   if (der != NULL) 
     delete der;
   if (izq != NULL) 
     delete izq;
}


//
//  Metodos de Sum
//

//  El operador suma tiene la precedencia de asociacion mas baja.
//  sus operandos no necesitan escribirse entre parentesis.
//
ostream& Sum::Pr(ostream& out) const
{
   return out << *izq << "+" << *der;
}

//  El valor de la suma es la suma de los valores de los hijos.
//
double Sum::Ev(void) const
{
   return izq->Ev() + der->Ev();
}

// La derivada de la suma es la suma de las derivadas de los hijos.
//
Nodo* Sum::Dv(void) const
{
   istringstream dv("izq dv der dv +");
   return Leer( dv, NULL, izq, der );
}

// Simplificacion de la suma
//    se encuentra la simplificacion de los hijos
//    cte + cte ->  cte
//    0 + expr -> expr
//    expr + 0 -> expr
//    se construye la suma de las simplificaciones de los hijos.
// 
Nodo* Sum::Sm(void) const
{
   Nodo* iz= izq->Sm();
   Nodo* de= der->Sm();
   
   int ti= iz->Tipo();
   int td= de->Tipo();

   if (ti==CTE && td==CTE)
     {
      double c= iz->Ev() + de->Ev();
      delete iz;
      delete de;
      return new Cte(c);
     }

   if (ti==CTE && iz->Ev()==0)
     {
      delete iz;
      return de;
     }

   if (td==CTE && de->Ev()==0)
     {
      delete de;
      return iz;
     }

   if ( td==CHS )
     {
      Nodo* r= new Res(iz, de->Der()->Cp()); 
      delete de;
      return r;
     }

   return new Sum(iz,de);
}


Nodo* Sum::Cp(void) const
{
   return new Sum(izq->Cp(), der->Cp());
}

int Sum::Tipo(void) const
{
   return SUM;
}

//
//   Metodos de la Resta
//
ostream& Res::Pr(ostream& out) const
{
   out << *izq << "-";
   if (der->Tipo()==SUM || der->Tipo()==RES)
      out << "(" << *der << ")";
   else
      out << *der;
   return out;
}

double Res::Ev(void) const
{
   return izq->Ev() - der->Ev();
}

Nodo* Res::Dv(void) const
{
   return new Res(izq->Dv(), der->Dv());
}

Nodo* Res::Sm(void) const
{
   Nodo* iz= izq->Sm();
   Nodo* de= der->Sm();
   
   int ti= iz->Tipo();
   int td= de->Tipo();

   if (ti==CTE && td==CTE)
     {
      double c= iz->Ev() - de->Ev();
      delete iz;
      delete de;
      return new Cte(c);
     }

   if (td==CTE && de->Ev()==0)
     {
      delete de;
      return iz;
     }

   if ( td==CHS )
     {
      Nodo* r= new Sum(iz, de->Der()->Cp()); 
      delete de;
      return r;
     }

   return new Res(iz,de);
}

Nodo* Res::Cp(void) const
{
   return new Res(izq->Cp(), der->Cp());
}

int Res::Tipo(void) const
{
   return RES;
}

//
//  Metodos de la multiplicacion
//
ostream& Mul::Pr(ostream& out) const
{
   if (izq->Tipo()==SUM || izq->Tipo()==RES)
      out << "(" << *izq << ")";
   else
      out << *izq;

   out << "*";

   if (der->Tipo()==SUM || der->Tipo()==RES)
      out << "(" << *der << ")";
   else
      out << *der;

   return out;
}

double Mul::Ev(void) const
{
   return izq->Ev() * der->Ev();
}

Nodo* Mul::Dv(void) const
{
   return  new Sum(
             new Mul(izq->Dv(), der->Cp()),
             new Mul(izq->Cp(), der->Dv())
           );
}

Nodo* Mul::Sm(void) const
{
   Nodo* iz= izq->Sm();
   Nodo* de= der->Sm();
   
   int ti= iz->Tipo();
   int td= de->Tipo();

   if (ti==CTE && td==CTE)
     {
      double c= iz->Ev() * de->Ev();
      delete iz;
      delete de;
      return new Cte(c);
     }

   if (td==CTE && de->Ev()==0)
     {
      delete iz;
      return de;
     }

   if (ti==CTE && iz->Ev()==0)
     {
      delete de;
      return iz;
     }

   if (td==CTE && de->Ev()==1)
     {
      delete de;
      return iz;
     }

   if (ti==CTE && iz->Ev()==1)
     {
      delete iz;
      return de;
     }

   return new Mul(iz,de);
}

Nodo* Mul::Cp(void) const
{
   return new Mul(izq->Cp(), der->Cp());
}

int Mul::Tipo(void) const
{
   return MUL;
}



int main(int nargs, char* args[])
{
   double x;
   Pila p; 
   Nodo* s;
 
   s= Leer(cin, &x);

   cout << "f(x)= " << *s << endl;
   
   Nodo* d= s->Dv();

   cout <<  "f'(x)= " << *d << endl;

   Nodo* sd= d->Sm();

   cout <<  "f'(x)= " << *sd << endl;

   delete sd;
   delete d;
   delete s; 

   system("PAUSE");
   return EXIT_SUCCESS;
}
 
