/*
	Esta clase permite leer números complejos en la forma binómica:

	a+bi, donde a y b son número reales e i=(-1)^(1/2)

	El tipo para contruir la clase debe contar con la función estática getREE() y getRE(),
	que devuelven una referencia al reconocedor de expresiones regulares correspondiente al
	tipo y a la expresión regular, respectivamente.

	Ariel Medina //***Debemos permitir que lea también en forma polar

	* Los ángulos están dados en grados, si se quiere convertir a radianes usar rad(x)
*/

#ifndef __COMPLEX_AMV
	#define __COMPLEX_AMV

#include <iostream>
//#include "rationalBCD.h"
#include "REEvaluator.h"
#include "stringAMV.h"
#include "algorithm.h"
#include "binaryTree.h"
#include "deque.h"

namespace amv	{

template<class type>
class complex	{
type re;
type im;
bool polar;
type magnitude;
type angle;
	public:
		complex(const type &r=type(), const type &i=type(), bool p=false):
		re(r), im(i), polar(p), magnitude(r), angle(i)
			{
			}
		complex toPolar() const
			{
			if(polar)
				return *this;
			
			return complex(norm(*this).re,atan(im/re),true);
			}
		complex toBinomic() const
			{
			if(polar)
				return complex(magnitude*cos(angle),magnitude*sin(angle));

			return *this;
			}

		complex &operator+=(const complex &c)
			{
			if(polar)
				{
				*this=toBinomic();
				}
			if(c.polar)
				{
				complex temp(c.toBinomic());
				re+=temp.re;
				im+=temp.im;
				}
			else
				{
				re+=c.re;
				im+=c.im;
				}
			
			return *this;
			}
		complex &operator+=(const type &t)
			{
			if(polar)
				{
				*this=toBinomic();
				}
			re+=t;

			return *this;
			}

		complex &operator-=(const complex &c)
			{
			if(polar)
				{
				*this=toBinomic();
				}
			if(c.polar)
				{
				complex temp(c.toBinomic());
				re-=temp.re;
				im-=temp.im;
				}
			else
				{
				re-=c.re;
				im-=c.im;
				}
			
			return *this;
			}
		complex &operator-=(const type &t)
			{
			if(polar)
				{
				*this=toBinomic();
				}
			re-=t;

			return *this;
			}

		complex &operator*=(const complex &c)
			{
			if(polar && c.polar)
				{
				magnitude*=c.magnitude;
				angle+=c.angle;
				}
			else if(polar && !c.polar)
				{
				complex temp(c.toPolar());
				magnitude*=temp.magnitude;
				angle+=temp.angle;
				}
			else if(!polar && c.polar)
				{
				*this=toPolar();
				magnitude*=c.magnitude;
				angle+=c.angle;
				}
			else
				{
				type r(re*c.re-im*c.im),i(re*c.im+im*c.re);
				re=r;
				im=i;
				}

			return *this;
			}
		complex &operator*=(const type &t)
			{
			if(polar)
				magnitude*=t;
			else
				{
				re*=t;
				im*=t;
				}

			return *this;
			}

		friend complex inv(const complex &c)
			{
			return complex(c.re/((c.re^2)+(c.im^2)),-c.im/((c.re^2)+(c.im^2)));
			}

		complex &operator/=(const complex &c)
			{
			if(polar && c.polar)
				{
				magnitude/=c.magnitude;
				angle-=c.angle;

				return *this;
				}
			else if(polar && !c.polar)
				{
				complex temp(c.toPolar());
				magnitude/=temp.magnitude;
				angle-=temp.angle;

				return *this;
				}
			else if(!polar && c.polar)
				{
				*this=toPolar();
				magnitude/=c.magnitude;
				angle-=c.angle;

				return *this;
				}

			return (*this*=inv(c));
			}
		complex &operator/=(const type &t)
			{
			if(polar)
				magnitude/=t;
			else
				{
				re/=t;
				im/=t;
				}

			return *this;
			}

		//se supone c es real y entero por ahora
		complex &operator^=(const complex &c)
			{
			type n(c.re);
			if(c.polar)
				n=c.magnitude;
				
			if(polar)
				{
				magnitude^=n;
				angle*=n;
				}
			else
				{
				complex temp(toPolar());

				temp.magnitude^=n;
				temp.angle*=n;

				*this=temp;
				}

			return *this;
			}
		complex &operator^=(const type &t)
			{
			if(polar)
				{
				magnitude^=t;
				angle*=t;
				}
			else
				{
				complex temp(toPolar());

				temp.magnitude^=t;
				temp.angle*=t;

				*this=temp;
				}

			return *this;
			}

		//hace el cálculo sólo con las partes reales
		complex &operator%=(const complex &c)
			{
			re%=c.re;

			return *this;
			}
		complex &operator%=(const type &t)
			{
			re%=t;

			return *this;
			}

		complex operator+() const
			{
			return *this;
			}
		complex operator-() const
			{
			return complex(-re,-im);
			}

/*
		complex operator+(const complex &c) const
			{
			return *this;
			}*/

		friend complex<type> fact(const complex<type> &n);
		friend complex<type> exp(const complex<type> &n);
		friend complex<type> log(const complex<type> &n);
		friend complex<type> ln(const complex<type> &n);
		friend complex<type> sin(const complex<type> &n);
		friend complex<type> sinh(const complex<type> &n);
		friend complex<type> asin(const complex<type> &n);
		friend complex<type> cos(const complex<type> &n);
		friend complex<type> cosh(const complex<type> &n);
		friend complex<type> acos(const complex<type> &n);
		friend complex<type> tan(const complex<type> &n);
		friend complex<type> tanh(const complex<type> &n);
		friend complex<type> atan(const complex<type> &n);
		friend complex<type> ctg(const complex<type> &n);
		friend complex<type> actg(const complex<type> &n);
		friend complex<type> sec(const complex<type> &n);
		friend complex<type> asec(const complex<type> &n);
		friend complex<type> csc(const complex<type> &n);
		friend complex<type> acsc(const complex<type> &n);
		//friend complex<type> sqrt(const complex<type> &n)
		friend complex<type> pow(const complex<type> &n, const complex<type> &nth);
		friend complex<type> root(const complex<type> &nth, const complex<type> &n);
		friend deque<complex<type> > roots(const complex<type> &nth, const complex<type> &n);

		friend ostream &operator<<(ostream &os, const complex &c)
			{
			type zero, one(1), mone(-1);
			if(c.polar)
				{
				if(c.magnitude==one)
					os<<"cis "<<c.angle;
				else if(c.magnitude==mone)
					os<<"-cis "<<c.angle;
				else
					os<<c.magnitude<<" cis "<<c.angle;
					
				return os;
				}

			os<<c.re;
			if(!(c.im==zero))
				{
				if(c.im==one)
					os<<'+'<<'i';
				else if(c.im==mone)
					os<<'-'<<'i';
				else if(c.im<zero)
					os<<c.im<<'i';
				else
					os<<'+'<<c.im<<'i';
				}

			return os;
			}

		friend istream &operator>>(istream &is, complex &c)
			{
			static REEvaluator REEnospace="#*";
			string s;
			type r;//si no se logra obtener el dato, ¿El estado del flujo es malo?
			static string re=string("(")+type::getRE()+")<i";
			static char *p1=re.str();
			static REEvaluator ipart=p1, onlyi="(\\+|\\-)?<i";
			static REEvaluator &REEtype=type::getREE();
			static REEvaluator REEpolar=" *<cis <((\\+|\\-)?<[0-9]+<(.<[0-9]+)?)";//ver el caso de -cis 60
			delete p1;
			p1=0;

			REEnospace.skipNoSymbols(is);
			if(onlyi.getLexema(is,s))
				{
				if(s.front()=='+' || s.front()=='i')
					c=complex(0,type(1));
				else
					c=complex(0,type(-1));
				}
			else if(ipart.getLexema(is,s))
				{
				s.pop_back();
				c=complex(0,type(s));
				}
			else if(REEtype.getLexema(is,s))
				{
				r=type(s);
				if(onlyi.getLexema(is,s))
					{
					if(s.front()=='+' || s.front()=='i')
						c=complex(r,type(1));
					else
						c=complex(r,type(-1));
					}
				else if(REEpolar.getLexema(is,s))
					{
					string::iterator pos=find(s,'s');
					string s1(pos+2,s.end());
					c.polar=true;
					c.magnitude=r;
					c.angle=type(s1);
					}
				else
					{
					if(ipart.getLexema(is,s))
						{
						s.pop_back();
						c=complex(r,type(s));
						}
					else
						c=complex(r);
					}
				}
			else if(REEpolar.getLexema(is,s))
				{
				string::iterator pos=find(s,'s');
				string s1(pos+2,s.end());
				c.polar=true;
				c.magnitude=type(1);//?
				c.angle=type(s1);
				}

			return is;
			}

		static binaryTree<function<complex<type> > > getFunctionsTree(const complex<type>  &f)
			{
			binaryTree<function<complex<type> > > t;

			t.insert(function<complex<type> >("abs",abs)),
			t.insert(function<complex<type> >("fact",fact)),
			t.insert(function<complex<type> >("exp",exp)),
			t.insert(function<complex<type> >("log",log)),
			t.insert(function<complex<type> >("ln",ln)),
			t.insert(function<complex<type> >("sin",sin)),
			t.insert(function<complex<type> >("sinh",sinh)),
			t.insert(function<complex<type> >("asin",asin)),
			t.insert(function<complex<type> >("cos",cos)),
			t.insert(function<complex<type> >("cosh",cosh)),
			t.insert(function<complex<type> >("acos",acos)),
			t.insert(function<complex<type> >("tan",tan)),
			t.insert(function<complex<type> >("tanh",tanh)),
			t.insert(function<complex<type> >("atan",atan)),
			t.insert(function<complex<type> >("ctg",ctg)),
			t.insert(function<complex<type> >("actg",actg)),
			t.insert(function<complex<type> >("sec",sec)),
			t.insert(function<complex<type> >("asec",asec)),
			t.insert(function<complex<type> >("csc",csc)),
			t.insert(function<complex<type> >("acsc",acsc)),
			t.insert(function<complex<type> >("min",min,function<complex<type> >::BINARY)),
			t.insert(function<complex<type> >("max",max,function<complex<type> >::BINARY)),
			t.insert(function<complex<type> >("pow",pow,function<complex<type> >::BINARY)),
			t.insert(function<complex<type> >("root",root,function<complex<type> >::BINARY));

			return t;
			}

    // complex manipulations
    friend type real(const complex &c);   // the real part
    friend type imag(const complex &c);   // the imaginary part
    friend complex conj(const complex &c);  // the complex conjugate
    friend complex norm(const complex &c);   // the square of the magnitude
    //friend type arg(const complex &c);    // the angle in the plane

	friend complex operator+(const complex &c1, const complex &c2)
		{
		complex temp(c1);

		return temp+=c2;
		}
	friend complex operator+(const complex &c, const type &t)
		{
		complex temp(c);

		return temp+=t;
		}
	friend complex operator+(const type &t, const complex &c)
		{
		complex temp(c);

		return temp+=t;
		}
	friend complex operator-(const complex &c1, const complex &c2)
		{
		complex temp(c1);

		return temp-=c2;
		}
	friend complex operator-(const complex &c, const type &t)
		{
		complex temp(c);

		return temp-=t;
		}
	friend complex operator-(const type &t, const complex &c)
		{
		complex temp(c);

		return temp-=t;
		}
	friend complex operator*(const complex &c1, const complex &c2)
		{
		complex temp(c1);

		return temp*=c2;
		}
	friend complex operator*(const complex &c, const type &t)
		{
		return complex(c.re*t,c.im*t);
		}
	friend complex operator*(const type &t, const complex &c)
		{
		return complex(c.re*t,c.im*t);
		}
	friend complex operator/(const complex &c1, const complex &c2)
		{
		complex temp(c1);

		return temp/=c2;
		}
	friend complex operator/(const complex &c, const type &t)
		{
		return complex(c.re/t,c.im/t);
		}
	friend complex operator/(const type &t, const complex &c)
		{
		complex temp(c);

		return temp/=t;
		}
	friend bool operator==(const complex &c1, const complex &c2)
		{
		return (c1.re==c2.re && c1.im==c2.im);
		}
	friend bool operator!=(const complex &c1, const complex &c2)
		{
		return !(c1==c2);
		}

};

template <class type>
inline type real(const complex<type> &c)
{
return c.re;
}

template <class type>
inline type imag(const complex<type> &c)
{
return c.im;
}

template <class type>
inline complex<type> conj(const complex<type> &c)
{
return complex<type>(c.re,-c.im);
}

template <class type>
inline complex<type> norm(const complex<type> &c)
{
return root(type(2),(c.re*c.re+c.im*c.im));//equivale a abs(z)
}

template <class type>
inline complex<type> polar(const type &mag, const type &angle=type(0))
{
return complex(mag*cos(angle),mag*sin(angle));
}

template <class type>
complex<type> abs(const complex<type> &n)
{
return norm(n);
}

template <class type>
complex<type> fact(const complex<type> &n)
{
complex<type> temp(1), i(2);
for(; i<n || i==n; ++i)
	temp*=i;

return temp;
}

//e^x, utiliza la serie de maclaurin
template <class type>
complex<type> exp(const complex<type> &n)
{
complex<type> temp(1), i(2), limit(10);
temp+=n;

for(; i<limit; ++i)
	{
	temp+=(n^i)/fact(i);
	}

return temp;
}

template <class type>
complex<type> log(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> ln(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> sin(const complex<type> &n)
{
complex<type> newAngle(reduce(n)), temp(newAngle), i(3), limit(10*2);

bool s=false;
for(; i<limit; i+=2)
	{
	if(s)
		temp+=(newAngle^i)/fact(i);
	else
		temp+=-((newAngle^i)/fact(i));
	s=!s;
	}

return temp;
}

template <class type>
complex<type> sinh(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> asin(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> cos(const complex<type> &n)
{
complex<type> newAngle(reduce(n)), temp(1), i(2), limit(10*2);

bool s=false;
for(; i<limit; i+=2)
	{
	if(s)
		temp+=(newAngle^i)/fact(i);
	else
		temp+=-((newAngle^i)/fact(i));
	s=!s;
	}

return temp;
}

template <class type>
complex<type> cosh(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> acos(const complex<type> &n)
{
complex<type> temp;

return temp;
}

template <class type>
complex<type> tan(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> tanh(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> atan(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> ctg(const complex<type> &n)
{
complex<type> temp(cos(n)/sin(n));

return temp;
}

template <class type>
complex<type> actg(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> sec(const complex<type> &n)
{
complex<type> temp(complex<type>(1)/cos(n));

return temp;
}

template <class type>
complex<type> asec(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> csc(const complex<type> &n)
{
complex<type> temp(complex<type>(1)/sin(n));

return temp;
}

template <class type>
complex<type> acsc(const complex<type> &n)
{
complex<type> temp(sin(n)/cos(n));

return temp;
}

template <class type>
complex<type> pow(const complex<type> &n, const complex<type> &nth)
{
return (n^nth);
}

template <class type>
complex<type> root(const complex<type> &nth, const complex<type> &n)
{
return (roots(nth,n))[0];
}

//los ángulos son en grados
//se supone nth es un complejo con la parte real entera, que es la única que se toma en cuenta
template <class type>
deque<complex<type> > roots(const complex<type> &nth, const complex<type> &n)
{
deque<complex<type> > droots;
type i(0);
if(n.polar)
	{
	for(; i<nth.re; ++i)
		droots.push_back(complex<type>(rootGomezMorin(n.magnitude,nth.re),(n.angle+type(360)*i)/nth.re,true));
	}
else
	{
	type magnitude=norm(n).re;
	type angle=atan(n.im/n.re);
	for(; i<nth.re; ++i)
		droots.push_back(complex<type>(rootGomezMorin(magnitude,nth.re),(angle+type(360)*i)/nth.re,true));
	}

return droots;
}

}

#endif