/*
 * file: utils/dpComplex.cpp
 * Purpose: portable complex class implementation
 * stolen from GNU's complex class
 * Author: Thomas Ott
 *
 * History: 30.07.1999: file created
 *          25.08.1999: changed calls to polar to complex_polar
 */

#include <math.h>
#include <stdlib.h>
#include "dpComplex.h"

/*
 * for debugging purposes: a counter of how many dpComplex are allocated
 */

long numberOfdpComplex = 0;

dpCOMPLEX Complex(double re, double im)
{
	dpCOMPLEX c;
	c.r=re;
	c.i=im;
	return c;
}

double Cabs(dpCOMPLEX z)
{
	gsl_complex c;
	
	GSL_SET_COMPLEX(&c, z.r, z.i);
	
	return gsl_complex_abs(c);
}

dpCOMPLEX Cmul(dpCOMPLEX a, dpCOMPLEX b)
{
	gsl_complex rv, aa, bb;
	GSL_SET_COMPLEX(&aa, a.r, a.i);
	GSL_SET_COMPLEX(&bb, b.r, b.i);
	
	rv = gsl_complex_mul(aa, bb);
	
	return Complex(GSL_REAL(rv), GSL_IMAG(rv));
}

dpCOMPLEX Cdiv(dpCOMPLEX a, dpCOMPLEX b)
{
	gsl_complex rv, aa, bb;
	GSL_SET_COMPLEX(&aa, a.r, a.i);
	GSL_SET_COMPLEX(&bb, b.r, b.i);
	
	rv = gsl_complex_div(aa, bb);
	
	return Complex(GSL_REAL(rv), GSL_IMAG(rv));
}

dpCOMPLEX RCmul(double x, dpCOMPLEX a)
{
	gsl_complex rv, aa;
	GSL_SET_COMPLEX(&aa, a.r, a.i);
	
	rv = gsl_complex_mul_real(aa, x);
	
	return Complex(GSL_REAL(rv), GSL_IMAG(rv));
}

dpComplex::dpComplex(double r, double i) {
	GSL_SET_COMPLEX(&value, r, i);
	numberOfdpComplex++;
}

dpComplex::dpComplex(const dpComplex &c) {
	GSL_SET_COMPLEX(&value, c.real(), c.imag());
	numberOfdpComplex++;
}

dpComplex::~dpComplex() {
	numberOfdpComplex--;
}

dpComplex operator + (const dpComplex& x, const dpComplex& y) {
    dpComplex result(x);
    result += y;

    return result;
}

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

dpComplex operator + (double x, const dpComplex& y) {
    return y + x;
}

dpComplex operator - (const dpComplex& x, const dpComplex& y) {
    dpComplex result;
	
    result.value = gsl_complex_sub(x.value, y.value);
	
    return result;
}

dpComplex operator - (const dpComplex& x, double y) {
    dpComplex result;
	
    result.value = gsl_complex_sub_real(x.value, y);
	
    return result;
}

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;
}

dpComplex operator * (const dpComplex& x, const dpComplex& y) {
    dpComplex result;
	
    result.value = gsl_complex_mul(x.value, y.value);
	
    return result;
}

dpComplex operator * (const dpComplex& x, double y) {
    dpComplex result;
	
    result.value = gsl_complex_mul_real(x.value, y);
	
    return result;
}

dpComplex operator * (double x, const dpComplex& y) {
    return y * x;
}

dpComplex operator / (const dpComplex& x, double y) {
    dpComplex result;
	
    result.value = gsl_complex_div_real(x.value, y);

    return result;
}

dpComplex operator + (const dpComplex& x) {
    return x;
}

dpComplex operator - (const dpComplex& x) {
    dpComplex result;
	
    result.value = gsl_complex_mul_real(x.value, -1.0);
	
    return result;
}

bool operator == (const dpComplex& x, const dpComplex& y) {
    return real(x) == real(y) && imag(x) == imag(y);
}

bool operator == (const dpComplex& x, double y) {
    return real(x) == y && imag(x) == 0;
}

bool operator == (double x, const dpComplex& y) {
    return x == real(y) && imag(y) == 0;
}

bool operator != (const dpComplex& x, const dpComplex& y) {
    return real(x) != real(y) || imag(x) != imag(y);
}

bool operator != (const dpComplex& x, double y) {
    return real(x) != y || imag(x) != 0;
}

bool operator != (double x, const dpComplex& y) {
    return x != real(y) || imag(y) != 0;
}

dpComplex& dpComplex::operator += (const dpComplex& r) {
	value = gsl_complex_add(value, r.value);
	
	return *this;
}

 dpComplex& dpComplex::operator -= (const dpComplex& r) {
	value = gsl_complex_sub(value, r.value);

	return *this;
}
 
dpComplex& dpComplex::operator *= (const dpComplex& r) {
	value = gsl_complex_mul(value, r.value);

	return *this;
}

double imag (const dpComplex& x) {
	return GSL_IMAG(x.value);
}

double real(const dpComplex& x) {
	return GSL_REAL(x.value);
}

bool isReal(const dpComplex& x) {
        return (fabs(GSL_IMAG(x.value)) < dpTOLERANCE);
}

bool isImag(const dpComplex& x) {
        return ((fabs(GSL_REAL(x.value)) < dpTOLERANCE) && (fabs(GSL_IMAG(x.value)) > dpTOLERANCE));
}

dpComplex complex_cos(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_cos(x.value);
	
	return result;
}

dpComplex complex_cosh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_cosh(x.value);
	
	return result;
}

dpComplex complex_exp(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_exp(x.value);
	
	return result;
}

dpComplex complex_log(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_log(x.value);
	
	return result;
}

dpComplex complex_pow(const dpComplex& x, const dpComplex& y) {
	dpComplex result;
	
	result.value = gsl_complex_pow(x.value, y.value);
	
	return result;
}

dpComplex complex_pow (const dpComplex& x, double y) {
	dpComplex result;
	
	result.value = gsl_complex_pow_real(x.value, y);
	
	return result;
}

dpComplex complex_pow (double x, const dpComplex& y) {
	dpComplex result(x, 0.0);
	
	return complex_pow(result, y);
}

dpComplex complex_sin(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_sin(x.value);
	
	return result;
}

dpComplex complex_sinh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_sinh(x.value);
	
	return result;
}

dpComplex complex_tan(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_tan(x.value);
	
	return result;
}

dpComplex complex_tanh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_tanh(x.value);
	
	return result;
}

dpComplex complex_asin(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arcsin(x.value);
	
	return result;
}

dpComplex complex_acos(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arccos(x.value);
	
	return result;
}

dpComplex complex_atan(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arctan(x.value);
	
	return result;
}

dpComplex complex_asinh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arcsinh(x.value);
	
	return result;
}

dpComplex complex_acosh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arccosh(x.value);
	
	return result;
}

dpComplex complex_atanh(const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_arctanh(x.value);
	
	return result;
}

dpComplex& dpComplex::operator /= (const dpComplex& y) {
	value = gsl_complex_div(value, y.value);

  	return *this;
}

dpComplex operator / (const dpComplex& x, const dpComplex& y) {
	dpComplex result;
	
	result.value = gsl_complex_div(x.value, y.value);
	
	return result;
}

dpComplex operator / (double x, const dpComplex& y) {
	dpComplex result(x, 1.0);
	
	result.value = gsl_complex_div(result.value, y.value);
	
	return result;
}

dpComplex complex_pow (const dpComplex& xin, int y) {
	dpComplex result;
	
	result.value = gsl_complex_pow_real(xin.value, (double)y);
	
	return result;
}

dpComplex complex_sqrt (const dpComplex& x) {
	dpComplex result;
	
	result.value = gsl_complex_sqrt(x.value);
	
	return result;
}

double complex_abs (const dpComplex& x) {
    return gsl_complex_abs(x.value);
}

double complex_arg (const dpComplex& x) {
    return gsl_complex_arg(x.value);
}

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

dpComplex complex_conj (const dpComplex& x) {
    dpComplex result;
	
    result.value = gsl_complex_conjugate(x.value);
	
    return result;
}

double complex_norm (const dpComplex& x) {
    return gsl_complex_abs2(x.value);
}
