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

using namespace std;

#include "nodo.h"

// Constantes ----------------------------------

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


int Cte::T(void)
{
   return CTE;
}


double Cte::E(void)
{
   return val;
}


string Cte::P(bool par)
{
   ostringstream oss;
   if (par)
      oss << '(' << val << ')';
   else
      oss << val;
   return oss.str();
}


Nodo* Cte::S(void)
{
   return new Cte(val);
}


Nodo* Cte::C(void)
{
   return new Cte(val);
}


Nodo* Cte::D(void)
{
   return new Cte(0);
}


// Variables ------------------------------

Nodo* VarX::D(void)
{
   return new Cte(1);
}


double VarX::E(void)
{
   return *var;
}


string VarX::P(bool par)
{
    ostringstream oss;

    if (par) 
       oss << "(x)";
    else
       oss << "x";
    return oss.str();
}


int VarX::T(void)
{
   return VARX;
}


Nodo* VarX::S(void)
{
   return new VarX(var);
}


Nodo* VarX::C(void)
{
   return new VarX(var);
}


VarX::VarX(double *v)
{
   var= v;
}

// Op Binarios -----------------------------

Bin::~Bin(void)
{
   delete der;
   delete izq;
}

// Suma ----------------------------------

string Sum::P(bool par)
{
   ostringstream oss;
   if (par)
      oss << '(';
   oss << izq->P(false) << '+' << der->P(false);
   if (par)
      oss << ')';
   return oss.str();
}

double Sum::E(void)
{
   return izq->E() + der->E();
}


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


int Sum::T(void)
{
   return SUM;
}


Nodo* Sum::S(void)
{
   Nodo* i= izq->S();
   Nodo* d= der->S();

   if (i->T()==CTE && i->E()==0.0)
      return d->C();

   if (d->T()==CTE && d->E()==0.0)
      return i->C();

   if (d->T()==CTE && i->T()==CTE)
      return new Cte(i->E() + d->E());

   return new Sum(i,d);
}


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

// Resta ----------------------------------

string Sub::P(bool par)
{
   ostringstream oss;
   if (par)
      oss << '(';
   oss << izq->P(false) 
       << '-' 
       << der->P(der->T()==SUM);
   if (par)
      oss << ')';
   return oss.str();
}


double Sub::E(void)
{
   return izq->E() + der->E();
}

Nodo* Sub::D(void)
{
   return new Sub( izq->D(), der->D() );
}


int Sub::T(void)
{
   return SUB;
}


Nodo* Sub::S(void)
{
   Nodo* i= izq->S();
   Nodo* d= der->S();

   if (d->T()==CTE && d->E()==0.0)
      return i->C();

   if (d->T()==CTE && i->T()==CTE)
      return new Cte(i->E() - d->E());

   return new Sub(i,d);
}


Nodo* Sub::C(void)
{
   return new Sub(izq->C(), der->C());
}


// -------------------------------------------

string Mul::P(bool par)
{
   ostringstream oss;
   int ti= izq->T();
   int td= der->T();

   if (par)
      oss << '(' ;
   oss << izq->P(ti==SUM || ti==SUB) 
       << '*' 
       << der->P(td==SUM || td==SUB);
   if (par)
      oss << ')';

   return oss.str();
}

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

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


int Mul::T(void)
{
   return MUL;
}

Nodo* Mul::S(void)
{
   Nodo* i= izq->S();
   Nodo* d= der->S();

   if (i->T()==CTE && i->E()==1.0)
      return d->C();

   if (d->T()==CTE && d->E()==1.0)
      return i->C();

   if (i->T()==CTE && i->E()==0.0)
      return new Cte(0);

   if (d->T()==CTE && d->E()==0.0)
      return new Cte(0);

   if (d->T()==CTE && i->T()==CTE)
      return new Cte(i->E() * d->E());

   return new Mul(i,d);
}


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

// Division ----------------------------------


string Div::P(bool par)
{
   ostringstream oss;
   int ti= izq->T();
   int td= der->T();

   if (par)
      oss << '(' ;
   oss << izq->P(ti==SUM || ti==SUB ) 
       << '/' 
       << der->P(td==SUM || td==SUB || td==MUL);
   if (par)
      oss << ')';

   return oss.str();
}

double Div::E(void)
{
   return izq->E() / der->E();
}

Nodo* Div::D(void)
{
   return new Div (
             new Sub(
                  new Mul( izq->D(), der->C() ),
                  new Mul( izq->C(), der->D() )
             ),
           new Pow(
              der->C(),
              new Cte(2)
             ) 
          );
}

int Div::T(void)
{
   return DIV;
}


Nodo* Div::S(void)
{
   Nodo* i= izq->S();
   Nodo* d= der->S();
   if (d->T()==CTE && d->E()==1.0)
      return izq->C();

   if (i->T()==CTE && i->E()==0.0)
      return new Cte(0);

   return new Div(i,d);
}

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


// Potencia ----------------------------------


string Pow::P(bool par)
{
   ostringstream oss;
   int ti= izq->T();
   int td= der->T();

   if (par)
      oss << '(' ;
   oss << izq->P(ti==SUM || ti==SUB || ti==DIV || ti==MUL) 
       << "**" 
       << der->P(td==SUM || td==SUB || td==DIV || td==MUL || td==POW);
   if (par)
      oss << ')';

   return oss.str();
}

double Pow::E(void)
{
   return pow(izq->E(), der->E());
}

Nodo* Pow::D(void)
{
   return new Sum (
             new Mul(
                  der->C(),
                  new Mul(
                        new Pow(
                           izq->C(),
                           new Sub(
                                 der->C(),
                                 new Cte(1)
                               )
                          ),
                        izq->D()  
                      )
                 ),
              new Mul(
                     new Mul(
                       this->C(),
                       der->D()
                     ),
                     new Log(
                           izq->C()
                         )
                  )  
             ); 
}

int Pow::T(void)
{
   return POW;
}


Nodo* Pow::S(void)
{
   Nodo* i= izq->S();
   Nodo* d= der->S();

   if (d->T()==CTE && d->E()==1.0)
      return i->C();

   if (d->T()==CTE && d->E()==0.0)
      return new Cte(1);

   if (i->T()==CTE && i->E()==0.0)
      return new Cte(0);

   if (i->T()==CTE && i->E()==1.0)
      return new Cte(1);

   return new Pow(i,d);
}

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


// Log --------------------------------------

int Log::T(void)
{
   return LOG;
}


double Log::E(void)
{
   return log(arg->E());
}


string Log::P(bool par)
{
   ostringstream oss;
   if (par)
      oss << '(';
   oss << "log" << arg->P(true);
   if (par)
      oss << ')';
   return oss.str();
}


Nodo* Log::S(void)
{
   return C();
}


Nodo* Log::C(void)
{
   return new Log(arg->C());
}


Nodo* Log::D(void)
{
   return new Div(
           arg->D(),
           arg->C()
          );
}



