/*
 *  complex.hpp
 *
 *  Created by D. Kevin McGrath on 11/14/2007.
 *
 */

#include <iostream>
#include <sstream>
#include <cmath>
#include <string>

#ifndef DMCGRATH_COMPLEX_HPP
#define DMCGRATH_COMPLEX_HPP

namespace dmcgrath{
	
	class complex{
		
	private:
		double real_val;
		double imaginary;
		
		
	public:
		/*
		 constructors:
		 default: set both real_val and imaginary to 0
		 single parameter: set real_val to parameter value, imaginary to 0
		 two parameters: real_val, imaginary
		 copy: copy constructor
		 */
		complex(){
			this->real_val = 0;
			this->imaginary = 0;
		}
		
		complex(double real_val){
			this->real_val = real_val;
			this->imaginary = 0;
		}
		
		complex(double real_val, double imaginary){
			this->real_val = real_val;
			this->imaginary = imaginary;
		}
		
		complex(const complex &c){
			this->real_val = c.real_val;
			this->imaginary = c.imaginary;
		}
		
		~complex(){
			
		}
		
		//return the real part
		const double real(){
			return this->real_val;
		}
		
		//return the imaginary part
		const double imag(){
			return this->imaginary;
		}
		
		//flip sign on imaginary part, return a NEW VALUE
		const complex conj(){
			complex tmp((this->real_val), -(this->imaginary));
			return tmp;
		}
		
		//overload output operator
		friend std::ostream& operator<<(std::ostream &os, const complex &c){
			std::stringstream ss;
			std::string outs;
			ss << '(' << c.real_val << ',' << c.imaginary << ')';

			ss >> outs;
			os << outs;
			return os;
		}
		
		//overload input operator
		friend std::istream& operator>>(std::istream &is, complex &c){
			
			char paren;
			char paren_or_comma;
			double real_val;
			double imaginary;
			
			is >> paren;
			if (paren == '('){
				//of the format (r,c) or (r) if legal
				is >> real_val;
				is >> paren_or_comma;
				if(paren_or_comma == ',')
					//then it is (r,c)
					is >> imaginary >> paren;
				else if(paren_or_comma == ')')
					//is just (r)
					imaginary = 0;
			}
			else{
				//of the format r if legal
				//we have the first character of the real_val value, so...
				std::stringstream ss;
				
				//stick it on a stringstream
				ss << paren;
				
				//get the rest of the value
				is >> real_val; 
				
				//combine with beginning
				ss << real_val;
				
				//store in real_val
				ss >> real_val;
			}
			
			c.real_val = real_val;
			c.imaginary = imaginary;
			
			return is;
		}
		
		//absolute value of c
		double abs(){
			return std::sqrt(norm());
		}
		
		//square absolute value
		const double norm(){
			double real_val_square = this->real_val * this->real_val;
			double imag_square = this->imaginary * this->imaginary;
			return real_val_square + imag_square;
		}
		
		//returns the phase angle, in radians
		double phase(){
			return std::atan2(this->imaginary, this->real_val);
		}
		
		//TODO: <, <=, >, >=
		
		bool operator==(const complex &RHS){
			return (this->real_val == RHS.real_val && 
					this->imaginary == RHS.imaginary);
		}

		bool operator!=(const complex &RHS){
			return !(*this == RHS);
		}
		
		
		bool operator+=(const complex &RHS){
			complex tmp(*this + RHS);
			this->real_val = tmp.real_val;
			this->imaginary = tmp.imaginary;
			return true;
		}
		
		bool operator-=(const complex &RHS){
			complex tmp(*this - RHS);
			this->real_val = tmp.real_val;
			this->imaginary = tmp.imaginary;
			return true;
		}
		
		bool operator*=(const complex &RHS){
			complex tmp(*this * RHS);
			this->real_val = tmp.real_val;
			this->imaginary = tmp.imaginary;
			return true;
		}
		
		bool operator/=(complex &RHS){
			complex tmp(*this / RHS);
			this->real_val = tmp.real_val;
			this->imaginary = tmp.imaginary;
			return true;
		}
		
		bool operator=(const complex &RHS){
			this->real_val = RHS.real_val;
			this->imaginary = RHS.imaginary;
			return true;
		}
		
		complex operator+(const complex &RHS){
			complex tmp;
			tmp.real_val = this->real_val + RHS.real_val;
			tmp.imaginary = this->imaginary + RHS.imaginary;
			
			return tmp;
		}
		
		complex operator-(const complex &RHS){
			complex tmp;
			tmp.real_val = this->real_val - RHS.real_val;
			tmp.imaginary = this->imaginary - RHS.imaginary;
			
			return tmp;
		}
		
		complex operator*(const complex &RHS){
			complex tmp;
			tmp.real_val = this->real_val * RHS.real_val - this->imaginary * RHS.imaginary;
			tmp.imaginary = this->imaginary * RHS.real_val + RHS.imaginary * this->real_val;
			
			return tmp;
		}
		
		complex operator/(complex &rhs){
			complex tmp = rhs.conj();
			
			double denominator = rhs.norm();
			complex numerator = (*this) * tmp;
			
			tmp.real_val = numerator.real_val / denominator;
			tmp.imaginary = numerator.imaginary / denominator;
			
			return tmp;
		}
		
		
	};
		
}

#endif