/*
Copyright (c) 2009-2013, UT-Battelle, LLC
All rights reserved

[PsimagLite, Version 1.0.0]

*********************************************************
THE SOFTWARE IS SUPPLIED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED.

Please see full open source license included in file LICENSE.
*********************************************************

*/

#ifndef PSICOMPLEX_H_
#define PSICOMPLEX_H_

#include "../loki/TypeTraits.h"
#include "AllocatorCpu.h"
#include <complex>

namespace PsimagLite
{

template <typename ComplexOrRealType>
class Real
{
public:

	typedef ComplexOrRealType Type;
};

template <typename RealType>
class Real<std::complex<RealType>>
{
public:

	typedef RealType Type;
};

template <typename T>
class IsComplexNumber
{
public:

	enum OpaqueEnum { True = false };
};

template <typename T>
class IsComplexNumber<std::complex<T>>
{
public:

	enum OpaqueEnum { True = Loki::TypeTraits<T>::isArith };
};

template <typename T>
struct IsNumber {
	enum {
		True = (IsComplexNumber<T>::True || Loki::TypeTraits<T>::isArith)
	};
};

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isArith, T>::Type real(T t)
{
	return t;
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isArith, T>::Type
real(const std::complex<T>& t)
{
	return std::real(t);
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isArith, T>::Type imag(T)
{
	return 0.0;
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isArith, T>::Type
imag(const std::complex<T>& t)
{
	return std::imag(t);
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isFloat, T>::Type conj(T t)
{
	return t;
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isFloat, std::complex<T>>::Type
conj(const std::complex<T>& t)
{
	return std::conj(t);
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isArith, T>::Type norm(T t)
{
	return std::fabs(t);
}

template <typename T>
typename EnableIf<Loki::TypeTraits<T>::isFloat, T>::Type
norm(const std::complex<T>& t)
{
	return std::norm(t);
}
} // namespace PsimagLite

namespace std
{
template <typename T>
typename PsimagLite::EnableIf<Loki::TypeTraits<T>::isFloat,
    std::complex<T>>::Type
operator*(int x, const std::complex<T>& y)
{
	return std::complex<T>(real(y) * x, imag(y) * x);
}
} // namespace std

#endif // PSICOMPLEX_H_
