#ifndef __COMPLEX_H
#define __COMPLEX_H

#include <string>
#include <exception>


/**
 * Complexクラスの例外クラス。
 * 例として、演算が成立しない場合など。
 * 
 */
class ComplexException: public exception{
  private:
    string what_str;
  public:
    ComplexException(const string &what_arg): what_str(what_arg){}
    ~ComplexException() throw() {}
    /**
     * エラー内容を取得します。
     * 
     * @return (chsr *) エラー内容
     */
    const char *what() const throw(){
      return what_str.c_str(); 
    }
};

#include <ostream>
#include <math.h>

#ifndef PI
  #define PI 3.14159265358973
#endif

/**
 * 複素数をあらわすクラスです。
 * 
 */
template <class T>
class Complex{
  private:
    T m_Real;
    T m_Imaginary;
  public:
    /**
     * コンストラクタ。
     * 
     * @param real 実数部
     * @param imaginary 虚数部
     */
    Complex(const T &real, const T &imaginary)
       : m_Real(real), m_Imaginary(imaginary){}
    /**
     * コンストラクタ。
     * 虚数部は0で初期化されます。
     * 
     * @param real 実数部
     */
    Complex(const T &real) : m_Real(real), m_Imaginary(0){}
    /**
     * コンストラクタ。
     * 実数部、虚数部ともに0に初期化されます。
     * 
     */
    Complex() : m_Real(0), m_Imaginary(0){}
    /**
     * デストラクタ。
     * 
     */
    ~Complex(){}
    
    /**
     * 実数部を返します。
     * 
     * @return (T) 実数部
     */
    T &real(){return m_Real;}
    /**
     * 虚数部を返します。
     * 
     * @return (T) 虚数部
     */
    T &imaginary(){return m_Imaginary;}
    
    /**
     * 絶対値の二乗を返します。
     * pow(real(), 2) + pow(imaginary(), 2)をしています。
     * 
     * @return (T) 絶対値の二乗
     * @see pow(T, T)
     */
    T abs2() const {return pow(m_Real, 2) + pow(m_Imaginary, 2);}
    /**
     * 絶対値を返します。
     * sqrt(abs2())をしています。
     * 
     * @return (T) 絶対値
     * @see abs2()
     * @see sqrt()
     */
    T abs() const {return sqrt(abs2());}
    /**
     * 偏角を返します。
     * 
     * @return (double) 偏角
     */
    double arg() const {
      if(m_Real == T(0)){return m_Imaginary < T(0) ? -PI/2 : PI/2;}
      if(m_Real < T(0)){
        return atan(m_Imaginary / m_Real) + (m_Imaginary < T(0) ? -PI : PI);
      }else{
        return atan(m_Imaginary / m_Real);
      }
    }
    /**
     * 指定乗します。
     * もし、虚数部が0でなく、かつ乗数が1以下の場合、エラーとなります。
     * 
     * @param factor 乗数
     * @return (Complex<T>) 結果
     * @throw ComplexException 演算が一意に成立しない場合
     */
    Complex<T> power(const T &factor) const throw(ComplexException){
        if(m_Imaginary == T(0)){
        if(m_Real < T(0)){throw ComplexException("Operation void!!");}
        return Complex(pow(m_Real, factor));
        }else{
            if(factor < T(1)){throw ComplexException("Operation void!!");}
            T _abs = pow(abs(), factor);
            double _arg = arg() * factor;
            return Complex(_abs * cos(_arg), _abs * sin(_arg));
        }
    }  
    
    /**
     * 共役複素数を返します。
     * 
     * @return (Complex<T>) 共役複素数
     */
    Complex<T> conjugate() const {
      Complex<T> result = *this;
      result.imaginary() *= -1;
      return result;
    }
    
    bool operator==(const Complex<T> &complex) const{
      return m_Real == const_cast<Complex *>(&complex)->real() ?
                        m_Imaginary == const_cast<Complex *>(&complex)->imaginary() :
                        false;
    }
    bool operator!=(const Complex<T> &complex) const{
      return !(*this == complex);
    }
    
    Complex<T> &operator+=(const T &scalar){
      m_Real += scalar;
      return *this;
    }
    Complex<T> operator+(const T &scalar) const{
      Complex<T> result = *this;
      return (result += scalar);
    }
    friend Complex<T> operator+(const T &scalar, const Complex<T> complex){return (complex + scalar);}
    
    Complex<T> &operator-=(const T &scalar){return (*this) += (-scalar);}    
    Complex<T> operator-(const T &scalar) const{return (*this) + (-scalar);}
    friend Complex<T> operator-(const T &scalar, const Complex<T> complex){return (complex -scalar);}
        
    Complex<T> &operator *=(const T &scalar){
      m_Real *= scalar;
      m_Imaginary *= scalar;
      return *this;
    }
    Complex<T> operator*(const T &scalar) const{
      Complex<T> result = *this;
      return (result *= scalar);
    }
    friend Complex<T> operator*(const T &scalar, const Complex<T> complex){return (complex * scalar);}
    
    Complex<T> &operator/=(const T &scalar){return (*this) *= (1/scalar);}
    Complex<T> operator/(const T &scalar) const{return (*this) * (1/scalar);}
    friend Complex<T> operator/(const T &scalar, const Complex<T> complex){return (complex / scalar);}
    
    Complex<T> operator -() const{return ((*this) * -1);}
    
    Complex<T> &operator+=(const Complex<T> &complex){
      m_Real += (const_cast<Complex *>(&complex))->real();
      m_Imaginary += (const_cast<Complex *>(&complex))->imaginary();
      return *this;
    }
    Complex<T> operator+(const Complex<T> &complex) const{
      Complex<T> result = *this;
      return (result += complex);
    }
    
    Complex<T> &operator-=(const Complex<T> &complex){
      return ((*this) += (-complex));
    }
    Complex<T> operator-(const Complex<T> &complex) const{
      return ((-complex) += (*this));
    }
    
    Complex<T> operator*(const Complex<T> &complex) const{
      Complex<T> result(m_Real * (const_cast<Complex *>(&complex))->real()
                          -m_Imaginary * (const_cast<Complex *>(&complex))->imaginary(),
                        m_Real * (const_cast<Complex *>(&complex))->imaginary()
                          + m_Imaginary * (const_cast<Complex *>(&complex))->real());
      return result;
    }
    Complex<T> &operator*=(const Complex<T> &complex){
      Complex<T> copy = *this;
      m_Real = 
        copy.real() * (const_cast<Complex *>(&complex))->real()
        -copy.imaginary() * (const_cast<Complex *>(&complex))->imaginary();
      m_Imaginary =
        copy.real() * (const_cast<Complex *>(&complex))->imaginary()
        + copy.imaginary() * (const_cast<Complex *>(&complex))->real();
      return *this;
    }
    
    Complex<T> &operator/=(const Complex<T> &complex){
      return (*this) *= (complex.conjugate() / complex.abs2());
    }
    Complex<T> operator/(const Complex<T> &complex) const{
      Complex<T> copy = (*this);
      return (copy /= complex);
    }
    
    /**
     * みやすい形で複素数を出力します。
     * 
     */
    friend ostream &operator<<(ostream &out, const Complex<T> &complex){
      out << (const_cast<Complex *>(&complex))->real() << " + "
          << (const_cast<Complex *>(&complex))->imaginary() << "i";
      return out;  
    }
};

#endif /* __COMPLEX_H */