#include <mathfunc.h>


#include <cmath>
using namespace std;

#include <rpn.h>


// Implementation
// ------------------------------------------------------------
//

/*

  Call to rpnf< fcos >; or other functional object.


*/


void rpnfsqrt::visit(deque<rpnbase*>& ds,rpninteger& n)
{
  rpnbase * x0 = ds.front();
  ds.pop_front();

  new rpnreal(ds,sqrt((long double)(n.num)));
  x0->dec();  
}




rpnrealconvert::rpnrealconvert(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* rpnrealconvert::copy() const
{
  return new rpnrealconvert();
}

void rpnrealconvert::eval( deque<rpnbase*>& ds )
{
  if(!ds.empty())
    ds[0]->accept(ds,*this);

  dec();
}

void rpnrealconvert::visit(deque<rpnbase*>& ds,rpnstring & s)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  long double n;
  stringstream ss(s.str.c_str());
  ss >> n;
  new rpnreal(ds,n);

  x0->dec();
}

void rpnrealconvert::visit(deque<rpnbase*>& ds,rpninteger & n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  long double num = (long double)(n.num);
  new rpnreal(ds,num);

  x0->dec();
}

void rpnrealconvert::visit(deque<rpnbase*>& ds,rpncomplex & n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();

  new rpnreal(ds,real(n.num));

  x0->dec();

}






rpnpow::rpnpow(deque<rpnbase*>& ds, bool const evaluate)
{
  if (evaluate)
    eval(ds);
  else
    ds.push_front(this);
}

rpnbase* rpnpow::copy() const
{
  return new rpnpow();
}

void rpnpow::eval( deque<rpnbase*>& ds )
{
  if (ds.size()>1)
    ds[0]->accept(ds,*this);

  dec();
}

void rpnpow::visit(deque<rpnbase*>& ds,rpnreal& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();

  if (x1->isreal())
  {
    long double y(n.num);
    long double & x(((rpnreal*)x1)->num);
    if (x==0.0)
      x=0.0;
    else
      x = pow(x,y);

//    ((rpnreal*)x1)->num = pow( ((rpnreal*)x1)->num, n.num );
    x0->dec();
    return;
  }
  else
  if (x1->iscomplex())
  {
    ((rpncomplex*)x1)->num = pow( ((rpncomplex*)x1)->num, n.num );
    x0->dec();
    return;
  }
  else
  if (x1->isinteger())
  {
    ds.pop_front();
    ds.push_front(x0);
    n.num = pow( ((rpninteger*)x1)->num, n.num );
    x1->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x0);
}

void rpnpow::visit(deque<rpnbase*>& ds, rpninteger& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();

  if (x1->isinteger())
  {
    long double const a(((rpninteger*)x1)->num);
    long double const b(n.num);

    ((rpninteger*)x1)->num = (long int) pow(a,b);
    x0->dec();
    return;
  }
  else
  if (x1->isreal())
  {
    long double const b(n.num);
    ((rpnreal*)x1)->num = pow( ((rpnreal*)x1)->num,b );
    x0->dec();
    return;
  }
  if (x1->iscomplex())
  {
    ((rpncomplex*)x1)->num = pow( ((rpncomplex*)x1)->num,n.num );
    x0->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x0);
}

void rpnpow::visit(deque<rpnbase*>& ds, rpncomplex& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();
  if (x1->iscomplex())
  {
    ((rpncomplex*)x1)->num = pow( ((rpncomplex*)x1)->num,n.num );
    x0->dec();
    return;
  }
  else
  if (x1->isreal())
  {
    ds.pop_front();
    ds.push_front(x0);
    complex< long double > n2( ((rpnreal*)x1)->num, 0.0);
    n.num = pow(n2,n.num);
    x1->dec();
    return;
  }
  else
  if (x1->isinteger())
  {
    ds.pop_front();
    ds.push_front(x0);
    complex< long double > n2( ((rpninteger*)x1)->num, 0.0);
    n.num = pow(n2,n.num);
    x1->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x0);
}


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

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

void rpnp_to_c::eval( deque<rpnbase*>& ds )
{
  if (ds.size()>1)
    ds[0]->accept(ds,*this);

  dec();
}

void rpnp_to_c::visit(deque<rpnbase*>& ds,rpnreal& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();
  ds.pop_front();

  if (x1->isreal())
  {
    long double const r = ((rpnreal*)x1)->num;
    long double const t = n.num;
    new rpncomplex(ds,r*cos(t),r*sin(t));
    x0->dec();
    x1->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x1);
  ds.push_front(x0);
}



void rpnr_to_c::eval( deque<rpnbase*>& ds )
{
  if (ds.size()>1)
    ds[0]->accept(ds,*this);

  dec();
}

void rpnr_to_c::visit(deque<rpnbase*>& ds,rpnreal& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();
  ds.pop_front();

  if (x1->isreal())
  {
    long double const x = ((rpnreal*)x1)->num;
    long double const y = n.num;
    new rpncomplex(ds,x,y);
    x0->dec();
    x1->dec();
    return;
  }
  else
  if (x1->isinteger())
  {
    long double const x = ((rpninteger*)x1)->num;
    long double const y = n.num;
    new rpncomplex(ds,x,y);
    x0->dec();
    x1->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x1);
  ds.push_front(x0);
}

void rpnr_to_c::visit(deque<rpnbase*>& ds,rpninteger& n)
{
  rpnbase* x0 = ds.front();
  ds.pop_front();
  rpnbase* x1 =ds.front();
  ds.pop_front();

  if (x1->isreal())
  {
    long double const x = ((rpnreal*)x1)->num;
    long double const y = n.num;
    new rpncomplex(ds,x,y);
    x0->dec();
    x1->dec();
    return;
  }
  else
  if (x1->isinteger())
  {
    long double const x = ((rpninteger*)x1)->num;
    long double const y = n.num;
    new rpncomplex(ds,x,y);
    x0->dec();
    x1->dec();
    return;
  }

  // Restore the stack.
  ds.push_front(x1);
  ds.push_front(x0);
}







long double mathconstants::pi = atan(1.0)*4.0;
long double mathconstants::r_to_d = 180.0/mathconstants::pi;
long double mathconstants::d_to_r = mathconstants::pi/180.0;



