/*
 * 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 reader.cpp
 * @brief Implementation of methods declared in reader.h */

#ifndef _READER_CPP_
#define _READER_CPP_

// Local
#include <reader.h>

using namespace std;

//! Flag to indicate if we are using wfn/wfx files or not
bool wfMode=false;

//! Flag to indicate if we are using wfx files or not
bool wfxMode=false;

//! Flag to indicate if we are using wfn files or not
bool wfnMode=false;

//! Flag to indicate if we are using rkf files or not
bool rkfMode=false;

//! Flag to indicate if we are parsing rkf without grid calculation
bool parseRKF=false;

//! Flag to indicate if the Pauli exchange is to be estimated
bool pauli=false;

//! Flag to indicate if the Pauli exchange is to be estimated with atomic contributions
bool pauliatomic=false;

//! Flag to indicate if the top elf basin analysis is requested
bool topelfFlag=false;



//! Flag to indicate if we are using the IBSI mode
bool ibsiMode=false;

//! Flag to indicate if we are using the ELF mode
bool elfMode=false;

//! Flag to indicate if we are using the HIRSH  mode
bool HirshMode=false;

//! Flag to indicate if we are searching for critical points
bool CriticMode=false;

//! Flag to indicate if we are using the IGM methodology     
bool igmMode   =true;

//! Flag to indicate if the dgSCALED mode is enabled        
bool dgSCALED=false;

//! Flag to indicate if the full level of accuracy is enabled for AO calculation
bool fullaoaccMode=false;


//! Flag to indicate if we are searching for critical points
//! with medium promolecular grid
bool CriticMEDIUMMode=true; // default

//! Flag to indicate if we are searching for critical points
//! with fine promolecular grid
bool CriticFINEMode=false;

//! Flag to indicate if we are searching for critical points
//! with ultrafine promolecular grid
bool CriticULTRAFINEMode=false;

//! Flag to indicate if we add seeds at the center of triangles formed by 3 atoms
//! for critical point search (time-consumming)
bool CriticAddSeedsMode=false;


//! Flag to indicate if we the user has defined a peakfocus
bool peakFocusIntra=false;
bool peakFocusInter=false;

//! Flag to indicate if increment have been set for a cube
bool cubeIncrement = false;

// EH
//! Flag to indicate if FRAG1/FRAG2 are defined          
bool frag1Defined=false;
bool frag2Defined=false;

//EH 07/26/23
//! Flag to indicate if the user has provided the index of the ligand within the set of two supplied fragments
bool setLIGAND=false;

//! Flag to indicate if a Pauli analysis is requested at the promolecular level
bool propauli=false;

//JC: must be changer to be equivalent to the other keywords
//! The value associated to the keyword MOL2ID
//unsigned int wfnMOLD2IDvalue=0;

//JC: molecule A atoms (user defined): FRAG1 definition (a given fragment of the studied system)
//! The indexes of the atom belonging to the molecule A according to the user definition
//! Indexes start a index 1 (Natural numbering)
std::set<int> moleculeAatoms;  // QM MODE, user atom index ! (can be different from internal RKF/ADF atom index)

//JC: molecule B atoms : FRAG2 (default: atom that are not in molecule A)
//! The indexes of the atom belonging to the molecule B according to the user definition
//! Indexes start a index 1 (Natural numbering)
std::set<int> moleculeBatoms;  // QM MODE, user atom index ! (can be different from internal RKF/ADF atom index)

//JC: FRAG1 (molecule A) atoms definition (read from user configuration file)
//! The original user definition of the molecule A
std::string moleculeAatomDefinition;  // QM MODE, user atom index ! (can be different from internal RKF/ADF atom index)

//EH: FRAG2 (molecule B) atoms definition (read from user configuration file)
////! The original user definition of the molecule A
std::string moleculeBatomDefinition;  // QM MODE, user atom index ! (can be different from internal RKF/ADF atom index)
//

//! The chosen bond i.e. pair of indexes
std::vector< std::pair<unsigned int, unsigned int> > chosenBonds;


// modif Eric to handle lowercase atom names in the wfn input file :  03/01/2019
// ###################### conversionAtom ######################
int
conversionAtom(std::string &tmp)
// takes a string as input (atom name = 2 characters)
// returns an integer  = the index of the current atom (string tmp) in the atom list of IGMPlot
// in the range [0:SIZE_ATOM-1]  <-> Z-1
{
  int j,result;

  j=0;
  result=-1;
  string atomName1 = tmp;
  string atomName2;

  // transform the current atom name to uppercase and save it to atomName1
   transform(atomName1.begin(), atomName1.end(), atomName1.begin(),::toupper);

  while ( (j < SIZE_ATOM) && (result==-1) )
    {
      // copy the examined ATOM_NAME (from the list of atoms) to atomName2 and transform it to uppercase
      // to be able to compare atomName1 (input parameter) with the atomName2 from IGMPLOT atom list
      atomName2 = ATOM_NAMES[j];	
      transform(atomName2.begin(), atomName2.end(), atomName2.begin(),::toupper);

      // test if current examined atom of the list corresponds to the atom "tmp" given in parameters of this routine
      if(atomName1.compare(atomName2) == 0)
	{
	  result=j;
	}
      ++j;
    } // end of while 
  
  return result;
  
} // end of conversionAtom method ..................................................


// ################## conversionParam #####################
int
conversionParam(std::string &param)
{
  int i,result;

  // parameter param is converted to uppercase
  transform(param.begin(), param.end(), param.begin(),::toupper);

  i=0;
  result=-1;

  // JC TESTING
  //std::cout << "[ " << param << " ]" <<std::endl;
  // JC END TESTING
  
  while ( (i < SIZE_PARAM) && (result==-1) )
    {
      // JC TESTING
      //std::cout << PARAM_NAMES[i] << std::endl;
      // JC END TESTING
      
      if(param.compare(PARAM_NAMES[i]) == 0)
	{
	  result=i;
	}
      ++i;
    }

  // JC TESTING
  //std::cout << endl;
  // JC END TESTING
  
  return result;
} // end of conversionParam method ..................................................


// ################## isANumber ###########################
bool
isANumber(const std::string &s)
{
  string::const_iterator it = s.begin();
  while (it != s.end() && isdigit(*it))
    {
      ++it;
    }
  return !s.empty() && it == s.end();
} // end of isANumber method ..................................................



// ################## r e a d P a r a m ###################
void
readParam(const char* parameterFileName, param_t *params)
// params : out , object in NCISolver
{

  /* Initialization (default values) */
  params->outputName		= "mol";  // default filename prefix for every result file        
  params->outputType		= 5;    // default output (can be in the range [1-5])
  params->intermolecular 	= 1.0; // density threshold used to detect intermolecular situation 
                                                // (x% means: only points for which one molecule brings at most x% of the total
                                                // electron density will be considered)
  params->increments[0]         = 0.1; // in angstroms, grid increment in x direction
  params->increments[1]         = 0.1; // in angstroms, grid increment in y direction
  params->increments[2]         = 0.1; // in angstroms, grid increment in z direction
  params->cutoffs[0] 		= 0.4; // density cutoff: used to print nci.dat file and igm.dat file
  params->cutoffs[1] 		= 10.0; // rdg cutoff: used to print nci.dat file
  params->cutplot[0] 		= 2.5; // density cutoff to select points written in cube file (only concerns NCI)
  params->cutplot[1] 		= 0.3; // RDG isovalue to be used in nci.vmd file
  params->cutplotIGM[0]         = 1.2; // default density threshold to decide which points will be written to dgIntra cube file
  params->cutplotIGM[1]         = 0.3; // default density threshold to decide which points will be written to dgInter cube file
  params->vmdcolrangIGM[0]      = 0.3; // default ED color range that serves to vmd session to color dg_intra isosurfaces
  params->vmdcolrangIGM[1]      = 0.08; // default ED color range that serves to vmd session to color dg_inter isosurfaces
  params->numLigand		= 1;    // default file index for ligand (default = first input file specified in param.igm)
  params->radiusRoundLigand     = 3.0; // default buffer radius around ligand to define gridBox (angstroms)
  params->cubeUsed		= false; // default is to use a box around the LIGAND
  params->radiusUsed            = false; // default is to use a box around the LIGAND for PROMOL and around the system for QM
  params->cubefrag              = false; // default is to consider all atoms in the grid box
  params->coremo                = 0;     // default number of core molecular orbital excluded from BDA calculations
  params->CHARGE                = NOCHARGE;// default CHARGE for the studied system                                    
  params->cutpeakIntra[0]       = -(params->cutplotIGM[0]); // default lowerlimit for peakfocus Intra
  params->cutpeakIntra[1]       =   params->cutplotIGM[0]; // default upperlimit for peakfocus Intra
  params->cutpeakInter[0]       = -(params->cutplotIGM[1]); // default lowerlimit for peakfocus Inter
  params->cutpeakInter[1]       =   params->cutplotIGM[1]; // default upperlimit for peakfocus Inter

// Local temporary variables
double x1,y1,z1,x2,y2,z2;

  /* Array used to know which parameters were given */
  //bool paramFound[SIZE_PARAM];
  for(int i(0) ; i < SIZE_PARAM ; ++i){
    params->paramFound[i] = false;
  }
	
  /* Opening stream to read the file */
  /* NCI file is the last parameter */
  //ifstream reader(argv[argc-1]);
  ifstream reader(parameterFileName);
	
  /* If the stream couldn't be open => exit */
  if( !reader.good() )
    {
      //cerr <<  endl;	
      //cerr << "[ERROR] Could not open file " << argv[1] << endl << "[ERROR] The program will now exit." << endl;
      cerr << "[ERROR] Could not open file \"" << parameterFileName << "\"" << endl
	   << "[ERROR] The parameter file name is maybe missing (only options)" << endl
	   << "[ERROR] The program will now exit." << endl;
      exit(EXIT_FAILURE);	
    }
	
  /* Reading number of files  */
  reader >> params->nbFiles;
	
  /* Reading the file's names, starting with the first one */
  reader >> params->molAFileName;

  /* four kinds of file extension are admitted ********************/
  string xyz(".xyz"); // initialize string xyz to file extension .xyz : promolecular calculation
  string wfn(".wfn"); // ED calculated from the wave function read in a .wfn input file
  string wfx(".wfx"); // ED calculated from the wave function read in a .wfx input file
  string rkf(".rkf"); // ED calculated from the wave function read in a .rkf input file (specific to ADF program)
	
  size_t molAFileNameLength = params->molAFileName.length();

  // Check the IGMPLOT RUN: either promolecular (promolecular ED implemented in IGMPLOT) , 
  // or wavefunction mode (ED obtained from wfn or wfx file or rkf file)
  if( params->molAFileName.compare(molAFileNameLength-wfn.length(),wfn.length(),wfn) == 0 ||  
      params->molAFileName.compare(molAFileNameLength-wfx.length(),wfx.length(),wfx) == 0 ||
      params->molAFileName.compare(molAFileNameLength-rkf.length(),rkf.length(),rkf) == 0)
    {
      wfMode=true;
      //cout << "WFN mode" << endl;
    }

  // WFX
  if( params->molAFileName.compare(molAFileNameLength-wfx.length(),wfx.length(),wfx) == 0 )
    {
      wfxMode=true;
    }

  //WFN
  if( params->molAFileName.compare(molAFileNameLength-wfn.length(),wfn.length(),wfn) == 0 )
    {
      wfnMode=true;
    }

  //RKF
  if( params->molAFileName.compare(molAFileNameLength-rkf.length(),rkf.length(),rkf) == 0 )
    {
      rkfMode=true;
    }

  /* END JC WFN ***************************************************/
	
  if(params->molAFileName.substr(params->molAFileName.find_last_of(".") + 1) != "xyz")
    {
      /* JC WF ******************************************************/
      if( ! wfMode )
	{
	  cerr <<  endl;
	  cerr << "[ERROR] Expected a .xyz file, found: " << params->molAFileName << "." << endl;
	  cerr << "[ERROR] The program will now exit." << endl;
	  exit(EXIT_FAILURE);
	}
      /* END JC WFN ***************************************************/
	    
    }
  if( params->nbFiles == 2 )
  // only for an .xyz input file (promolecular with two fragments)
    {
      /* JC WFN ******************************************************/
      if(wfMode)
	{
	  cerr << endl;
	  //cerr << "[ERROR] Only one file authorized in WFN mode, please use MOL2ID to specify the beginning of the second molecule" << endl;
	  cerr << "[ERROR] Only one file authorized in WFN mode, please use FRAG1 keyword to specify the atoms belonging to the first fragment"
               << " (the remaining atoms will form the second FRAGMENT )" << endl;
	  cerr << "[ERROR] The program will now exit." << endl;
	  exit(EXIT_FAILURE);
	}
      /* END JC WFN ***************************************************/
	    
      reader >> params->molBFileName;
      if(params->molBFileName.substr(params->molBFileName.find_last_of(".") + 1) != "xyz"){
      // the second input file must be of type .xyz as well
	cerr <<  endl;
	cerr << "[ERROR] Expected a .xyz file, found: " << params->molBFileName << "." << endl;
	cerr << "[ERROR] The program will now exit." << endl;
	exit(EXIT_FAILURE);
      }
		
    }else if( params->nbFiles == 1 ){
	
    params->molBFileName = "";
			
  }else{
    cerr <<  endl;
    cerr << "[ERROR] This program can read one or two files, you asked for " << params->nbFiles << "." << endl;
    cerr << "[ERROR] The program will now exit." << endl;
    exit(EXIT_FAILURE);
		
  }
	
  /* Reading the parameters */
  string param;
  string line;

  // fill the current line with the line coming from reader input stream 
  getline(reader, line);

  // check keywords
  while( getline(reader, line) )
    {
      /* Getting parameter's type */
      // line is the line taken from input file (reader input file stream)
      // args = line converted as a stringstream
      stringstream args(line);

          
      // takes the next word from this stringstream 
      args >> param;

      // check if this keyword belongs to the IGMPLOT dictionnary
      // found = integer returned by conversionParam
      int found(conversionParam(param));
		
      /* Parameter is unknown */
      if(found < 0 || found > SIZE_PARAM)
	{
		
	  cerr <<  endl;
	  cerr << "[ERROR] Unknown KEYWORD: " << param << endl << "Possible KEYWORDS are : " << endl;
	  for(int j(0) ; j < SIZE_PARAM ; ++j)
	    {
			
	      cerr << PARAM_NAMES[j] << ", ";
			
	    }
	  cerr << endl << "[ERROR] The program will now exit." << endl;
	  exit(EXIT_FAILURE);
			
		
	}
      else // of if(found < 0 || found > SIZE_PARAM)
	{
	  /* Parameter was already seen */
	  if(params->paramFound[found])
	    {
  
	      cerr << " " << endl;	
	      cerr << "[WARNING]\tKEYWORD \"" << param << "\" followed by a blank line or " << endl;
	      cerr << "         \tfound at least twice (first value will be considered)." << endl;
		
	    }
	  else
	    { // mark this keywords as already given
	      params->paramFound[found] = true;
	      switch( found )
		{
			
		  /* Increments parameter found => reading 3 floats */
		case TYPE_INCREMENTS :
		
		  for ( int i(0) ; i < 3 ; ++i ){
							
		    if( (args >> ws).peek() != char_traits<char>::eof() )
		      {
			args >> params->increments[i];
			if( params->increments[i] <= 0.0 )
			  {
			    cerr << endl;
			    cerr << "[ERROR] Grid increment should be positive, found:" << params->increments[i] << endl;
			    cerr << "[ERROR] The program will now exit." << endl;
			    exit(EXIT_FAILURE);
			  }	
		      }
		    else
		      {
			cerr << endl;	
			cerr << "[ERROR] 3 grid increments expected, found: " << line << endl;
			cerr << "[ERROR] The program will now exit." << endl;
			exit(EXIT_FAILURE);
								
		      }
							
		  } // end of for ( int i(0) ; i < 3 ; ++i 

		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr << endl;
		      cerr << "[ERROR] 3 grid increments expected, found: " << line  << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }

                  cubeIncrement = true;

		  break;
  
		  /* Intermolecular parameter found => reading a single float */
		case TYPE_INTERMOLECULAR :

		  
		  if(isWFmodeActivated())
		    {
		      cerr << endl;
		      cerr << "[ERROR] parameter " << PARAM_NAMES[TYPE_INTERMOLECULAR] << " can not be used in quantum mode." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
		    }
		  
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      args >> params->intermolecular;
		      if( (params->intermolecular < 0.0) || (params->intermolecular > 1.0) )
			{
			  cerr << endl;
			  cerr << "[ERROR] Density fraction threshold in the range [0 - 1.0], found:" << params->intermolecular  << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
			}
		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  cerr << endl;	
			  cerr << "[ERROR] Only one value expected after keyword INTERMOLECULAR, found: " << line << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
							
			}
		    }
		  else
		    {
		      cerr << endl;	
		      cerr << "[ERROR] Value omitted after keyword " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
						
		    }
			
		  break;
	
		  /* Radius parameter found => reading four floats = sphere center + radius */
		case TYPE_RADIUS :
			
		  if(params->paramFound[TYPE_CUBE] || params->paramFound[TYPE_LIGAND])
		    {
						
		      cerr << endl;
		      cerr << "[ERROR] Only one of the following parameters can be given at a time : CUBE, LIGAND, RADIUS." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
		  for ( int i(0) ; i < 4 ; ++i )
		    {
					
		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  args >> params->radius[i];
			}
		      else
			{
		    
			  cerr << endl;	
			  cerr << "[ERROR] 4 values expected after keyword RADIUS, found:" << line << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
							
			}
							
		    } // end of for ( int i(0) ; i < 4 

                  // sphere radius must be positive !
                  if( params->radius[3] <=0.0 )
                    {
                      cerr << endl;
                      cerr << "[ERROR] Sphere RADIUS must be positive" << line << endl;
                      cerr << "        Read : " << params->radius[3] << std::endl;
                      cerr << "[ERROR] in method readParam." << std::endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);
                    }

						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr << endl;	
		      cerr << "[ERROR] 4 values expected after keyword RADIUS, found:" << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }

		  params->radiusUsed = true;
				
			
		  break;
	
		  /* Ouput name parameter found => reading a string */
		case TYPE_OUTPUT_NAME :
			
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
						
		      args >> params->outputName;
						
		    }
		  else
		    {
		      cerr << endl;               	
		      cerr << "[ERROR] ONAME value omitted, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr << endl;	
		      cerr << "[ERROR] Only one value expected after keyword ONAME, found: << " << line << " >>." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
						
		    }
			
		  break;
	
		  /* Cube parameter found => reading 6 floats representing 2 diagonal points (angstroms) */
		case TYPE_CUBE :
			
		  if(params->paramFound[TYPE_RADIUS] || params->paramFound[TYPE_LIGAND])
		    {
						
		      cerr << endl;
		      cerr << "[ERROR] Only one of the following parameters can be given at a time : CUBE, LIGAND, RADIUS." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
			
		  for ( int i(0) ; i < 6 ; ++i ){
							
		    if( (args >> ws).peek() != char_traits<char>::eof() )
		      {
							
			args >> params->cube[i];
							
		      }
		    else
		      {
			cerr << endl;	
			cerr << "[ERROR] 6 values expected after keyword CUBE, found:" << line << endl;
			cerr << "[ERROR] The program will now exit." << endl;
			exit(EXIT_FAILURE);
							
		      }

					
		  }
						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr << endl;        
		      cerr << "[ERROR] 6 values expected after keyword CUBE, found:" << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }

                  /* ========== C O N T R O L S    o n     C U B E     d e f i n i t i o n ========== */
                  /* Check if P1 != P2 (P1 and P2, the two points of the cube diagonal*/
                  if( (params->cube[0]==params->cube[3]) && 
                      (params->cube[1]==params->cube[4]) && 
                      (params->cube[2]==params->cube[5]) )
                    {
                       /* cannot build cube */
                       std::cerr << std::endl;
                       std::cerr << "[ERROR] Invalid coordinates given for cube creation; point P1 must differ from P2" << std::endl;
                       std::cerr << "The program will now exit." << std::endl;
                       exit(EXIT_FAILURE);
         
                    } // end of if( (cubeParam[0]==cubeParam[3]) && (cubeParam[1]==cubeParam[4]) && (cubeParam[2]==cubeParam[5]) )

                 /* coordinates must be different two by two between P1 and P2 */
                 /* in order to be able to define a volume */
                  if (  ( (params->cube[0]==params->cube[3]) ) or 
                        ( (params->cube[1]==params->cube[4]) ) or
                        ( (params->cube[2]==params->cube[5]) )
                     )
                    {
                      /* cannot build cube */
                      std::cerr << std::endl;
                      std::cerr << "[ERROR] Invalid coordinates given for cube creation; P1 and P2 coordinates " << std::endl;
                      std::cerr << "        must differ two by two." << std::endl;
                      std::cerr << "[ERROR] in method readParam" << std::endl;
                      std::cerr << "The program will now exit." << std::endl;
                      exit(EXIT_FAILURE);
                    }

                  /* ========= R E O R D E R   the   2   points  to design a diagonal ==== */
                 /*  ========= from bottom left --> top right                         ==== */
		  x1 = std::min(params->cube[0],params->cube[3]) ;
                  y1 = std::min(params->cube[1],params->cube[4]) ;
                  z1 = std::min(params->cube[2],params->cube[5]) ;
                  x2 = std::max(params->cube[0],params->cube[3]) ;
                  y2 = std::max(params->cube[1],params->cube[4]) ;
                  z2 = std::max(params->cube[2],params->cube[5]) ;
                  params->cube[0] = x1;
                  params->cube[1] = y1;
                  params->cube[2] = z1;
                  params->cube[3] = x2;
                  params->cube[4] = y2;
                  params->cube[5] = z2;
		    

		  params->cubeUsed = true;
			
		  break;
  
		  // Cutoffs parameter found => reading 2 floats   
		case TYPE_CUTOFFS :
			
		  for ( int i(0) ; i < 2 ; ++i )
		    {
							
		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  double temp(0.0);
			  args >> temp;
			  if(temp > 0.0){ 
			    params->cutoffs[i] = temp;
			  }
			  else
			    {
			      cerr << endl;
			      cerr << "[ERROR] Density/RDG CUTOFFs values must be positive, found: " << temp << endl;
			      cerr << "The program will now exit." << endl;
			      exit(EXIT_FAILURE);
			    }
			}
		      else
			{
			  cerr <<  endl;
			  cerr << "[ERROR] Two CUTOFFS values are expected, found: " << line  << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
							
			}
					
		    } // end of for ( int i(0) ; i < 2 ; ++i )
						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] Two CUTOFFS values are expected, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
			
		  break;
	
		  // Cutplot parameter found => reading 2 floats   
		case TYPE_CUTPLOT :
			
		  for ( int i(0) ; i < 2 ; ++i )
		    {
					
		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  double temp(0.0);
			  args >> temp;
			  if(temp > 0.0)
			    { 
			      params->cutplot[i] = temp;
			    }
			  else
			    {
			      cerr <<  endl;
			      cerr << "[ERROR] VMD CUTPLOT values must be positive, found: " << temp << endl;
			      cerr << "The program will now exit." << endl;
			      exit(EXIT_FAILURE);
			    }
			}
		      else
			{
			  cerr <<  endl;
			  cerr << "[ERROR] Two VMD CUTPLOT values are expected, found: " << line << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
							
			}
					
		    } // for ( int i(0) ; i < 2 ; ++i )

						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] No more than two VMD CUTPLOT values are expected, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
			
		  break;

		  // CutplotIGM parameter found => reading 2 floats   
		case TYPE_CUTPLOT_IGM :

		  for ( int i(0) ; i < 2 ; ++i )
		    {

		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  double temp(0.0);
			  args >> temp;
			  if(temp > 0.0)
			    {
			      params->cutplotIGM[i] = temp;
			    }
			  else
			    {
		      
			      cerr <<  endl;
			      cerr << "[ERROR] VMD CUTPLOT_IGM values must be positive, found: " << temp << endl;
			      cerr << "The program will now exit." << endl;
			      exit(EXIT_FAILURE);
			    }
			}
		      else
			{
			  cerr <<  endl;
			  cerr << "[ERROR] Two VMD CUTPLOT_IGM values are expected, found: " << line << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);

			}

		    } // for ( int i(0) ; i < 2 ; ++i )


		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;
		      cerr << "[ERROR] No more than two VMD CUTPLOT_IGM values are expected, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);

		    }

		  break;


		  // vmdcolrangIGM parameter found => reading 2 floats   
		case TYPE_VMD_COLRANG_IGM:

		  for ( int i(0) ; i < 2 ; ++i )
		    {

		      if( (args >> ws).peek() != char_traits<char>::eof() )
			{
			  double temp(0.0);
			  args >> temp;
			  if(temp > 0.0)
			    {
			      params->vmdcolrangIGM[i] = temp;
			    }
			  else
			    {
			      cerr <<  endl;
			      cerr << "[ERROR] VMD_COLRANG_IGM  values must be positive, found: " << temp << endl;
			      cerr << "The program will now exit." << endl;
			      exit(EXIT_FAILURE);
			    }
			}
		      else
			{
			  cerr <<  endl;
			  cerr << "[ERROR] Two VMD VMD_COLRANG_IGM values are expected, found: " << line << endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);

			}

		    } // for ( int i(0) ; i < 2 ; ++i )


		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;
		      cerr << "[ERROR] No more than two VMD_COLRANG_IGM values are expected, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);

		    }

		  break;



		   //Ligand parameter found => reading two numbers //
		case TYPE_LIGAND :

		  if(isWFmodeActivated())
		    {
		      cerr << endl;
		      cerr << "[ERROR] parameter " << PARAM_NAMES[TYPE_LIGAND] << " cannot be used in quantum mode." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
		    }

		  if(params->paramFound[TYPE_CUBE] || params->paramFound[TYPE_RADIUS])
		    {
						
		      cerr << endl;
		      cerr << "[ERROR] Only one of the following parameters can be given at a time : CUBE, LIGAND, RADIUS." << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
			
		  int temp; //index of the fragment selected to be encompassed by the grid (1 or 2)
		  double temp2; // the buffer distance around the selected fragment is read in ANGSTROMS !!
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      args >> temp;
		    }
		  else
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] LIGAND file index and/or buffer radius around LIGAND omitted: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      args >> temp2;
		    }
		  else
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR]  2 parameters are expected for LIGAND keyword, found: " << line <<  endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] 2 parameters are expected for LIGAND keyword, found: " << line << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
		  if(temp == 1 || temp == 2)
		    {

		      // ligand file index cannot be larger than the number of files read !
		      if (temp > params->nbFiles)
			{
			  cerr <<  endl;
			  cerr << "[ERROR]  LIGAND file index cannot be greater than the number of input" ;
                          cerr << " files, found: " << line <<  endl;
			  cerr << "[ERROR] The program will now exit." << endl;
			  exit(EXIT_FAILURE);
			}
		      //params->numLigand = temp-1;
		      params->numLigand = temp; // the index specifying the fragment be encompassed by the grid
		      if(temp2 > 0)
			{ 
			  params->radiusRoundLigand = temp2; // will be considered in Angstroms later
			}
		      else
			{
			  cerr <<  endl;
			  cerr << "[ERROR] Buffer radius around LIGAND must be positive, found: " << temp2 << endl;
			  cerr << "The program will now exit." << endl;
			  exit(EXIT_FAILURE);
			}
		    }
		  else
		    {
		      cerr <<  endl;
		      cerr << "[ERROR] LIGAND index must be in range [1-2], found : " << temp << endl;
		      cerr << "The program will now exit." << endl;
		      exit(EXIT_FAILURE);
		    }
				
                  // says that the user has supplied the index of the ligand within the set of the two fragments
                  setLIGAND=true;

		  break;
	
		  // Output parameter found => reading a single integer   
		case TYPE_OUTPUT :
					
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      args >> params->outputType;
		    }
		  else
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] OUTPUT value omitted, found: " << line  << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
		  if(params->outputType < 0 || params->outputType > 5)
		    {
		      cerr <<  endl;
		      cerr << "[ERROR] OUPUT value must be in range [0-5]:" << endl;
		      cerr << "0 : no output" << endl;
		      cerr << "1 : outputs .dat files  (nciplot, igmplot)" << endl;
		      cerr << "2 : outputs .cube files (rho, RDG)" << endl;
		      cerr << "3 : outputs .cube files (dg-inter, dg-intra)" << endl;
		      cerr << "4 : outputs .dat file (to colour atoms according to their contribution to gradient drops)" << endl;
		      cerr << "5 : outputs all files" << endl;
		      cerr << "Value found : " << params->outputType << endl;
		      cerr << "The program will now exit." << endl;
		      exit(EXIT_FAILURE);
		    }
						
		  if( (args >> ws).peek() != char_traits<char>::eof() )
		    {
		      cerr <<  endl;	
		      cerr << "[ERROR] One single value expected for OUPUT keyword, found: " << line  << endl;
		      cerr << "[ERROR] The program will now exit." << endl;
		      exit(EXIT_FAILURE);
							
		    }
				
		  break;
									
		case FRAG1:
		  //we need all the content of the line after the keyword FRAG1
		  moleculeAatomDefinition=line.substr(line.find(PARAM_NAMES[FRAG1])+PARAM_NAMES[FRAG1].length());
                  frag1Defined = true;

		  break;

                case FRAG2:
                  //we need all the content of the line after the keyword FRAG2
                  moleculeBatomDefinition=line.substr(line.find(PARAM_NAMES[FRAG2])+PARAM_NAMES[FRAG2].length());
                  frag2Defined = true;

                  break; 

                case ELF:
                   // only the ELG analysis will be performed
                  elfMode = true;
                  igmMode = false;
                  break;


                case FULLAOACC:
                  // AOs have to be computed at every node (see speed-up procedure by J. Pilme)
                  fullaoaccMode = true;
                  break;


		case IBSI:
		  {
                    ibsiMode=true;
		    // before reading the list of bond pair indexes
		    // check the existence of a possible bond cutoff
                    // ========= R A D I U S   V A L U E  ==================================
                    if( (args >> ws).peek() != char_traits<char>::eof() )
                        {
                          double temp(0.0);
                          args >> temp;
                          if(temp >= 2.0){
                            params->bondcut = temp;  // store BOND CUTOFF
                          }
                          else if ( (temp > 0) and (temp < 2.0) ){
                              cerr << endl;
                              cerr << "[ERROR] Bond environment CUTOFF value must be > 2.0 Angstroms: " << temp << endl;
                              cerr << "The program will now exit." << endl;
                              exit(EXIT_FAILURE); 
                          }
                          else
                            {
                              cerr << endl;
                              cerr << "[ERROR] Bond environment CUTOFF value must be positive, found: " << temp << endl;
                              cerr << "The program will now exit." << endl;
                              exit(EXIT_FAILURE);
                            }
                        } //end of if( (args >> ws
                     else  // no cutoff given for the bond environment ==> full IBSI calculation
                          {
                              params->bondcut = 0.0;
                          }



                     // ========= I N D E X  P A I R S  o f  a t o m s ==================================
                     while ( getline(reader, line) && removeSpaces(line).compare("ENDIBSI")!=0 )
		      {
			stringstream argsBond(line);
			
                        // lines below IBSI must be made of 2 integers
                        // ========== F I R S T    I N D E X     ========================
			if( (argsBond >> ws).peek() != char_traits<char>::eof() )
			  {
			    unsigned int atom1, atom2;
                            std::string atom1s, atom2s;
                            stringstream atom1ss, atom2ss;

                            argsBond >> atom1s;
                            if (not isStringInt(atom1s) )
                             {
                              cerr << endl;
                              cerr << "[ERROR] lines below IBSI KEYWORD must be made of 2 integers" << std::endl;
                              cerr << "        line = " << line << std::endl;
                              cerr << "[ERROR] in method readParam" <<  std::endl;
                              cerr << "The program will now exit." << std::endl;
                              exit(EXIT_FAILURE);
                             } // end of testing if it is an integer
                             else 
                             { 
                               atom1ss << atom1s;
                               atom1ss >> atom1;
                               atom1ss.str("");
                             } // store the atom1 index in atom1

                            //  ========== S E C O N D    I N D E X     ======================== 
			    if( (argsBond >> ws).peek() != char_traits<char>::eof() )
			      {
                                 argsBond >> atom2s;
                                 if (not isStringInt(atom2s) )
                                  {
                                   cerr << endl;
                                   cerr << "[ERROR] lines below IBSI KEYWORD must be made of 2 integers" << std::endl;
                                   cerr << "        line = " << line << std::endl;
                                   cerr << "[ERROR] in method readParam" <<  std::endl;
                                   cerr << "The program will now exit." << std::endl;
                                   exit(EXIT_FAILURE);
                                  } // end of testing if it is an integer

                                  else  // store the index of atom2
                                  {
                                    atom2ss << atom2s;
                                    atom2ss >> atom2; 
                                    atom2ss.str("");
                                  } 

                                    
                                  // ============ Check if atoms are different !! ============
                                  if (atom1 != atom2) 
                                   {	
                                     chosenBonds.push_back(std::pair<unsigned int,unsigned int>(atom1,atom2));
                                   }
                                  else 
                                   {
                                     cerr << endl;
                                     cerr << "[ERROR] Invalid atom pair in IBSI section, identical atoms: " 
                                          << atom1 << " - " << atom2  << endl;
                                     cerr << "The program will now exit." << endl;
                                     exit(EXIT_FAILURE);
                                   } // end of else of if (atom1 != atom2)

			    } // end of READ the second index from the stream argsBond
			} // end of READ the first  index from the stream argsBond
		      } // end of while over index pairs
		    break;
		  } // end of case IBSI:


                case HIRSH: // The atomic Hirshfeld partition will be used instead of the GBP partition of the ED gradient
                  HirshMode = true;
                  break;

                case CRITIC: // A search of the critical points will be performed 
                  CriticMode = true;
                  CriticMEDIUMMode = true;
                  CriticFINEMode = false;
                  CriticULTRAFINEMode = false;
                  igmMode = false;

                  break;

                case CRITICFINE: // A search of the critical points will be performed  with IGM promol fine grid
                  CriticMode = true;
                  CriticFINEMode = true;
                  CriticMEDIUMMode = false;
                  CriticULTRAFINEMode = false;
                  igmMode = false;

                  
                  break;

                case CRITICULTRAFINE: // A search of the critical points will be performed  with IGM promol ultra fine grid
                  CriticMode = true;
                  CriticULTRAFINEMode = true;
                  CriticMEDIUMMode = false;
                  CriticFINEMode = false;
                  igmMode = false;

                  break;

                case CRITICaddSEEDS: // A search of the critical points will be performed by using triangle centers as seeds
                                     // time-consumming
                  CriticMode = true;
                  CriticAddSeedsMode = true;
                  igmMode = false;
                  
                  break;

                case DGSCALED: // dg scaled by rho will be saved in the cubes instead of dg
                               // in that case, the Hirshmode is activated automatically
                               // since dg/rho iso-surfaces obtained with standard dg (GBP)
                               //  can be contaminated (in region of very low ED)
                  dgSCALED   = true;

                  break;

                case PARSERKF: // the RKF binary file will be parsed no grid calculation
                               // useful to read the content of an RKF binary file
                  parseRKF   = true;

                  break;

                case SELF:     // the Pauli exchange is estilmated rapidly from the diff
                               // of kinetic energy density between a real system (fermion)
                               // and that of a boson (Weizsacker formula)
                  pauli      = true;
                  igmMode = false;

                  break;

                case SELFATOMIC:     // the Pauli exchange is estilmated rapidly from the diff
                                     // of kinetic energy density between a real system (fermion)
                                     // and that of a boson (Weizsacker formula)
                  pauli      = true;
                  pauliatomic= true; // atomic contributions are calculated in addition (expensive)
                  igmMode = false;

                  break;

                case PROSELF:  // a steric effect analysis between the two fragments is performed at
                                // the promolecular level
                  propauli = true;
                  igmMode = false;
 
                  break;

                case TOPELF:  // requests a topological analysis of ELF basins through SELF function
                  topelfFlag = true;
                  elfMode    = true;
                  igmMode = false;

                  break;




                case TYPE_PEAKFOCUSINTRA:  // the user has defined a specific peak to be studied in the dg signature INTRA

                  // two values must be read defining the limit of the peak in electron density
                  for ( int i(0) ; i < 2 ; ++i )
                    {

                      if( (args >> ws).peek() != char_traits<char>::eof() )
                        {
                          double temp(0.0);
                          args >> temp;
                              params->cutpeakIntra[i] = temp;
                        }
                      else
                        {
                          cerr <<  endl;
                          cerr << "[ERROR] Two PEAKFOCUSINTRA values are expected, found: " << line << endl;
                          cerr << "[ERROR] The program will now exit." << endl;
                          exit(EXIT_FAILURE);

                        }
                    } // for ( int i(0) ; i < 2 ; ++i )


                  if( (args >> ws).peek() != char_traits<char>::eof() )
                    {
                      cerr <<  endl;
                      cerr << "[ERROR] No more than two PEAKFOCUSINTRA values are expected, found: " << line << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);

                    }

                  if( params->cutpeakIntra[0] >= params->cutpeakIntra[1])
                    {
                      cerr <<  endl;
                      cerr << "[ERROR] The lower limit of PEAKFOCUSINTRA must be lower than the upper limit" << line << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);
                    }
                  else 
                    { 
                     peakFocusIntra = true; 
                    }

                  break;

                case TYPE_PEAKFOCUSINTER:  // the user has defined a specific peak to be studied in the dg signature INTER

                  // two values must be read defining the limit of the peak in electron density
                  for ( int i(0) ; i < 2 ; ++i )
                    {

                      if( (args >> ws).peek() != char_traits<char>::eof() )
                        {
                          double temp(0.0);
                          args >> temp;
                              params->cutpeakInter[i] = temp;
                        }
                      else
                        {
                          cerr <<  endl;
                          cerr << "[ERROR] Two PEAKFOCUSINTER values are expected, found: " << line << endl;
                          cerr << "[ERROR] The program will now exit." << endl;
                          exit(EXIT_FAILURE);

                        }
                    } // for ( int i(0) ; i < 2 ; ++i )


                  if( (args >> ws).peek() != char_traits<char>::eof() )
                    {
                      cerr <<  endl;
                      cerr << "[ERROR] No more than two PEAKFOCUSINTER values are expected, found: " << line << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);

                    }

                  if( params->cutpeakInter[0] >= params->cutpeakInter[1])
                    {
                      cerr <<  endl;
                      cerr << "[ERROR] The lower limit of PEAKFOCUSINTER must be lower than the upper limit" << line << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);
                    }
                  else
                    {
                     peakFocusInter = true;
                    }

                  break;



                case CUBEFRAG:  // only atoms in the grid box will be considered in calculations
                  params->cubefrag = true;
                  break;
   
					
/* deprecated   case CHARGE:     // deprecated,  when a WFN is read (not a WFX) the CHARGe is not available.
                                 // the user must supply the CHARGE of the system in order        
                                 // to determine the exact number of core MO (in case of pseudopotential ...)
                  if( (args >> ws).peek() != char_traits<char>::eof() )
                    {

                      args >> params->CHARGE;
                      // and, specifically here, we have to set in advance the WFN netCharge
                      // since this info is not available in a WFN file
                      if (wfnMode)
                         { 
                           wf_setNetCharge(params->CHARGE);
                         }

                    }
                  else
                    {
                      cerr << endl;
                      cerr << "[ERROR] CHARGE value omitted, found: " << line << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);
                    }

                  if( (args >> ws).peek() != char_traits<char>::eof() )
                    {
                      cerr << endl;
                      cerr << "[ERROR] Only a single integer is expected after keyword CHARGE, found: << " << line << " >>." << endl;
                      cerr << "[ERROR] The program will now exit." << endl;
                      exit(EXIT_FAILURE);
                    }
                  break;
*/
  
                  
                 	
		default : 
		  cerr <<  endl;
		  cerr << "[ERROR] Unknown KEYWORD: " << param << endl;
		  cerr << "The program will now exit." << endl;
		  exit(EXIT_FAILURE);
			
		} // end of switch( found )
	    }  // end of else of if(paramFound[found])

       } // end of if(found < 0 || found > SIZE_PARAM)
    } // end of while( getline(reader, line) )
	
  /* Closing reader */
  reader.close();


} // end of readParams

/* ============   r e a d x y z M i n M a x  ============== P R O M O L E C U L A R ======== */
ProgData* 
readxyzMinMax(param_t * params){

	string atomType;
	int nbAtomsMolA(0);
	int nbAtomsMolB(0);
	double posx, posy, posz;
	ProgData *data;

	/* Opening stream to read files */
	ifstream readerOne(params->molAFileName.c_str());
	ifstream readerTwo(params->molBFileName.c_str());
	
	/* Checking correct file handling */
	if( !readerOne.good() ){
                cout << endl;
		cout << "[ERROR] Could not open the file at " << params->molAFileName.c_str() << endl 
                     << "The program will now exit." << endl;
		exit(EXIT_FAILURE);
	}
	
	if( !params->molBFileName.empty() && !readerTwo.good() ){
                cout << endl;
		cout << "[ERROR] Could not open the file at " << params->molBFileName << endl 
                     << "The program will now exit." << endl;
		exit( EXIT_FAILURE );
	}
	
	/* Gathering molecules' sizes */
	readerOne >> nbAtomsMolA;
	if( !params->molBFileName.empty() )
	  {
	    readerTwo >> nbAtomsMolB;
	  }
	
	/* Ignoring comment line */
	string line;
	std::getline(readerOne, line);
	std::getline(readerOne, line);
	/* No valid without -std=c++11
	 *readerOne.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	 *readerOne.ignore(std::numeric_limits<std::streamsize>::max(), '\n');*/
	if( !params->molBFileName.empty() )
	  {
	    std::getline(readerTwo, line);
	    std::getline(readerTwo, line);
	    /* No valid without -std=c++11
	     *readerTwo.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	     *readerTwo.ignore(std::numeric_limits<std::streamsize>::max(), '\n');*/
	  }
	
	/* Instanciating and allocating program's data */

        // creating the object data collecting all data for the calculation
        // here, 0 bonds studied (only a Promolecular teatment here !) 
	//data = new ProgData(nbAtomsMolA, nbAtomsMolB, params->numLigand, 0); // 
	// P R O M O L E C U L A R   T R E A T M E N T ......................
	data = new ProgData(nbAtomsMolA, nbAtomsMolB, nbAtomsMolA+nbAtomsMolB, params->numLigand, getChosenBonds());

	
	/* If CUBE option has been used */
	if( params->cubeUsed ){
	
		/* Setting the min/max coordinates  according to the CUBE information */
		data->setCube( params->cube );
		
	}

        /* If RADIUS option has been used */
        if( params->radiusUsed ){

                /* Setting the min/max coordinates according to the RADIUS information */
                data->setRadius( params->radius );

        }

	
	
	/* Reading first atom type from XYZ file */
	readerOne >> atomType; // atom name = string (can be either 1 or 2 characters)
	
	/* Parsing first file */
	while( !readerOne.eof() )
	  {
	    /* Reading informations about an atom from XYZ file */
	    readerOne >> posx;
	    readerOne >> posy;
	    readerOne >> posz;
	    
	    /* Adding atom to data object */
            // atomType is converted to atom index taken from IGMPLOT list
	    data->addAtom( conversionAtom(atomType), posx, posy, posz, setLIGAND); 
	    
	    /* Trying to read next atom type */
	    readerOne >> atomType;
	  }
		
	/* Reading first atom type */
	readerTwo >> atomType;
		
	/* If there is a second file */
	if( !params->molBFileName.empty() )
	  {
	    /* Parsing second file */
	    while( !readerTwo.eof() )
	      {
		/* Reading informations about an atom */
		readerTwo >> posx;
		readerTwo >> posy;
		readerTwo >> posz;
		
		/* Adding atom */
		data->addAtom( conversionAtom(atomType), posx, posy, posz, setLIGAND);
		
		/* Trying to read next atom type */
		readerTwo >> atomType;
	      }
	  }
	
	/* Closing streams */
	readerOne.close();
	readerTwo.close();

	/* Check validity of PROMOLECULAR data */
        // calculate maxCoordinates and minCoordinates for the grid
	data->validate( params->increments, params->radiusRoundLigand, setLIGAND, false); // increments are converted in bohr
	
	return data;
} // end of readxyzMinMax ........P R O M O L E C U L A R    M O D E .................


/* ==============================  isWFmodeActivated =============================================== */
bool
isWFmodeActivated()
{
  return wfMode;
}


/* ==============================  isWFXmodeActivated =============================================== */
bool
isWFXmodeActivated()
{
  return wfxMode;
}

/* ==============================  isWFNmodeActivated =============================================== */
bool
isWFNmodeActivated()
{
  return wfnMode;
}

/* ==============================  isRKFmodeActivated =============================================== */
bool
isRKFmodeActivated()
{
  return rkfMode;
}


/* ==============================  isIBSImodeActivated  ============================================ */
bool
isIBSImodeActivated()
{
  return ibsiMode;
}

/* ==============================  isELFmodeActivated  ============================================ */
bool
isELFmodeActivated()
{
  return elfMode;
}

/* ==============================  isIGMmodeActivated  ============================================ */
bool
isIGMmodeActivated()
{
  return igmMode;
}


/* ==============================  isfullAOAccActivated =========================================== */
bool   
isfullAOAccActivated()
{
  return fullaoaccMode;
}


/* ==============================  isHirshmodeActivated  ============================================ */
bool
isHirshmodeActivated()
{
  return HirshMode;
}

/* ==============================  isCriticmodeActivated  ============================================ */
bool
isCriticmodeActivated()
{
  return CriticMode;
}


/* ==============================  isdgSCALED  ============================================ */
bool
isdgSCALED()
{
  return dgSCALED;
}


/* ==============================  isParseRKF  ============================================ */
bool
isParseRKF()
{ 
  return parseRKF;
}

/* ==============================  isPauli     ============================================ */

bool isPauli()
{
  return pauli;
}

/* ==============================  isPauliAtomic ========================================== */

bool isPauliAtomic()
{
  return pauliatomic;
}


/* ==============================  isPROSELF   ============================================ */

bool isPROSELF()
{
  return propauli;
}

/* ==============================  isPROSELF   ============================================ */

bool isTOPELF()
{
  return topelfFlag;
}

/* ==============================  isPauliAtomic ========================================== */

/* ==============================  isCriticMEDIUMmodeActivated  ============================================ */
bool
isCriticMEDIUMmodeActivated()
{
  return CriticMEDIUMMode;
}

/* ==============================  isCriticFINEmodeActivated  ============================================ */
bool
isCriticFINEmodeActivated()
{
  return CriticFINEMode;
}

/* ==============================  isCriticULTRAFINEmodeActivated  ============================================ */
bool
isCriticULTRAFINEmodeActivated()
{
  return CriticULTRAFINEMode;
}

/* ==============================  isCriticAddSeedsmodeActivated  ============================================ */
bool
isCriticAddSeedsmodeActivated()
{
  return CriticAddSeedsMode;
}


/* ==============================  isPeakFocusIntraActivated  ============================================ */
bool
isPeakFocusIntraActivated()
{
  return peakFocusIntra;
}

/* ==============================  isPeakFocusInterActivated  ============================================ */
bool
isPeakFocusInterActivated()
{
  return peakFocusInter;
}


/* ==============================  analyseSetFRAG1FRAG2  =========================================== */
void
analyseSetFRAG1FRAG2(const std::string definition1, const std::string definition2, const int nbAtoms,  param_t *params)
{ // ONLY USED IN QM mode !
  // Note that definitions1 and definitions2 refer to User atom numbering ! (not necessarily RKF numbering!)
  // input: definition1: a string describing fragment1 in a molecular system (ex.: 1-3;8;12-14  --> atoms 1 to 3, 8 and 12 to 14)
  // input: definition2: a string describing fragment2 in a molecular system (ex.: 1-3;8;12-14  --> atoms 1 to 3, 8 and 12 to 14)
  // input: nbAtoms :    total number of atoms in the system
  // input: params :     a pointer to a params object (belonging to NCISolver object)
  //
  // This routines fills the moleculeAatoms and moleculeBatoms variables (set of integers = indexes of atoms)
  // !!: atom numbering returned will be in the "human" range [1:N] not [0:N-1] 
  // OUTPUT : moleculeAatoms and moleculeBatoms fields are filled with user-numbered indices
  
  /* ===============  F R A G   D E F I N I T I O N S  ============================== */
  // we consider five situations:
  // FRAG1+FRAG2 defined, FRAG1 solely defined, FRAG2 solely defined, no FRAG definition, CUBEFRAG definition
  //                 |                   |                   |                 | 
  //     CASE1       |     CASE2         |     CASE3         |   CASE4         |   CASE5
  // no FRAG defined |   FRAG1           |   FRAG2           |   FRAG1+FRAG2   |  CUBEFRAG (requires CUBE keyword as well)
  //   Frag1 = ALL   | Frag1 = FRAG1     | Frag1 = ALL-FRAG2 | Frag1 = FRAG1   | Frag1 = atoms in IGM BOX
  //   Frag2 = 0     | Frag2 = ALL-FRAG1 | Frag2 = FRAG2     | Frag2 = FRAG2   | Frag2 = 0


if (not params->cubefrag)  // the standard case: the Atom selection is explicitly given by the user (string pattern)
                           // or not and in such case all the molecule is considered
 {
     if (frag1Defined)
      {  // set FRAG1 atom indexes from definition1 string
         setFrag(definition1, FRAG1, nbAtoms);
         if (frag2Defined)  // FRAG1+FRAG2 have been defined
           { // set FRAG2 atom indexes from definition2 string
             setFrag(definition2, FRAG2, nbAtoms);                       // CASE4
           }
         else // only FRAG1 has been defined                             // CASE2
             // default frag2 = the rest of atoms (except frag1 atoms)
           { //complete the moleculeBatoms
               for(int iat=1;iat<=nbAtoms;++iat)
                 {
                   if(moleculeAatoms.find(iat)==moleculeAatoms.end()) // iat is not in the set A
                     {
                       moleculeBatoms.insert(iat);
                     }
                 } // end of loop over atoms
   
   
           } // end of else of if (frag2Defined) 
      } // end of if (frag1Defined)
      else 
        { if (frag2Defined) 
             { // only FRAG2 has been defined                            // CASE3
               setFrag(definition2, FRAG2, nbAtoms);
               // default frag1 = the rest of atoms (except frag2 atoms)
               for(int iat=1;iat<=nbAtoms;++iat)
                 {
                   if(moleculeBatoms.find(iat)==moleculeBatoms.end())
                     {
                       moleculeAatoms.insert(iat);
                     }
                 } // end of loop over atoms
             } // end of if (frag2Defined) 
          else // FRAG1 and FRAG2 have not been defined !                // CASE1 or IBSI !!
             { // default: FRAG1 = the whole molecular system
               //          FRAG2 = empty

               for(int iat=1;iat<=nbAtoms;++iat)
                 {
                       moleculeAatoms.insert(iat);
                 } // end of loop over atoms
             } // end of else of if (frag2Defined)
   
   
        } // end of else of if (frag1Defined)
   
     // check that, if FRAG1 exists, it does not represent the whole system 
     if (frag1Defined and (wfn_getMoleculeAnbAtoms()==wf_number_of_nuclei ()) )
        {
             /* cannot accept a fragment definition as large as the whole system ! */
             std::cerr << std::endl;
             std::cerr << "[ERROR] Invalid FRAG1 definition, FRAG1 has as many atoms as the whole system." << std::endl;
             std::cerr << "        in function analyseSetFRAG1FRAG2 " << std::endl;
             std::cerr << "The program will now exit." << std::endl;
             exit(EXIT_FAILURE);
        }

     // check that, if FRAG2 exists, it does not represent the whole system 
     if (frag2Defined and wfn_getMoleculeBnbAtoms()==wf_number_of_nuclei ())
        {
             /* cannot accept a fragment definition as large as the whole system ! */
             std::cerr << std::endl;
             std::cerr << "[ERROR] Invalid FRAG2 definition, FRAG2 has as many atoms as the whole system." << std::endl;
             std::cerr << "        in function analyseSetFRAG1FRAG2 " << std::endl;
             std::cerr << "The program will now exit." << std::endl;
             exit(EXIT_FAILURE);
        }

   
   
     // check that, if FRAG1 and FRAG2 exist, there is no intersection
     // between the two sets given by the user
     if (frag1Defined and frag2Defined) {
        for (int iat=1;iat<=nbAtoms;++iat)  {
           // MODIF ERIC 25 nov 2023
           //if (isInMoleculeA(iat) and isInMoleculeB(iat) ) 
             if( (moleculeAatoms.find(iat)!=moleculeAatoms.end()) // iat is in the set A
                 and
                 (moleculeBatoms.find(iat)!=moleculeBatoms.end()) // iat is in the set B
               )
              {
               /* cannot accept intersection for FRAG1 and FRAG2 definitions */
                std::cerr << std::endl;
                std::cerr << "[ERROR] Invalid FRAG1/FRAG2 definition (intersection exists for atom " 
                          << iat << ") in function analyseSetFRAG1FRAG2 " << std::endl;
                std::cerr << "The program will now exit." << std::endl;
                exit(EXIT_FAILURE);
               }
        } // end of for iat
   
   
       }  // end of if (frag1Defined and frag2Defined) 

  } // end of if (not cubefrag)

else  // cubefrag defined ==> only atoms inside the IGM cube defined by the user  // CASE5
      //                     (keyword CUBE or RADIUS)  are considered and put in FRAG1
  {  // search for atoms inside the user-defined cubic box
      /* If the cube is not NULL */
      if( params->cubeUsed)
      {
            /* atoms in the cube box are selected */
            /* cube definition has already been checked upon reading the data */

            //  get the centers information already read before
            centerData* wfnReadCenters=wf_center_data();

            /* loop over the atoms to check those belonging to the box */
           for(int iat=0;iat<nbAtoms;++iat)
              {
              // for each atom, its coordinates must be inside the box boundaries
              // to be selected in FRAG1 (FRAG2 will be empty)
              if ((wfnReadCenters[iat].coordinates[0] >= params->cube[0]/BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[1] >= params->cube[1]/BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[2] >= params->cube[2]/BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[0] <= params->cube[3]/BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[1] <= params->cube[4]/BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[2] <= params->cube[5]/BOHRTOA) )
                 {
                     moleculeAatoms.insert(iat+1);
                 } // end of if ((wfnReadCenters[i].coordinates[0] > cubeParam[0]/BOHRTOA)...
              } // end of loop over atoms
      }  // end of if( params->cube )

     else if (params->radiusUsed) // maybe the user has chosen the RADIUS KEYWORD
     {
        //  get the centers information already read before
            centerData* wfnReadCenters=wf_center_data();

        /* loop over the atoms to check those belonging to the box */
           for(int iat=0;iat<nbAtoms;++iat)
              {    
              // for each atom, its coordinates must be inside the box boundaries
              // to be selected in FRAG1 (FRAG2 will be empty)
              if ((wfnReadCenters[iat].coordinates[0] >= (params->radius[0] - params->radius[3]) / BOHRTOA) &&
                  (wfnReadCenters[iat].coordinates[1] >= (params->radius[1] - params->radius[3]) / BOHRTOA) && 
                  (wfnReadCenters[iat].coordinates[2] >= (params->radius[2] - params->radius[3]) / BOHRTOA) && 
                  (wfnReadCenters[iat].coordinates[0] <= (params->radius[0] + params->radius[3]) / BOHRTOA) && 
                  (wfnReadCenters[iat].coordinates[1] <= (params->radius[1] + params->radius[3]) / BOHRTOA) && 
                  (wfnReadCenters[iat].coordinates[2] <= (params->radius[2] + params->radius[3]) / BOHRTOA)  )
                 {    
                     moleculeAatoms.insert(iat+1);
                 } // end of if ((wfnReadCenters[i].coordinates[0] > cubeParam[0]/BOHRTOA)...
              } // end of loop over atoms
      }  // end of if( params->radiusUsed... )

      else  // there is a pb, since cubfrag is defined that requires CUBE or RADIUS to be also defined ...
     {
          std::cerr << std::endl;
          std::cerr << "[ERROR] FRAGCUBE keywords requires CUBE or RADIUS KEYWORD to be defined in param.igm input file" << std::endl;
          std::cerr << "[ERROR] in function analyseSetFRAG1FRAG2 " << std::endl;
          std::cerr << "The program will now exit." << std::endl;
          exit(EXIT_FAILURE);
     }

  } // end of else of if (not cubefrag)

} // end of analyseSetFRAG1FRAG2 ....................................................................


/* ==============================  setFrag               =========================================== */
void
setFrag(const std::string definition, const param_types_t FRAG, const int nbAtoms)
{  // ONLY in QM mode !
   // procedure that takes a USER definition string as input corresponding to atom selection patterns
   // and convert them to integers and put them in the moleculeA(orB) vector of atoms
   // !!: atom numbering returned will be in the range [1:N] not [0:N-1] AND user defined (not ADF numbered!)
   // !!: only resorts to user atomic numbering (not ADF numbering for instance)
   //
  // ascribe reference fragAtoms vector to the appropriate fragment vector
  if ( (FRAG != FRAG1) and (FRAG != FRAG2) )
    {
     /* cannot accept FRAG other than FRAG1 or FRAG2 */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid FRAG parameter passed to function setFrag in analyseSetFRAG1FRAG2 " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
    } 


  // open a stream to read the definition string 
  // remove extraspaces from definition
  std::string refinedDef = removeSpaces(definition);
  std::stringstream myStringStream(refinedDef);
  std::string interval;

  
  // loop over each field of definition (; = field separator)
  while (std::getline(myStringStream, interval, ';'))
    { 
      int value=-1;
      if(interval.length()==0)  // for instance "1;;" selection pattern                         
	{
          std::cerr << std::endl;
          std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition (empty interval or no value) in function setFrag " << std::endl;
          std::cerr << "The program will now exit." << std::endl;
          exit(EXIT_FAILURE);

	}
      else
	{ // treat the current selection (current selection is given between ; and ; : example: 1-3; or 3;)
	  size_t index=interval.find('-');
	  if(index == std::string::npos)  // npos means: no occurence of '-' in the string !
	    { // a unique atom index given (no range)
	      //It is not an interval
              // ... test if the string only contains digit numbers !!
              if (!isNumeric(interval))
                      { // range boundary contains a non-digit !
                        std::cerr << std::endl;
                        std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition. in function setFrag." << std::endl;
                        std::cerr << "[ERROR] selection contains a non-digit number: " << interval << std::endl;
                        std::cerr << "The program will now exit." << std::endl;
                        exit(EXIT_FAILURE);
                      }

              // convert current atom string to integer value (atom index)
	      value=atoi(interval.c_str());

              // check that this index is in the correct range of atom indexes
	      if(value<1 || value > nbAtoms)
		{
                  /* cannot accept FRAG1 or FRAG2 out of range */
                  std::cerr << std::endl;
                  std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition (atom index " << value << " out of 1-" 
                            << nbAtoms << " range) in function setFrag " << std::endl;
                  std::cerr << "The program will now exit." << std::endl;
                  exit(EXIT_FAILURE);
		}
	      else
		{ // add the current atom index to the FRAG selection
                  // if this atom has already been recorded in the set, an error message
                  // will be delivered
                  if (FRAG == FRAG1)
                     {
                       if(moleculeAatoms.find(value)==moleculeAatoms.end()) // iat (user numbering) is not in the set A
                         {
                           moleculeAatoms.insert(value);
        		 }
                       else // already present !! -> the user must correct the fragment definition
                         {
                            std::cerr << std::endl;
                            std::cerr << "[ERROR] Invalid FRAG1 definition" << std::endl;
                            std::cerr << "        Atom " << value << " appears several times" << std::endl;
                            std::cerr << " in function setFrag " << std::endl;
                            std::cerr << "The program will now exit." << std::endl;
                            exit(EXIT_FAILURE);
                         }
                     }
                  else  // FRAG2
                     { 
                       if(moleculeBatoms.find(value)==moleculeBatoms.end()) // iat (user numbering) is not in the set B
                         {
                           moleculeBatoms.insert(value);
                         }
                       else // already present !! -> the user must correct the fragment definition
                         {
                            std::cerr << std::endl;
                            std::cerr << "[ERROR] Invalid FRAG2 definition" << std::endl;
                            std::cerr << "        Atom " << value << " appears several times" << std::endl;
                            std::cerr << " in function setFrag " << std::endl;
                            std::cerr << "The program will now exit." << std::endl;
                            exit(EXIT_FAILURE);
                         }
	             } // end of if (FRAG == FRAG1)
	       } // end of else of if(value<1 || value > nbAtoms)
             } // end of if(index == std::string::npos)

	  else // a range of atom indexes is given using the '-' character
	    {
	      if(index==0)
		{ // interval starting with "-":  "-3;" for instance
                  std::cerr << std::endl;
                  std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition (starting index is missing to define a range, ex.:-3;)"
                            << " in function setFrag " << std::endl;
                  std::cerr << "The program will now exit." << std::endl;
                  exit(EXIT_FAILURE);

		}
	      else // interval of type: 1-3 or 1-
		{ // ==> find the starting index and ending index
                  // ... test user input error as : 1,3;  
                  std::string stringBegin = interval.substr(0,index).c_str();
                  if (!isNumeric(stringBegin)) 
                      { // range boundary contains a non-digit !
                        std::cerr << std::endl;
                        std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition. in function setFrag." << std::endl;
                        std::cerr << "[ERROR] Range bound contains a non-digit number: " << stringBegin << std::endl;
                        std::cerr << "The program will now exit." << std::endl;
                        exit(EXIT_FAILURE);
                      }

		  int begin=atoi(interval.substr(0,index).c_str());
		  int end=nbAtoms; // default for end is the total number of atoms, typically: 20-; ==> atom 20 to last atom
		  if(index != interval.length()-1) // but if a second index is given, like in: 20-30;
		    {
                      // ... test user input error as : 1-3,8;  
                      std::string stringEnd = interval.substr(index+1).c_str();
                      if (!isNumeric(stringEnd))
                          { // range boundary contains a non-digit !
                            std::cerr << std::endl;
                            std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition. in function setFrag." << std::endl;
                            std::cerr << "[ERROR] Range bound contains a non-digit number: " << stringEnd << std::endl;
                            std::cerr << "The program will now exit." << std::endl;
                            exit(EXIT_FAILURE);
                          }
                      end=atoi(interval.substr(index+1).c_str());
		    }

                  // NOW: everything has been tested: begin and end point to digits 
		  if(begin>=end)
		    {
                     std::cerr << "[ERROR] The interval bounds are not in the right order ..." << std::endl; 
                     std::cerr << "        current String: " << refinedDef << std::endl;
                     std::cerr << " in function setFrag " << std::endl;
                     std::cerr << "The program will now exit." << std::endl;
                     exit(EXIT_FAILURE);
		    } // end of  if(begin>=end)
		  else // normal case : begin < end
		    {
		      for(int value=begin;value<=end;++value)
			{
			  if(value<0 || value > nbAtoms)
			    {
                              /* cannot accept FRAG1 or FRAG2 out of range */
                              std::cerr << std::endl;
                              std::cerr << "[ERROR] Invalid FRAG1 or FRAG2 definition (atom index " << value << " out of 1-"
                                        << nbAtoms << " range) in function setFrag " << std::endl;
                              std::cerr << "The program will now exit." << std::endl;
                              exit(EXIT_FAILURE);

			    }
			  else // correct range of atoms
			    {
			      //  add the current atom index to the FRAG selection
                              if (FRAG == FRAG1)
                                 {
                                   if(moleculeAatoms.find(value)==moleculeAatoms.end()) // iat (user numbering) is not in the set A
                                     {
                                       moleculeAatoms.insert(value);
                                     }
                                   else // already present !! -> the user must correct the fragment definition
                                     {
                                        std::cerr << std::endl;
                                        std::cerr << "[ERROR] Invalid FRAG1 definition" << std::endl;
                                        std::cerr << "        Atom " << value << " appears several times" << std::endl;
                                        std::cerr << " in function setFrag " << std::endl;
                                        std::cerr << "The program will now exit." << std::endl;
                                        exit(EXIT_FAILURE);
                                     }
                                 } 
                              else  // FRAG2
                                 {
                                   if(moleculeBatoms.find(value)==moleculeBatoms.end()) // iat (user numbering) is not in the set B
                                     {
                                       moleculeBatoms.insert(value);
                                     }
                                   else // already present !! -> the user must correct the fragment definition
                                     {
                                        std::cerr << std::endl;
                                        std::cerr << "[ERROR] Invalid FRAG2 definition" << std::endl;
                                        std::cerr << "        Atom " << value << " appears several times" << std::endl;
                                        std::cerr << " in function setFrag " << std::endl;
                                        std::cerr << "The program will now exit." << std::endl;
                                        exit(EXIT_FAILURE);
                                     }
                                 } // end of if (FRAG == FRAG1)

			    } // end of else of if(value<0 || value > nbAtoms)
			  
			} // end of loop of value (atoms)
		    } // end of else if(begin>=end)
		} // end of else of if(index==0)
	    } // end of else of if(index == std::string::npos)
	} // end of if(interval.length()==0)
    } // end of while (std::getline(myStringStream, interval, ';'))

} // end of setFRAG method ..................................................................................... */

bool
isInMoleculeA(unsigned int atomNaturalIndex) // Internal atomNaturalIndex in the range[1:N] (not User index for ADF)
// QM MODE
// input : atomNaturalIndex = atom index associated with WFN/WFX or ADF (RKF) <-> wavefunction numbering 
//         (not necessarily the user numbering) but in the range [1:N]
// takes a WF_1_based atom index as input and returns true or false
{ // returns true if the atom is in user-defined FRAG1 
 // (for the IBSI treatment, no fragment is defined, so all the atoms are in FRAG1)

 // MODIF ERIC 25 nov 2023
 // remind that ADF atom numbering is different from user's numbering ! which served to define FRAGMENTS !
 unsigned int userCenter;
 unsigned int* ADF2user = wf_ADF2user_atomOrder();
 if (isRKFmodeActivated()) {
       userCenter = ADF2user[atomNaturalIndex-1];  // the atomNaturalIndexa index is going to be mapped
                                                // to the user numbering 
                                                // ADF2user array takes a 0_based number as input (within ADF ordering) !
                                                // and returns a 1_based number as output = the index of the atom 
                                                // within the user numbering
                                                // ==> userCenter is 1_based and within the user ordering
      }
      else {userCenter = atomNaturalIndex;} // in WFX and WFN, user numbering is preserved 


  return moleculeAatoms.find(userCenter)!=moleculeAatoms.end(); // moleculeAatoms is user defined
} // end of isInMoleculeA

bool
isInMoleculeB(unsigned int atomNaturalIndex) // atomNaturalIndex in the range[1:N]
// QM MODE
// input : atomNaturalIndex = atom index associated with WFN/WFX or ADF
// takes a WF_1_based atom USER index as input and returns true or false
{ // returns true if the atom is in user-defined FRAG2 
 // MODIF ERIC 25 nov 2023
 // remind that ADF atom numbering is different from user's numbering ! which served to define FRAGMENTS !
 unsigned int userCenter;
 unsigned int* ADF2user = wf_ADF2user_atomOrder();
 if (isRKFmodeActivated()) {
       userCenter = ADF2user[atomNaturalIndex-1];  // the atomNaturalIndexa index is going to be mapped
                                                // to the user numbering 
                                                // ADF2user array takes a 0_based number as input (within ADF ordering) !
                                                // and returns a 1_based number as output = the index of the atom 
                                                // within the user numbering
                                                // ==> userCenter is 1_based and within the user ordering
      }
      else {userCenter = atomNaturalIndex;} // in WFX and WFN, user numbering is preserved 

  return moleculeBatoms.find(userCenter)!=moleculeBatoms.end(); // moleculeBatoms is user defined
} // end of isInMoleculeB

std::string
getMoleculeAatomDefinition()
{
  return moleculeAatomDefinition;
}

std::string
getMoleculeBatomDefinition()
{
  return moleculeBatomDefinition;
}

unsigned int
wfn_getMoleculeAnbAtoms()
{
  return moleculeAatoms.size();
}

unsigned int
wfn_getMoleculeBnbAtoms()
{
  return moleculeBatoms.size();
}

std::string
getCondensedDescriptionOfMoleculeA()
{
  return getCondensedInformationFromNumericSet(moleculeAatoms);
}

std::string
getCondensedDescriptionOfMoleculeB()
{
  return getCondensedInformationFromNumericSet(moleculeBatoms);
}

void
howUsingIGMplot()
{
  std::cout << std::endl;
  std::cout << "[INFO ] Usage of IGMplot: ./IGMplot [--limit] parameterFilename.igm" << std::endl
	    << "[INFO ] Option(s):" << std::endl
	    << "[INFO ] \t--limit : the maximum output file size is limited to a value in MB (by default 2048MB <=> 2GB)"
	    << std::endl << std::endl;
}

std::vector< std::pair<unsigned int, unsigned int > > &
getChosenBonds()
{
  return chosenBonds;
}

bool
atomBondsHaveBeenRegistered()
{
  return (chosenBonds.size()!=0);
}

/* =============  V E R I F Y     K E Y W O R D S     C O M P A T I B I L I T Y  */
void       
checkKeywords(param_t *params)
{

 std::cout << "Checking Keywords ...     " << std::endl;
 // CUBEFRAG requires CUBE to be defined
 if (params->cubefrag and (not params->cubeUsed) and (not params->radiusUsed) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CUBEFRAG requires CUBE to be defined "  << std::endl;           
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }


 // CUBEFRAG and FRAG1 (FRAG2) are incompatible
 if (params->cubefrag and (frag1Defined or frag2Defined))
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CUBEFRAG and FRAG1 (FRAG2) are incompatible KEYWORDS "  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }


 // CUBEFRAG, FRAG1, FRAG2, IBSI, ELF cannot be used in PROMOL mode
 if ( (frag1Defined or frag2Defined or params->cubefrag or ibsiMode or elfMode) and not wfMode)  
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CUBEFRAG, FRAG1 (FRAG2), IBSI and ELF KEYWORDS cannot be used in PROMOLECULAR mode"  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

 // IBSI or ELF and (FRAG1 or FRAG2 or CUBEFRAG) are incompatible keywords
 if ( (frag1Defined or frag2Defined or params->cubefrag) and (ibsiMode or elfMode) ) 
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] IBSI and ELF are incompatible with FRAG1 or FRAG2 or CUBEFRAG KEYWORDS"  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

 // CUTOFF, CUTPLOT, INCREMENT, ... not used within the IBSI mode 
 if ( ibsiMode and ( params->paramFound[TYPE_INCREMENTS] 
                  or params->paramFound[TYPE_CUBE] 
                  or params->paramFound[TYPE_CUTOFFS]
                  or params->paramFound[TYPE_CUTPLOT]
                  or params->paramFound[TYPE_CUTPLOT_IGM]
                  or params->paramFound[TYPE_VMD_COLRANG_IGM]
                  or params->paramFound[TYPE_OUTPUT] ) )
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] IBSI uses a cylindrical grid and does not generate cube outputs."  << std::endl;
      std::cerr << "          Hence, KEYWORDS INCREMENTS, CUBE, CUTOFFS, CUTPLOT, "  << std::endl;
      std::cerr << "          INCREMENTS, CUBE, CUTOFFS, CUTPLOT, CUTPLOT_IGM, VMD_COLRANG_IGM, OUTPUT " << std::endl;
      std::cerr << "          are not used." << std::endl;
      std::cerr << "[WARNING] in reading parameters" << std::endl;
   }

 // IBSI, PEAKFOCUSINTRA, PEAKFOCUSINTER, VMD_COLRANGE_IGM, HIRSH, CRITIC, CRITICFINE, CRITICULTRAFINE,CRITICADDSEEDS
 // not used with ELF mode 
 if ( elfMode  and ( params->paramFound[TYPE_PEAKFOCUSINTRA]
                  or params->paramFound[TYPE_PEAKFOCUSINTER]
                  or params->paramFound[TYPE_VMD_COLRANG_IGM]
                  or CriticMode ) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR]   IBSI, PEAKFOCUSINTRA, PEAKFOCUSINTER, VMD_COLRANGE_IGM, HIRSH," << std::endl;
      std::cerr << "          CRITIC, CRITICFINE, CRITICULTRAFINE,CRITICADDSEEDS keywords cannot be used in ELF mode" << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);

   }

  // PARSERKF cannot be used with any other keyword
  if (isParseRKF()) {
     for(int i(0) ; i < SIZE_PARAM ; ++i){
       if (i != PARSERKF) {
              if (params->paramFound[i] == true) 
              { 
               std::cerr << std::endl;
               std::cerr << "[ERROR]   PARSERKF keyword can only be used alone.                      " << std::endl;
               std::cerr << "[ERROR]   Found another keyword.                                                                " << std::endl;
               std::cerr << "[ERROR]   in reading parameters" << std::endl;
               std::cerr << "The program will now exit." << std::endl;
               exit(EXIT_FAILURE);
              } // end of if (params->paramFound[i] == true)
          } // end of if (i != PARSERKF) 
     } // end of for
  } // end of if (isParseRKF()) 


  // CHARGE must only be used in IBSI mode
/* if (  (isWFmodeActivated() and not ibsiMode) and params->paramFound[CHARGE] ) 
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] CHARGE keyword is only employed within the IBSI mode,"  << std::endl;
      std::cerr << "          for PDA calculations. CHARGE will be ignored here." << std::endl;
      std::cerr << "[WARNING]  reading parameters" << std::endl;
      params->CHARGE = NOCHARGE;
   }


  // CHARGE can only be used in QM  mode
   if (  (not isWFmodeActivated()) and params->paramFound[CHARGE] )
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] CHARGE keyword is not employed within the promolecular mode,"  << std::endl;
      std::cerr << "          CHARGE will be ignored here." << std::endl;
      std::cerr << "[WARNING]  reading parameters" << std::endl; 
      params->CHARGE = NOCHARGE;
   }


  // CHARGE MUST be used in WFN mode when IBSI is required
   if (          isWFNmodeActivated()
            and (ibsiMode and not params->paramFound[CHARGE])
      )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] When a WFN input file is supplied, the "  << std::endl;
      std::cerr << "        CHARGE keyword is required within the IBSI mode."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

  // CHARGE only needs to be employed with WFN, not WFX ...
   if (  (isWFXmodeActivated()) and params->paramFound[CHARGE] )
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] CHARGE keyword is only required when WFN file is supplied,  "  << std::endl;
      std::cerr << "          since the WFX already provides Net Charge for the system.   "  << std::endl;
      std::cerr << "          CHARGE will be ignored here." << std::endl;
      std::cerr << "[WARNING] reading parameters" << std::endl;
   }
*/

  // HIRSH option is only available for QM treatment ...
   if (  HirshMode and (not wfMode) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] HIRSH KEYWORD cannot be used in PROMOLECULAR mode."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

  // HIRSH option is not used for IBSI treatment ...
   if (  HirshMode and ibsiMode )    
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] IBSI calculation does not use the Hirshfeld partition of the ED gradient"  << std::endl;
      std::cerr << "          but rather the GBP partition. HIRSH keyword will be ignored."  << std::endl;
      std::cerr << "[WARNING] in reading parameters" << std::endl;
   }

  // HIRSH option is not used for CRITIC treatment ...
   if (  HirshMode and CriticMode )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CRITIC and HIRSH keywords cannot be used simultaneously"  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl; 
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);

   }

  // HIRSH option is not used for ELF                   
//   if (  HirshMode and elfMode)
//   {
//      std::cerr << std::endl;
//      std::cerr << "[ERROR] ELF and HIRSH keywords cannot be used simultaneously"  << std::endl;
//      std::cerr << "[ERROR] in reading parameters" << std::endl;
//      std::cerr << "The program will now exit." << std::endl;
//      exit(EXIT_FAILURE);
//   }


  // PEAKFOCUS and IBSI are not compatible
  if (  (peakFocusIntra or peakFocusInter) and ibsiMode )
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] PEAKFOCUS keyword cannot be used in conjunction with IBSI keyword."  << std::endl;
      std::cerr << "          PEAKFOCUS will be ignored here." << std::endl;
      std::cerr << "[WARNING] in reading parameters" << std::endl;
   }

  // PEAKFOCUS and CUTPLOT_IGM are not compatible
  if (  (peakFocusIntra or peakFocusInter) and params->paramFound[TYPE_CUTPLOT_IGM] )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CUTPLOT_IGM keyword cannot be used in conjunction with PEAKFOCUSINTRA or PEAKFOCUSINTER keyword."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

  // PEAKFOCUS-->INTER<-- requires a fragment be defined
  if (peakFocusInter) {
    if (    (    isWFmodeActivated()       and (not (frag1Defined or frag2Defined or params->cubefrag)) )
       or
            (    (not isWFmodeActivated()) and (params->nbFiles == 1) )// nbfiles==2 is a criterion to detect promol with two fragments
       )
       {
          std::cerr << std::endl;
          std::cerr << "[ERROR] PEAKFOCUSINTER requires a fragment be defined by keyword FRAG1 or FRAG2 or CUBEFRAG in QM mode"  << std::endl;
          std::cerr << "        or by supplying two .xyz input files in PROMOLECULAR mode." << std::endl;
          std::cerr << "[ERROR] in reading parameters" << std::endl;
          std::cerr << "The program will now exit." << std::endl;
          exit(EXIT_FAILURE);
       }
  } // end of if (peakFocusInter)

 // CRITIC and : FRAG1 or FRAG2 or CUBEFRAG or IBSI or DGSCALED or SELF of ELF or promolecular are incompatible keywords
 if ( (frag1Defined or frag2Defined or params->cubefrag 
                    or ibsiMode or isdgSCALED() or isPauli() or elfMode
                    or (not isWFmodeActivated()) ) and CriticMode)
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] CRITIC is incompatible with FRAG1 or FRAG2 or CUBEFRAG KEYWORDS"  << std::endl;
      std::cerr << "        or IBSI calculations or promolecular mode or ELF or SELF KEYWORDS"  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

 // CRITIC uses its own increments steps 
 if ( params->paramFound[TYPE_INCREMENTS] and CriticMode)
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] CRITICal point search uses its own grid INCREMENTS values"  << std::endl;
      std::cerr << "          Supplied INCREMENTS will be ignored"  << std::endl;
      std::cerr << "[WARNING] in reading parameters" << std::endl;
   }


 // CRITIC or CRITICFINE or CRITICULTRAFINE only should be used  
 if ( (params->paramFound[CRITIC] and params->paramFound[CRITICFINE])      or
      (params->paramFound[CRITIC] and params->paramFound[CRITICULTRAFINE]) or
      (params->paramFound[CRITICFINE] and params->paramFound[CRITICULTRAFINE])
    )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] a single keyword CRITIC or CRITICFINE or CRITICULTRAFINE must be used"  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }
 // OUTPUT type 4 DEPRECATED
 if (params->outputType == 4) 
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] OUTPUT type 4 deprecated"  << std::endl;
      std::cerr << "[WARNING] OUTPUT type set to 5" << std::endl;
      params->outputType            = 5;
   }

// DGSCALED option only allowed for QM calculations 
if ( isdgSCALED() and (not isWFmodeActivated()) )           
{
      std::cerr << std::endl;
      std::cerr << "[ERROR] DGSCALED option not available for the promolecular mode."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
} // end of isdgSCALED() and (not isWFmodeActivated()) ....

 // DGSCALED and: IBSI or SELF of ELF or CRITIC or promolecular are incompatible keywords
 if ( (ibsiMode or isPauli() or elfMode or CriticMode or (not isWFmodeActivated()) ) and
       isdgSCALED() )
   {  
      std::cerr << std::endl;
      std::cerr << "[ERROR] DGSCALED is incompatible with IBSI or SELF or ELF or CRITIC"  << std::endl;
      std::cerr << "        or promolecular mode "  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

if ( isPauli() and ( params->cubefrag or ibsiMode or (not wfMode) or CriticMode 
                     or peakFocusIntra or peakFocusInter 
                     or elfMode or isParseRKF() or isdgSCALED()
                   )
   )
{
      std::cerr << std::endl;
      std::cerr << "[ERROR] Pauli estimation incompatible with keywords CUBEFRAG   "  << std::endl;
      std::cerr << "[ERROR] IBSI, PEAKFOCUSINTRA, PEAKFOCUSINTER, CRITIC, ELF" << std::endl;
      std::cerr << "[ERROR] PARSERKF, DGSCALED or promolecular mode."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
} // end of if ( isPauli() and ...

// PAULI requests at least one fragment be defined
if ( isPauli() and (not (frag1Defined or frag2Defined ) ) )
{     
      std::cerr << std::endl;
      std::cerr << "[ERROR] Pauli exchange estimation requires FRAG1 or FRAG2 being defined."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
} // end of isPauli() and ( ... ...........................



// OUTPUT not used within the PAULI mode 
 if ( isPauli() and ( //params->paramFound[TYPE_CUBE]
                     params->paramFound[TYPE_CUTOFFS]
                  or params->paramFound[TYPE_CUTPLOT]
                  or params->paramFound[TYPE_CUTPLOT_IGM]
                  or params->paramFound[TYPE_VMD_COLRANG_IGM]
                  or params->paramFound[TYPE_OUTPUT] ) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] KEYWORDS SELF and CUTOFFS, CUTPLOT, "  << std::endl;
      std::cerr << "        CUTPLOT_IGM, VMD_COLRANG_IGM, OUTPUT " << std::endl;
      std::cerr << "        cannot be used together." << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

// KEYWORD SELF should not be specified  together with SELFATOMIC 
 if ( params->paramFound[SELF]  and  params->paramFound[SELFATOMIC]  )
   {
      std::cerr << std::endl;
      std::cerr << "[WARNING] KEYWORD SELF should not be specified    "  << std::endl;
      std::cerr << "          together with SELFATOMIC          " << std::endl;
      std::cerr << "[WARNING] in reading parameters" << std::endl;
   }

// PROSELF cannot be used within QM mode         
 if ( isPROSELF() and ( wfMode) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] KEYWORDS ¨PROSELF cannot be used in QM mode."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

// PROSELF requests 2 fragments to be defined    
 if ( isPROSELF() and ( params->nbFiles != 2) )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] KEYWORDS ¨PROSELF analysis requests 2 xyz files to be read."  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }

// PROSELF incompatible with following keywords  
 if ( isPROSELF() and ( (frag1Defined or frag2Defined ) or
                        ibsiMode or CriticMode or
                        peakFocusIntra or peakFocusInter or
                        elfMode or isParseRKF() or isdgSCALED() or
                        isPauli() or isPauliAtomic() or isfullAOAccActivated()
                      )
    )
   {
      std::cerr << std::endl;
      std::cerr << "[ERROR] KEYWORDS ¨PROSELF incompatible with keywords FRAG1 or FRAG2"  << std::endl;
      std::cerr << "[ERROR] or IBSI or CRITIC or PEAKFOCUSINTRA or PEAKFOCUSINTER   "  << std::endl;
      std::cerr << "[ERROR] or ELF or PARSERKF or DGSCALED or SELF or SELFATOMIC "  << std::endl;
      std::cerr << "[ERROR] or FULLAOACC. "  << std::endl;
      std::cerr << "[ERROR] in reading parameters" << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
   }



} // end of checkKeywords .................................



#endif
