
#include <amsmathutil25/amsmathutil25.hpp>

namespace ams
{
namespace cmp
{

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

complex64::complex64(float _r, float _i)
{
  r = _r; i = _i;
  return;
}

complex64::complex64(float _r)
{
  r = _r; i = 0.0;
  return;
}

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

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

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

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

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

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

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

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

 complex64 complex64::operator/(const complex64 &z)
{
	complex64 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;
}

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

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

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

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

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

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

float complex64::mag()
{
	return ::sqrtf(r*r+i*i);
}

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

bool complex64::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 complex64::isimag()
{
  bool ret = 0;
  if(r==0.0)
  {
    ret = 1;
  }
  return ret;
}

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

float real(complex64 z)
{
  return z.r;
}

float imag(complex64 z)
{
  return z.i;
}

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

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


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

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

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

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

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

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

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

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

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

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

float abs(complex64 z)
{
  return z.mag();
}

float arg(ams::cmp::complex64 z)
{
  return ams::arg(z.r,z.i);
}

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

bool complex64::operator!=(const complex64 &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 complex64::operator>(const complex64& 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 complex64::operator<(const complex64& z)
{
  bool ret = 0;
  ret = !((*this>z) || *this==z);
  return ret;
}

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

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

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

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

complex64 csgn(complex64 z)
{
  float a;
  complex64 ret = ams::cmp::complex64(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