#pragma once

#ifndef TMATH_UTIL_H
#define TMATH_UTIL_H

//#include "tcommon.h"
#include "texception.h"

#include <numeric>
#include <complex>

#ifndef __sgi
#include <cmath>
#else
#include <math.h>
#endif

#undef DVAPI
#undef DVVAR
#ifdef TNZCORE_EXPORTS
#define DVAPI DV_EXPORT_API
#define DVVAR DV_EXPORT_VAR
#else
#define DVAPI DV_IMPORT_API
#define DVVAR DV_IMPORT_VAR
#endif

enum TMathError { INFINITE_SOLUTIONS = -1 };

//-----------------------------------------------------------------------------

class DVAPI TMathException final : public TException {
  TString m_msg;

public:
  TMathException(std::string msg);
  virtual ~TMathException() {}

  TString getMessage() const override { return m_msg; }
};

//-----------------------------------------------------------------------------

/*!
  Decompose a square matrix [A] dim(A)=[n x n]  using the LU method.
  A is a matrix stored for row.
  \par A array of coeff
  \par n size of matrix
  \par indx array with permutation from pivoting
  \par d +1/-1 and depend whether the number of row interchanges (even or odd)
  \note If error a TException is thrown.
  \include mathutils_ex2.cpp
 */
DVAPI void tLUDecomposition(double *A, int n, int *indx, double &d);

//-----------------------------------------------------------------------------

/*!
  Use the back-substitution method to solve the linear
  system A*x=b.
  \par A matrix exited from tLUDecomposition
  \par n size of matrix
  \par indx array exited from tLUDecomposition
  \par b in input is array of element
  \ret b in output contains result
  \note A is the square matrix [n x n] returned from tLUDecomposition routine.
  \sa tLUDecomposition
 */
DVAPI void tbackSubstitution(double *A, int n, int *indx, double *b);

//-----------------------------------------------------------------------------

/*!
  Solve the system A*x=b.
  It`s needed to insert b in res.
  A and res are modified in the routine.
  \par A matrix
  \par n size of matrix
  \note A is square [n x n]
  \include mathutils_ex1.cpp
 */
DVAPI void tsolveSistem(double *A, int n, double *res);

//-----------------------------------------------------------------------------

/*!
  Solve the system A*x=b.
  It`s needed to insert b in res.
  A and res are modified in the routine.
  \note A is square [n x n]
  \include mathutils_ex1.cpp
 */
inline void tsolveSistem(std::vector<double> &A, std::vector<double> &res) {
  assert(res.size() * res.size() == A.size());
  tsolveSistem(&A[0], res.size(), &res[0]);
}

//-----------------------------------------------------------------------------

/*!
  Find determinant of square matrix A
  \par A is matrix to test
  \par n size of matrix
 */
DVAPI double tdet(double *A, int n);

//-----------------------------------------------------------------------------

/*!
  Find determinant of square matrix A [n x n], using information
  from tLUDecomposition decomposition.
  \par LUa output matrix from tLUDecomposition
  \par n size of matrix
  \par d parameter from tLUDecomposition
  \sa tLUDecomposition
 */
DVAPI double tdet(double *LUa, int n, double d);

//-----------------------------------------------------------------------------

/*!
  Find real root of a polynomious
  \par poly is a vector with coeff of polynomious in crescent order.
  \ret sol is the vector of solution and contain real found solution.
 */
DVAPI int rootFinding(const std::vector<double> &poly,
                      std::vector<double> &sol);

//-----------------------------------------------------------------------------

/*!
  Find complex roots of a quadratic equation
    a*x^2 + b*x + c = 0
  \ret count of roots found:
    0 if a and b are almost zero
    1 if a is almost zero
    2 if a is not almost zero
    roots may have duplicates, count depends only from type of equation
 */
DVAPI int solveEquation2(std::complex<double> *roots, double a, double b, double c);

//-----------------------------------------------------------------------------

/*!
  Find complex roots of a cubic equation:
    a*x^3 + b*x^2 + c*x + d = 0
  \ret count of roots found:
    0 if a, b and c are almost zero
    1 if a and b are almost zero
    2 if a is almost zero
    3 if a is not almost zero
    roots may have duplicates, count depends only from type of equation
 */
DVAPI int solveEquation3(std::complex<double> *roots, double a, double b, double c, double d);

//-----------------------------------------------------------------------------

/*!
  Find complex roots of a power of four equation:
    a*x^4 + b*x^3 + c*x^2 + d*x + e = 0
  \ret count of roots found:
    0 if a, b, c and d are almost zero
    1 if a, b and c are almost zero
    2 if a and b are almost zero
    3 if a is almost zero
    4 if a is not almost zero
    roots may have duplicates, count depends only from type of equation
 */
DVAPI int solveEquation4(std::complex<double> *roots, double a, double b, double c, double d, double e);

//-----------------------------------------------------------------------------

/*!
  Check if val is nearest to epsilon.
  \par val value to test
  \par eps tolerance required
  \ret true if value is nearest to zero
 */
inline bool isAlmostZero(double val, double eps = TConsts::epsilon) {
  return -eps < val && val < eps;
}

//-----------------------------------------------------------------------------

/*!
  Check if complex val is nearest to epsilon.
  \par val value to test
  \par eps tolerance required
  \ret true if value is nearest to zero
 */
inline bool isAlmostZero(std::complex<double> val, double eps = TConsts::epsilon) {
  return val.real()*val.real() + val.imag()*val.imag() < eps*eps;
}


//-----------------------------------------------------------------------------

/*!
  Find number of roots of a poly in interval min max.
  \par order degree of polynomious
  \par coeff of poly in crescent order
  \par lesser extreme
  \par greater extreme
  \ret number of root
 */
DVAPI int numberOfRootsInInterval(int order, const double *polyH, double min,
                                  double max);

//-----------------------------------------------------------------------------

/*!
  Find the real root of the poly a*x^3+b*x^2+c*x+d in [0,1]
  \par a coeff of x^3
  \par b coeff of x^2
  \par c coeff of x^1
  \par d coeff of x^0
  \ret the real root of the poly, or 1.0 if the root is not in [0,1]
*/
DVAPI double cubicRoot(double a, double b, double c, double d);

//-----------------------------------------------------------------------------

/*!
  Find the root of the poly a*x^2+b*x+c
  \par a coeff of x^2
  \par b coeff of x^1
  \par c coeff of x^0
  \ret the root of the poly, or 1.0 if it does not exist
*/
DVAPI double quadraticRoot(double a, double b, double c);

//-----------------------------------------------------------------------------

#endif  // #ifndef TMATH_UTIL_H
//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------
