
#include <amsmathutil25/amsmathutil25.hpp>

namespace ams
{
namespace cmp
{

complex::complex()
{
  //initcode
  r = 0.0;
  i = 0.0;
  return;
}

complex::complex(double _r, double _i)
{
  r = _r; i = _i;
  return;
}

complex::complex(double _r)
{
  r = _r; i = 0.0;
  return;
}

complex::complex(const complex &other)
{
  r = other.r;
  i = other.i;
  return;
}

complex& complex::operator=(const complex& other)
{
  r = other.r;
  i = other.i;
  return *this;
}

complex& complex::operator=(const double other)
{
  r = other;
  i = 0.0;
  return *this;
}

double& complex::operator[](int& ind)
{
  if(ind==0)
  {
    return this->r;
  }
  else if(ind==1)
  {
    return this->i;
  }
  else
  {
    printf("complex::operator[] index %d out of bounds\n",ind);
    exit(0);
  }
}

const double& complex::operator[](const int& ind) const
{
  if(ind==0)
  {
    return this->r;
  }
  else if(ind==1)
  {
    return this->i;
  }
  else
  {
    printf("complex::operator[] index %d out of bounds\n",ind);
    exit(0);
  }
}

complex complex::operator+(const complex &z)
{
  complex cp;
  cp.r = r+z.r;
  cp.i = i+z.i;
  return cp;
}

 complex  complex::operator-(const complex &z)
{
  complex cp;
  cp.r = r-z.r;
  cp.i = i-z.i;
  return cp;
}

 complex complex::operator*(const complex &z)
{
  complex cp;
	cp.r = r * z.r - i * z.i;
	cp.i = r * z.i + i * z.r;
	return cp;
}

 complex complex::operator/(const complex &z)
{
	complex cp;
	cp.r = (r*z.r+i*z.i)/(z.r*z.r+z.i*z.i);
	cp.i = (i*z.r-r*z.i)/(z.r*z.r+z.i*z.i);
	return cp;
}

 complex complex::operator+(const double &z)
{
	complex cp;
	cp.r = r + z;
	cp.i = i + 0.0;
	return cp;
}

 complex complex::operator-(const double &z)
{
	complex cp;
	cp.r = r - z;
	cp.i = i - 0.0;
	return cp;
}

 complex complex::operator*(const double &z)
{
	complex cp;
	cp.r = r * z;
	cp.i = i * z;
	return cp;
}

 complex complex::operator/(const double &z)
{
	complex cp;
	cp.r = r/z;
	cp.i = i/z;
	return cp;
}

complex operator-(const complex &z)
{
	complex ret;
	ret.r = -z.r;
	ret.i = -z.i;
	return ret;
}

double complex::arg()
{
	double ret = ams::arg(this->r,this->i);
	return ret;
}

double complex::mag()
{
	return ::sqrt(r*r+i*i);
}

bool complex::iszero()
{
	bool ret = 0;
	if(r==0.0 && i == 0.0)
  {
    ret = 1;
  }
	return ret;
}

bool complex::isreal()
{
  bool ret = 0;
	// bool ret = 0;
	// if(re>DSMALL||re<-DSMALL)
	// {
	// 	ret = 0;
	// }
	// else
	// {
	// 	ret = 1;
	// }
  if(i==0.0)
  {
    ret = 1;
  }

	return ret;
}

bool complex::isimag()
{
  bool ret = 0;
  if(r==0.0)
  {
    ret = 1;
  }
  return ret;
}

const complex complex::conj()
{
  complex cp;
  cp.r = r;
  cp.i = -i;
  return cp;
}

double real(complex z)
{
  return z.r;
}

double imag(complex z)
{
  return z.i;
}

complex conj(complex z)
{
  complex cp;
  cp.r = z.r;
  cp.i = -z.i;
  return cp;
}

complex exp(complex z)
{
  complex ret;
  ret.r = ::exp(z.r)*::cos(z.i);
  ret.i = ::exp(z.r)*::sin(z.i);
  return ret;
}


complex log(complex z)
{
  complex ret;
  ret.r = ::log(::sqrt(z.r*z.r+z.i*z.i));
  ret.i = ams::arg(z.r,z.i);
  return ret;
}

complex cos(complex z)
{
  complex ret = complex(0.0,0.0);
  complex div = complex(2.0,0.0);
  complex im1 = complex(0.0,1.0);
  ret = (exp(im1*z)+exp(im1*(-z)))/div;
  return ret;
}

complex sin(complex z)
{
  complex ret = complex(0.0,0.0);
  complex div = complex(0.0,2.0);
  complex im1 = complex(0.0,1.0);
  ret = (exp(im1*z)-exp(im1*(-z)))/div;
  return ret;
}

complex cosh(complex z)
{
  complex ret = complex(0.0,0.0);
  complex div = complex(2.0,0.0);

  ret = (exp(z)+exp((-z)))/div;
  return ret;
}

complex sinh(complex z)
{
  complex ret = complex(0.0,0.0);
  complex div = complex(2.0,0.0);

  ret = (exp(z)-exp(-z))/div;
  return ret;
}

complex tan(complex z)
{
	return sin(z)/cos(z);
}

complex tanh(complex z)
{
	return sinh(z)/cosh(z);
}

complex pow(complex z1, complex z2)
{
	complex ret;
	if(z1.mag()>DSMALL)
		ret= ams::cmp::exp(z2*ams::cmp::log(z1));
	else
		ret = 0.0;
	return ret;
}

double abs(complex z)
{
  return z.mag();
}

double arg(ams::cmp::complex z)
{
  return ams::arg(z.r,z.i);
}

//comparison operators
bool complex::operator==(const complex &z)
{
  bool ret = 0;
  if((z.r == this->r) && (z.i == this->i))
  {
    ret = 1;
  }
  return ret;
}

bool complex::operator!=(const complex &z)
{
  bool ret = !(*this == z);
  return ret;
}

//sort first by real value, then by imaginary value
//this is done so that an ordering exists, as long as two values aren't equal
bool complex::operator>(const complex& z)
{
  bool ret = 0;
  if(this->r > z.r)
  {
    ret = 1;
  }
  else if(this->r == z.r)
  {
    if(this->i > z.i)
    {
      ret = 1;
    }
  }

  return ret;
}

bool complex::operator<(const complex& z)
{
  bool ret = 0;
  ret = !((*this>z) || *this==z);
  return ret;
}

bool complex::operator>=(const complex &z)
{
  bool ret = 0;
  ret = (*this==z) || (*this>z);
  return ret;
}

bool complex::operator<=(const complex &z)
{
  bool ret = 0;
  ret = (*this<z) || (*this==z);
  return ret;
}

bool complex::isnan()
{
  bool ret = 0;
  if(ams::isnan(this->r) || ams::isnan(this->i))
  {
    ret = 1;
  }
  return ret;
}

bool complex::isinf()
{
  bool ret = 0;
  //calls math.h isinf()
  if(::isinf(this->r) || ::isinf(this->i))
  {
    ret = 1;
  }
  return ret;
}

complex csgn(complex z)
{
  double a;
  complex ret = ams::cmp::complex(0,0);
  if(z.r==0.0 && z.i==0.0)
  {
    //do nothing
  }
  else
  {
    a = ams::cmp::arg(z);
    ret.r = ::cos(a);
    ret.i = ::sin(a);
  }

  return ret;
}

};//end namespace cmp
};//end namespace ams