/*
 * Copyright University of Reims Champagne-Ardenne
 * Authors and Contributors: Akilan RAJAMANI, Corentin LEFEBVRE, Johanna KLEIN,
 *                           Emmanuel PLUOT, Gaetan RUBEZ, Hassan KHARTABIL,
 *                           Jean-Charles BOISSON and Eric HENON
 * (24/07/2017)
 * jean-charles.boisson@univ-reims.fr, eric.henon@univ-reims.fr
 *
 * This software is a computer program whose purpose is to
 * detect and quantify interactions from electron density
 * obtained either internally from promolecular density or
 * calculated from an input wave function input file. It also
 * prepares for the visualization of isosurfaces representing
 * several descriptors (dg) coming from the IGM methodology.
 *
 * This software is governed by the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL-C
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C license and that you accept its terms.
 *
 * */

/**
 * @file toolbox.h

 * @brief generic methods*/

#ifndef _TOOLBOX_H_
#define _TOOLBOX_H_


// STL
// JC Move to general
//#include <set>
//#include <sstream>
//#include <iostream>
//#include <fstream>
//#include <iomanip>
//#include <fstream>
//#include <cstdlib>
//#include <vector>

// LOCAL
//#include <general.h>
#include <eig3.h>
//#include <reader.h> fait planter la compil ...
// donc, je redefinis les atomnames ici ...: pas propre ...
const std::string ATOM_NAMES_ERIC [] =
  {
    "H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr","Nb","Mo","Tc", "Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No","Lr"
  };
  

/**
 * @fn double getMaxFromPrec3DMatrix (double*** _3DMatrix, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ, double *** rho, const double lowerlimit, const double upperlimit)
 * @brief From a 3D matrix, return the maximum value found
 * @param _3DMatrix The matrix to parse the cube itself
 * @param dimX The first dimension of the matrix
 * @param dimY The second dimension of the matrix
 * @param dimZ The third dimension of the matrix
 * @param rho  The ED cube
 * @param lowerlimit The lower limit of the ED range used to filter the _3DMatrix cube
 * @param upperlimit The upper limit of the ED range used to filter the _3DMatrix cube
 * @return The maximum value found but according the given rho threshold*/
double getMaxFromPrec3DMatrix (double*** _3DMatrix, const unsigned int dimX, const unsigned int dimY, 
                               const unsigned int dimZ, double *** rho, const double lowerlimit, const double upperlimit);

/**
 * @fn double getMinFromPrec3DMatrix (double*** _3DMatrix, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ, double *** rho, const double lowerlimit, const double upperlimit)
 * @brief From a 3D matrix, return the maximum value found
 * @param _3DMatrix The matrix to parse the cube itself
 * @param dimX The first dimension of the matrix
 * @param dimY The second dimension of the matrix
 * @param dimZ The third dimension of the matrix
 * @param rho  The ED cube
 * @param lowerlimit The lower limit of the ED range used to filter the _3DMatrix cube
 * @param upperlimit The upper limit of the ED range used to filter the _3DMatrix cube
 * @return The minimum value found but according the given rho threshold*/
double getMinFromPrec3DMatrix (double*** _3DMatrix, const unsigned int dimX, const unsigned int dimY,
                               const unsigned int dimZ, double *** rho, const double lowerlimit, const double upperlimit);


/**
 * @fn double getMaxFrom2Matrices(double*** cube1, double*** cube2,double threshold, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ)
 * @brief From cube1, return the maximum value found for which cube2 > threshold
 * @param cube1 The matrix where to find the maximum value
 * @param cube2 The matrix providing the condition test               
 * @param dimX The first dimension of the matrix
 * @param dimY The second dimension of the matrix
 * @param dimZ The third dimension of the matrix
 * @return The maximum value found but according cube2 */
double getMaxFrom2Matrices(double*** cube1, double*** cube2,double threshold, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ);
// this method returns the maximum value found in cube1 for which cube2 >threshold


/**
 * @fn double getMaxFromPrecVectorized3DMatrix (double* _Vectorized3DMatrix, double* cubeRho, const double lowerlimit, const double upperlimit, unsigned int dimX, const unsigned int dimY, const unsigned int dimZ)
 * @brief From a Vectorized 3D matrix, return the maximum value found
 * @param _Vectorized3DMatrix The matrix to parse the cube itself
 * @param cubeRho The electron density cube serving to filter the _Vectorized3DMatrix
 * @param lowerlimit The lower limit of the ED range used to filter the _Vectorized3DMatrix
 * @param upperlimit The upper limit of the ED range used to filter the _Vectorized3DMatrix
 * @param dimX The first dimension of the matrix
 * @param dimY The second dimension of the matrix
 * @param dimZ The third dimension of the matrix
 * @warning The matrix is a vector 
 * @return The maximum value found but according the given rho range*/
//double getMaxFromPrecVectorized3DMatrix (double* _Vectorized3DMatrix, double* cubeRho, const double threshold, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ);
double getMaxFromPrecVectorized3DMatrix (double* _Vectorized3DMatrix, double* cubeRho, const double lowerlimit, const double upperlimit,
                                         unsigned int dimX, const unsigned int dimY, const unsigned int dimZ);


/**
 * @fn std::string getCondensedInformationFromNumericSet(std::set<int> numericValues)
 * @brief From a set of numeric value, return a string version of the content with interval (if the information is 1,2,3,4,5,6,9; return "1-6, 9")
 * @param numericValues The set that contains numeric values (so sorted and unique)
 * @return The string version of the information */
std::string getCondensedInformationFromNumericSet(std::set<int> numericValues);

/**
 * @fn void space(size_t nbSpaces, size_t limit=100)
 * @brief Print spaces characters
 * @param nbSpaces thenumber of space to print 
 * @param limit the upper bound for avoid odd behavior */
void space(size_t nbSpaces, size_t limit=100);

/**
 * @fn void printCurrentState(unsigned int done, unsigned int total, time_t ellapsed)
 * @brief Tool to display timing info for user 
 * @param done The number of grid points completed
 * @param total The total number of grid point to process
 * @param ellapsed The time spent until now              */
//void printCurrentState(bool start=true, bool end=false, unsigned int currentValue=0, unsigned int totalToReach=0);
void printCurrentState(unsigned int done, unsigned int total, time_t ellapsed);


void printDEBUG(std::string message);

/**
 * @fn double getScalarProduct( const double *vectorA, const double  *vectorB )
 * @brief Tool procedure computing scalar product between two 3D vectors
 * @param vectorA The first vector
 * @param vectorB The second vector
 * @return The scalar product vectorA*vectorB*/
double getScalarProduct( const double *vectorA, const double *vectorB );

/**
 * @fn double getCrossProductMagn(const double  *vectorA, const double  *vectorB) 
 * @brief computes the magnitude of the cross product between two given vectors
 * @param vectorA The first vector
 * @param vectorB The second vector
 * @return The magnitude of the cross product */
double getCrossProductMagn(const double  *vectorA, const double  *vectorB);


/**
 * @fn double getNormOfVector( const double *vectorA)
 * @brief Tool procedure computing the norm of a given 3D vector
 * @param vectorA The vector to compute the norm
 * @return The vectorA norm*/
double getNormOfVector( const double *vectorA);

/**
 * @fn double getNormOfVector( const double vectorA[3])
 * @brief Tool procedure computing the norm of a given 3D vector
 * @param vectorA The vector to compute the norm
 * @return The vectorA norm*/
/*double getNormOfVector(const double vectorA[3]);*/


/**
 * @fn void computeRotationMatrix(const double * V, const double alpha, double **rotMat);
 * @brief Translation of method subroutine calcRotMat(V,alpha,rotMat) of the Fortran version
 * jklein: cylinder system
 * R o t a t i o n   &   B a c k - r o t a t i o n   o f   a   v e c t o r
 * @param V input : the vector AB defining the cross product
 * @param alpha the angle between the molecule studied and the z axis of wfn
 * @param rotMah output : the rotation matrix*/
void computeRotationMatrix(const double V[3], const double alpha, double rotMat[3][3]);

/**
 * @fn void fortranMatMul33Matrix3Vector(const double matrix[3][3], const double V[3], double VR[3])
 * @brief Specific version of Fortran MatMul for 3*3 matrix and a vector
 * @param matrix the 3*3 matrix
 * @param V the vector
 * @param VR the result vector*/
void fortranMatMul33Matrix3Vector(const double matrix[3][3], const double V[3], double VR[3]);

/**
 * @fn print_3_3_matrix(const double **matrix)
 * @brief Tool procedure for printing 3*3 matrix. For debug purpose
 * @param matrix a 3*3 double matrix*/
void print_3_3_matrix(const double matrix[3][3]);

/**
 * @fn getAtomName(const int iatom)            
 * @brief function to obtain the 2-character name associated to the IGMPLOT atom index (starting to 0)
 * @param IGMPLOT atom index (integer) 
 * @return The atom name   */
std::string getAtomName(const unsigned int iatom);

/**
 * @fn isStringInt(const std::string s)        
 * @brief function to test that a string really represents an integer 
 * @param s a string s
 * @return True if the string s is an integer */
bool isStringInt(const std::string s);

/**
 * @fn invSym33(double[3][3], double MINV[3][3]);  
 * @brief function that computes the inverse of a 3x3 symmetric matrix 
 * @param M the matrix to be inversed
 * @param MINV the inversed matrix 
 * @return false if determinant = 0 else return true */
bool invSym33(double M[3][3], double MINV[3][3]);  

/**
 * @fn modulo(int a, int b) 
 * @brief function that returns the modulo of a by b (works for negative a values)
 * @param a --> the numerator
 * @param b --> the divisor
 * @return the modulo of a by b */
int modulo(int a, int b);


/**
 * @fn distance(double[3] a, double[3] b) 
 * @brief function that returns the distance between two points a and b            
 * @param a --> the first point
 * @param b --> the second point
 * @return the distance */
double distance(double a[3], double b[3]);


/**
 * @fn getTransposed(double **M1, double M2[3][3])
 * @brief function that take the transposed matrix of M1 and put it in M2          
 * @param M1 --> the first matrix (pointer of pointer here)
 * @param M2 --> the second matrix (2D array here) */
void getTransposed(double **M1, double M2[3][3]);

/**
 * @fn getTransposed(double M1[3][3], double M2[3][3])
 * @brief function that takes the transposed matrix of M1 and put it in M2          
 * @param M1 --> the first matrix (2D array here)
 * @param M2 --> the second matrix (2D array here) */
void getTransposed(double M1[3][3], double M2[3][3]);

/**
 * @fn isNumeric(std::string const &str)
 * @brief function that takes a string as input and check it is numeric
 * @return true if only digit are present in the string */
bool isNumeric(std::string const &str);

/**
 * @fn getSelfMaxRange(double*** cube1, double*** cube2, double threshold, const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ)
 * @brief function that returns the 90e percentil of a given cube of values        
 * @param cube --> input, the cube of values
 * @param cube2--> input, the second cube of values, a 'mask' cube
 * @param threshold --> input, the threshold serving in the cube mask       
 * @param dimX --> input, number of step along direction X in the cube
 * @param dimY --> input, number of step along direction Y in the cube
 * @param dimZ --> input, number of step along direction Z in the cube
 * @return the 90e percentil */
double getSelfMaxRange(double*** cube,double*** cube2, double threshold,const unsigned int dimX, const unsigned int dimY, const unsigned int dimZ);

/** 
* @fn void merge(double* arr, double* temp, int left, int mid, int right) 
* @brief function Merging two sorted subarrays into one sorted array segment:
* @param temp is a temporary buffer used to store the merged result
* @param left:   The left subarray is [left, mid)
* @param right:  The right subarray is [mid, right) */
void merge(double* arr, double* temp, int left, int mid, int right);


/**
* @fn void parallelMergeSort(double* arr, double* temp, int left, int right, int depth = 0)
* @brief Recursively sorts the array segment [left, right) using Merge Sort,with parallelism enabled for large segments and limited recursion depth
* @param arr is the array to sort
* @param temp is a temporary array used during merging
* @param depth controls how deeply we allow parallel tasks (prevents thread explosion) */
void parallelMergeSort(double* arr, double* temp, int left, int right, int depth = 0);



#endif

