/*
 * file: utils/dpComplex.h
 * Purpose: portable complex class definition
 * stolen from GNU's complex class
 * Author: Thomas Ott
 *
 * History: 30.07.1999: file created
 *          25.08.1999: changed method polar to complex_polar so it
 *                      doesn't interfere with other complex classes
 *          20.10.2004: now uses GNU Scientific Library code
 */

#ifndef dpComplex_H
#define dpComplex_H

#include <math.h>
#include <string.h>

#include <gsl/gsl_complex_math.h>
#include "defines.h"

dpCOMPLEX Complex(double re, double im);
double Cabs(dpCOMPLEX z);
dpCOMPLEX Cmul(dpCOMPLEX a, dpCOMPLEX b);
dpCOMPLEX Cdiv(dpCOMPLEX a, dpCOMPLEX b);
dpCOMPLEX RCmul(double x, dpCOMPLEX a);

extern long numberOfdpComplex;

class dpComplex {
public:
	dpComplex (double r = 0.0, double i = 0.0);
	dpComplex(const dpComplex &);
	~dpComplex();
	dpComplex& operator += (const dpComplex&);
	dpComplex& operator -= (const dpComplex&);
	dpComplex& operator *= (const dpComplex&);
	dpComplex& operator /= (const dpComplex&);
	double real () const { return GSL_REAL(value); }
	double imag () const { return GSL_IMAG(value); }
//private:
	gsl_complex value;

  // These functions are specified as friends for purposes of name injection;
  // they do not actually reference private members.
  friend double real (const dpComplex&);
  friend double imag (const dpComplex&);
	friend bool isReal(const dpComplex&);
	friend bool isImag(const dpComplex&);
  friend dpComplex operator + (const dpComplex&, const dpComplex&);
  friend dpComplex operator + (const dpComplex&, double);
  friend dpComplex operator + (double, const dpComplex&);
  friend dpComplex operator - (const dpComplex&, const dpComplex&);
  friend dpComplex operator - (const dpComplex&, double);
  friend dpComplex operator - (double, const dpComplex&);
  friend dpComplex operator - (const dpComplex&);
  friend dpComplex operator * (const dpComplex&, const dpComplex&);
  friend dpComplex operator * (const dpComplex&, double);
  friend dpComplex operator * (double, const dpComplex&);
  friend dpComplex operator / (const dpComplex&, const dpComplex&);
  friend dpComplex operator / (const dpComplex&, double);
  friend dpComplex operator / (double, const dpComplex&);
  friend bool operator == (const dpComplex&, const dpComplex&);
  friend bool operator == (const dpComplex&, double);
  friend bool operator == (double, const dpComplex&);
  friend bool operator != (const dpComplex&, const dpComplex&);
  friend bool operator != (const dpComplex&, double);
  friend bool operator != (double, const dpComplex&);
  friend dpComplex complex_polar (double, double);
  friend dpComplex complex_pow (const dpComplex&, const dpComplex&);
  friend dpComplex complex_pow (const dpComplex&, double);
  friend dpComplex complex_pow (const dpComplex&, int);
  friend dpComplex complex_pow (double, const dpComplex&);
};

// double imag(const dpComplex &);
// double real(const dpComplex &);
// bool isReal(const dpComplex &);
// bool isImag(const dpComplex &s);

// inline dpComplex operator + (const dpComplex& x, const dpComplex& y) {
// 	dpComplex result(x);
// 	result += y;
// 
// 	return result;
// }

// inline dpComplex operator + (const dpComplex& x, double y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_add_real(x.value, y);
// 	
// 	return result;
// }
// 
// inline dpComplex operator + (double x, const dpComplex& y) {
// 	return y + x;
// }

// inline dpComplex operator - (const dpComplex& x, const dpComplex& y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_sub(x.value, y.value);
// 	
// 	return result;
// }
// 
// inline dpComplex operator - (const dpComplex& x, double y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_sub_real(x.value, y);
// 	
// 	return result;
// }
// 
// inline dpComplex operator - (double x, const dpComplex& y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_mul_real(y.value, -1.0);
// 	result.value = gsl_complex_add_real(result.value, x);
// 	
// 	return result;
// }
// 
// inline dpComplex operator * (const dpComplex& x, const dpComplex& y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_mul(x.value, y.value);
// 	
// 	return result;
// }
// 
// inline dpComplex operator * (const dpComplex& x, double y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_mul_real(x.value, y);
// 	
// 	return result;
// }
// 
// inline dpComplex operator * (double x, const dpComplex& y) {
// 	return y * x;
// }
// 
// inline dpComplex operator / (const dpComplex& x, double y) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_div_real(x.value, y);
// 
// 	return result;
// }
// 
// inline dpComplex operator + (const dpComplex& x) {
// 	return x;
// }
// 
// inline dpComplex operator - (const dpComplex& x) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_mul_real(x.value, -1.0);
// 	
// 	return result;
// }
// 
// inline bool operator == (const dpComplex& x, const dpComplex& y) {
// 	return real(x) == real(y) && imag(x) == imag(y);
// }
// 
// inline bool operator == (const dpComplex& x, double y) {
// 	return real(x) == y && imag(x) == 0;
// }
// 
// inline bool operator == (double x, const dpComplex& y) {
// 	return x == real(y) && imag(y) == 0;
// }
// 
// inline bool operator != (const dpComplex& x, const dpComplex& y) {
// 	return real(x) != real(y) || imag(x) != imag(y);
// }
// 
// inline bool operator != (const dpComplex& x, double y) {
// 	return real(x) != y || imag(x) != 0;
// }
// 
// inline bool operator != (double x, const dpComplex& y) {
// 	return x != real(y) || imag(y) != 0;
// }

// inline double complex_abs (const dpComplex& x) {
// 	return gsl_complex_abs(x.value);
// }
// 
// inline double complex_arg (const dpComplex& x) {
// 	return gsl_complex_arg(x.value);
// }

// inline dpComplex complex_polar (double r, double t)
// {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_polar(r, t);
// 	
// 	return result;
// }

// inline dpComplex complex_conj (const dpComplex& x) {
// 	dpComplex result;
// 	
// 	result.value = gsl_complex_conjugate(x.value);
// 	
// 	return result;
// }
// 
// inline double complex_norm (const dpComplex& x) {
// 	return gsl_complex_abs2(x.value);
// }

// Declarations of functions in dpComplex.cpp

	dpComplex operator / (const dpComplex&, const dpComplex&);
	dpComplex operator / (double, const dpComplex&);
	dpComplex complex_cos (const dpComplex&);
	dpComplex complex_cosh (const dpComplex&);
	dpComplex complex_exp (const dpComplex&);
	dpComplex complex_log (const dpComplex&);
	dpComplex complex_pow (const dpComplex&, const dpComplex&);
	dpComplex complex_pow (const dpComplex&, double);
	dpComplex complex_pow (const dpComplex&, int);
	dpComplex complex_pow (double, const dpComplex&);
	dpComplex complex_sin (const dpComplex&);
	dpComplex complex_sinh (const dpComplex&);
	dpComplex complex_sqrt (const dpComplex&);
	dpComplex complex_tan (const dpComplex&);
	dpComplex complex_tanh (const dpComplex&);
	dpComplex complex_asin (const dpComplex&);
	dpComplex complex_acos (const dpComplex&);
	dpComplex complex_atan (const dpComplex&);
	dpComplex complex_asinh (const dpComplex&);
	dpComplex complex_acosh (const dpComplex&);
	dpComplex complex_atanh (const dpComplex&);
   double complex_abs (const dpComplex& x);
   dpComplex complex_conj (const dpComplex& x);
   double complex_arg (const dpComplex& x);
   dpComplex complex_polar (double, double);

#endif
