//---------------------------------------------------------------------------
// ŠNikolai V. Shokhirev, 2004-2008  <nikolai@shokhirev.com> http://www.shokhirev.com/nikolai.html
// Variant 2 - based on compound assignment
//---------------------------------------------------------------------------
#include "Complex.h"

using namespace std;

Complex::Complex()
{
  Re = 0.0;  Im = 0.0;
}

Complex::Complex( real re, real im )
{
  Re = re;  Im = im;
}

Complex::~Complex()
{
}

Complex::Complex( const Complex& c )
{
  Re = c.Re;  Im = c.Im;
}

Complex& Complex::operator=( const Complex& c )
{
  if(this != &c) { Re = c.Re; Im = c.Im; }
  return (*this);
}

Complex& Complex::operator=( const real& d )
{
  Re = d;  Im = 0.0;
  return (*this);
}

Complex& Complex::operator+=( const Complex& c )
{
  Re += c.Re;  Im += c.Im;
  return (*this);
}

Complex& Complex::operator+=( const real& d )
{
  Re += d;
  return (*this);
}

Complex& Complex::operator-=( const Complex& c )
{
  Re -= c.Re;  Im -= c.Im;
  return (*this);
}

Complex& Complex::operator-=( const real& d )
{
  Re -= d;
  return (*this);
}

Complex& Complex::operator*=( const Complex& c )
{
  real re = Re;      // backup Re, as we're going to modify it
  real cre = c.Re;   // backup c.Re too, in case c *= c;
  Re = Re * c.Re - Im * c.Im;
  Im = re * c.Im + Im * cre;
  return (*this);
}

Complex& Complex::operator*=( const real& d )
{
  Re *= d; // same as above with c.Im = 0 and c.Re = d
  Im *= d; // logically and happily simplified
  return (*this);
}

Complex& Complex::operator/=( const Complex& c )
{
  if (this == &c) {     // c /= c;
    Re = 1.0;
    Im = 0.0;
  }
  else {
    real re = Re;    // backup Re, as we're going to modify it
    real m = mod2(c);
    Re = ( Re * c.Re + Im * c.Im ) / m;
    Im = ( Im * c.Re - re * c.Im ) / m;
  }
  return (*this);
}

Complex& Complex::operator/=( const real& d )
{
  Re /= d;  Im /= d;
  return (*this);
}

bool IsZero(const Complex& c)
{
  return (c.Re == 0.0) && (c.Im == 0.0);
}


// Prints out a complex with the form a+bi or a-bi or 0
ostream& operator<<( ostream& s, const Complex& c )
{
  s << c.Re << setiosflags( ios::showpos );
  if( c.Im != 0 )
  {
    s << c.Im << "i";
  }
  s << resetiosflags ( ios::showpos );

  return s;
}

// Reads a complex number c into the input stream s.
// Format: number + ('+' or '-') + number + ('i' or 'j' or 'I' or 'J')
// spaces can be used between the elements, but not inside an element ("- 7+2i" is incorrect)
// Valid:  1.2+5i ; 1e2-.5J (= 100-0.5i) ; -4 + -8 i (= -4-8i)
// Invalid: - 7+5I (space inside the first number) ; 7i-4 (order)
// If bad input is encountered, s.setstate(ios::failbit) is called.
istream& operator>>( istream& s, Complex& c )
{
  char op, ij;
  if( !( (s >> c.Re >> op >> c.Im >> ij)
    && (op == '+' || op == '-')
    && (ij == 'i' || ij == 'I' || ij == 'j' || ij == 'J') ) )
  {
    s.setstate( ios::failbit );
  }
  else if( op == '-' && c.Im != 0.0 ) {
    c.Im *= -1;
  }

  return s;
}

Complex operator+( const Complex& lhs, const Complex& rhs )
{
  return Complex(lhs) += rhs;
}

Complex operator+( const Complex& lhs )
{
  return Complex( lhs );
}

Complex operator-( const Complex& lhs, const Complex& rhs )
{
  return Complex(lhs) -= rhs;
}

Complex operator-( const Complex& c )
{
  return Complex(-c.Re , -c.Im);
}

Complex operator*( const Complex& lhs, const Complex& rhs )
{
  return Complex(lhs) *= rhs;
}

Complex operator/( const Complex& lhs, const Complex& rhs )
{
  return Complex(lhs) /= rhs;
}

// Complex library

real mod2(const Complex& c)
{
  return (c.Re*c.Re + c.Im*c.Im);
}


real mod(const Complex& c)

{

  return sqrt(mod2(c));

}


bool Equal(Complex c1, Complex c2, real eps)

{

  return (fabs(c1.Re-c2.Re) < eps && fabs(c1.Im-c2.Im) < eps);

}


bool Equal(Complex c1, real r, real i, real eps)

{

  return (fabs(c1.Re-r) < eps && fabs(c1.Im-i) < eps);

}

Complex exp(const Complex& c)
{
  return (exp(c.Re)*Complex(cos(c.Im),sin(c.Im)));
}

// result = sqrt(z), result.Re > 0
Complex Csqrt(Complex z)
{
  Complex result;
  result.Re = sqrt(abs(0.5*(mod(z) + z.Re)));
  result.Im = sqrt(abs(0.5*(mod(z) - z.Re)));
  return result;
};