/*! \file pimathcomplex.h
 * \ingroup Math
 * \~\brief
 * \~english Complex numbers
 * \~russian Комплексные числа
 */
/*
	PIP - Platform Independent Primitives
	PIP math complex
	Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIMATHCOMPLEX_H
#define PIMATHCOMPLEX_H

#include <complex>
#include "pimathbase.h"
#include "pibytearray.h"
#include "pivector2d.h"

#define PIP_MATH_COMPLEX

using std::complex;

typedef complex<int> complexi;
typedef complex<short> complexs;
typedef complex<float> complexf;
typedef complex<ldouble> complexld;
#ifndef QPIEVALUATOR_COMPLEX
typedef complex<double> complexd;

const complexd complexd_i(0., 1.);
const complexd complexd_0(0.);
const complexd complexd_1(1.);
#endif
const complexld complexld_i(0., 1.);
const complexld complexld_0(0.);
const complexld complexld_1(1.);

inline complexd sign(const complexd & x) {return complexd(sign(x.real()), sign(x.imag()));}

inline complexd round(const complexd & c) {return complexd(piRound<double>(c.real()), piRound<double>(c.imag()));}
inline complexd floor(const complexd & c) {return complexd(floor(c.real()), floor(c.imag()));}
inline complexd ceil (const complexd & c) {return complexd(ceil(c.real()), ceil(c.imag()));}

#define acosc acos
#define asinc asin
#define atanc atan

#ifdef CC_GCC
#  if CC_GCC_VERSION <= 0x025F
inline complexd tan(const complexd & c) {return sin(c) / cos(c);}
inline complexd tanh(const complexd & c) {return sinh(c) / cosh(c);}
inline complexd log2(const complexd & c) {return log(c) / M_LN2;}
inline complexd log10(const complexd & c) {return log(c) / M_LN10;}
#  endif
#endif

template<typename T>
inline PICout operator <<(PICout s, const complex<T> & v) {s.space(); s.saveAndSetControls(0); s << "(" << v.real() << "; " << v.imag() << ")"; s.restoreControls(); return s;}


inline PIVector<double> abs(const PIVector<complexd> & v) {
	PIVector<double> result;
	result.resize(v.size());
	for (uint i = 0; i < v.size(); i++)
		result[i] = abs(v[i]);
	return result;
}


inline PIVector2D<double> abs(const PIVector2D<complexd> & v) {
	PIVector2D<double> result(v.rows(), v.cols());
	for (uint i = 0; i < v.rows(); i++)
		for (uint j = 0; j < v.cols(); j++)
			result[i][j] = abs(v.element(i,j));
	return result;
}


/**
* \brief floating point number specific comparison between value passed as parameter and zero
*
* @param v floating point parameter for comparison
* @return true if v in locality of zero, otherwise false
*/
template<typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
inline bool PIMathFloatNullCompare(const T v) {
	static_assert(std::is_floating_point<T>::value, "Type must be floating point");
	return (piAbs(v) < T(1E-200));
}

/**
* \brief floating point number specific comparison between parameter value and zero.
*
* @param v complex with floating point real and imag parts
* @return true if absolute of v in locality of zero, otherwise false
*/
template<typename T,typename std::enable_if<
		std::is_floating_point<decltype(T::real)>::value &&
		std::is_floating_point<decltype(T::imag)>::value
		, int>::type = 0>
inline bool PIMathFloatNullCompare(const T v) {
	static_assert(std::is_floating_point<decltype(v.real)>::value, "Type must be floating point");
	static_assert(std::is_floating_point<decltype(v.imag)>::value, "Type must be floating point");
	return (abs(v) < float(1E-200));
}

#endif // PIMATHCOMPLEX_H
