/*
 * 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 ProgData.cpp
 * @brief Implementation of ProgData description (from ProgData.h)
 * @warning No copy constructor defined, careful when trying to pass this by value
 * @author Emmanuel */

#ifndef _PROGDATA_CPP_
#define _PROGDATA_CPP_

// LOCAL
#include <ProgData.h>


//ProgData::ProgData(int nbAtomMolAParam, int nbAtomMolBParam, int numLigandParam, int nbBondParam) : nbAtomMolA(nbAtomMolAParam), nbAtomMolB(nbAtomMolBParam), numLigand(numLigandParam), nbAtom(nbAtomMolAParam + nbAtomMolBParam), nbBond(nbBondParam)
ProgData::ProgData(int nbAtomMolAParam, int nbAtomMolBParam, int nbTotalAtomParam, int numLigandParam, std::vector<std::pair< unsigned int,  unsigned int> > &chosenBondsParam ) : nbAtomMolA(nbAtomMolAParam), nbAtomMolB(nbAtomMolBParam), numLigand(numLigandParam), nbAtom(nbTotalAtomParam)                  


{

  /* Allocating memory */
  atomTypes 		= new int[nbAtom];  // Z-1 for each atom  in the range [0:NbAtoms-1]
  atomPositions.xValues = new double[nbAtom];	
  atomPositions.yValues = new double[nbAtom];	
  atomPositions.zValues = new double[nbAtom];

 /* Bond features (in QM treatment), associated with the chosenBond vector of reader.cpp */
  nbBond                = chosenBondsParam.size();

  bonds=NULL;

  if(nbBond != 0)
    {
      bonds                 = new bond_t[nbBond];
      
      // initialize the bonds array
      for(unsigned int ibond=0;ibond<nbBond;++ibond){
	bonds[ibond].atomPair = chosenBondsParam[ibond];
	bonds[ibond].length=0.0;
	bonds[ibond].ibsi=0.0;
	bonds[ibond].baf=0.0;
        bonds[ibond].ABx=0.0;
        bonds[ibond].ABy=0.0;
        bonds[ibond].ABz=0.0;
 
      } // end of for ibond=0 

    }

 
  /* Cursor instanciation */
  cursor 			= 0;  // atom index
  cnt				= 0;
  boxSet 			= false;
  radiusSet                     = false;

}

ProgData::~ProgData()
{
  /* Deallocating memory */
  delete [] atomTypes;
  //delete [] atomPositions;
  delete [] atomPositions.xValues;
  delete [] atomPositions.yValues;
  delete [] atomPositions.zValues;

  if(bonds!=NULL)
    {
      // delete bonds
      delete [] bonds;
    }
}

void
ProgData::addAtom(int type, double posx, double posy, double posz, bool setLIGAND, bool WFNmode, bool HirshMode)
{
		 
 // input: type        = integer = Z-1 = index of the atom according to the internal IGMPLOT list 
 //                                in the range [0:SIZE_ATOM-1]  <=> Z-1
 // input: posx,y,z    = atom cartesian coordinates in angstroms
 // input:  WFNmode    = says if QM or Promolecular mode
 // input:  Hirsh      = says if a Hirshfeld partition is requested within a QM job
		  
  if( cursor < nbAtom )  // cursor is the index on the current examined atom in the atom list 
                         // in the range[0:nbAtoms-1]
    {
      /* Setting type */
      atomTypes[cursor] = type; // type = Z-1, cursor in the range [0:NbAtoms-1]
      if(type < 0)
	{
	  std::cerr << std::endl;
	  std::cerr << "[ERROR] Unknown atom "<< std::endl << "[ERROR] The program will now exit." << std::endl;
	  exit(EXIT_FAILURE);
				
	}
      else
	{ // CHECK the Z limit for a promolecular job
	  // type is > 0 so don't worry: the atom is known, but for promolecular mode, only the 36 first atoms are parameterized ...
	  if (! WFNmode) // PROMOLECULAR job
             {
          	  if( (((unsigned int)type) > NB_KNW_ATMS)  or
                      // for now, only Te(Z=52),I(Z=53) and Xe(Z=54) are available on row 5
                      // take care, type in the range [0....n]= Z-1  if Z>36 and Z <52:
                      ( ( ((unsigned int)type) > 35) &&  ( ((unsigned int)type) < 51) )
                    )
          	    {
          				    
          	      std::cerr << std::endl;	
          	      std::cerr << "[ERROR] Atom valid but no handled by this promolecular version, Z="<< type+1 << "." << std::endl;
          	      std::cerr << "[INFO!] Only the atoms of rows 1,2,3,4 and p-block elements Te, I, Xe of row 5 are allowed." << std::endl;
          	      std::cerr << "[ERROR] The program will now exit." << std::endl;
          	      exit(EXIT_FAILURE);			
          	    }
      				
             }  // end of if (! WFNmode)

          // CHECK the Z limit for a QM job using the Hirshfeld partition
          if (WFNmode and HirshMode)
             {    // only 1st, 2nd and 3d row atoms are allowed
                  // since the promolecular ED is correctly fitted for these atoms
                  if( ((unsigned int)type) > HIRSHAtomLimit) 
                    {

                      std::cerr << std::endl;
                      std::cerr << "[ERROR] Atom valid but no handled for the Hirshfeld partition, Z="<< type+1 << "." << std::endl;
                      std::cerr << "[INFO!] Only the atoms of rows 1,2,3 are allowed in this release." << std::endl;
                      std::cerr << "[ERROR] The program will now exit." << std::endl;
                      exit(EXIT_FAILURE);
                    }

             }  // end of if (WFNmode and HirshMode)
            

        }
      // store the cartesian coordinate of current atom
      // indexed in the range [0:nb atoms-1]
      atomPositions.xValues[cursor] = posx;
      atomPositions.yValues[cursor] = posy;
      atomPositions.zValues[cursor] = posz;

      //JC: with WFN files, everything is already in BOHR, but for promolecular input files, XYZ are given in angstroms
      //       P R O M O L E C U L A R :  conversion angstroms ---> bohr
      if( ! WFNmode )
	{
	  atomPositions.xValues[cursor] /= BOHRTOA;
	  atomPositions.yValues[cursor] /= BOHRTOA;
	  atomPositions.zValues[cursor] /= BOHRTOA;
	}

      //std::cout << "boxSet = " << (boxSet?"TRUE":"FALSE") << std::endl;
      //std::cout << "radiusSet = " << (radiusSet?"TRUE":"FALSE") << std::endl;
      
      /* If CUBE and RADIUS options have not been used */
      /* then, coordinates read must be used to determine the NCI/IGM grid box */
      if(!boxSet && !radiusSet)
	{
	  //std::cout << "WFNmode = " << (WFNmode?"TRUE":"FALSE") << std::endl;
	  if( WFNmode)  // QUANTUM TREATMENT   =============================
	    {
	       /* First atom addition in the list of atoms */
	      if( cursor == 0 )
		{
		  /* Setting the first addition as max coordinates for all axis */
		  maxCoordinates[0] = atomPositions.xValues[cursor];
		  maxCoordinates[1] = atomPositions.yValues[cursor];
		  maxCoordinates[2] = atomPositions.zValues[cursor];
		  
		  /* Setting the first addition as min coordinates for all axis */
		  minCoordinates[0] = atomPositions.xValues[cursor];
		  minCoordinates[1] = atomPositions.yValues[cursor];
		  minCoordinates[2] = atomPositions.zValues[cursor];
		  
		  /* Addition other than the first */
		}
	      else // the current atom is not the first added to the list
		{
		  /* Updating max coordinates */
		  if( maxCoordinates[0] < atomPositions.xValues[cursor] )
		    maxCoordinates[0] = atomPositions.xValues[cursor];
		  if( maxCoordinates[1] < atomPositions.yValues[cursor] )
		    maxCoordinates[1] = atomPositions.yValues[cursor];
		  if( maxCoordinates[2] < atomPositions.zValues[cursor] )
		    maxCoordinates[2] = atomPositions.zValues[cursor];
		  
		  /* Updating min coordinates */
		  if( minCoordinates[0] > atomPositions.xValues[cursor] )
		    minCoordinates[0] = atomPositions.xValues[cursor];
		  if( minCoordinates[1] > atomPositions.yValues[cursor] )
		    minCoordinates[1] = atomPositions.yValues[cursor];
		  if( minCoordinates[2] > atomPositions.zValues[cursor] )
		    minCoordinates[2] = atomPositions.zValues[cursor];
		}
					
	    } // end of WFNMode  ...........................................
	  
	  else  // PROMOLECULAR Mode ========================================
	    {

              if (setLIGAND) { // PROMOLECULAR MODE with LIGAND keyword
                   // if the LIGAND KEYWORD has been explicitely employed by the user then
                    // the grid will encompass either FRAG1.xyz or FRAG2.xyz depending on the LIGAND keyword value    
         	    // Note that if only one fragment is provided by the user (mol.xyz)
                    // then the grid will encompass the whole fragment
         	    // ELSE if FRAG1.xyz or FRAG2.xyz have been provided and if LIGAND KEYWORD not set
                    //  the grid will be set to encompass the smallest fragment 
         	    // EH: numLigand should never be 0 in the new release
                    // default is 1, or 2 if two .xyz files have been provided in promolecular mode
                    // numLigand indicates the INDEX of the 'ligand' molecule (1 or 2)
         	    if( ( numLigand == 1 && cursor < nbAtomMolA ) || ( numLigand == 2 && cursor >= nbAtomMolA ) )
         		{
         		  
         		  /* First addition */
         		  //JC 2.5.3 not only when cursor == 0 but also when cursor == nbAtomMolA for numLigand == 2
         		//if( ( (numLigand == 0 || numLigand == 1) && cursor == 0 ) || (numLigand == 2 && cursor == nbAtomMolA))
                           if( ( (numLigand == 1) && (cursor == 0) ) || ( (numLigand == 2) && (cursor == nbAtomMolA) ) )
         		    {
         		      
                              /* Setting the first addition as max coordinates for all axis */
         		      maxCoordinates[0] = atomPositions.xValues[cursor];
         		      maxCoordinates[1] = atomPositions.yValues[cursor];
         		      maxCoordinates[2] = atomPositions.zValues[cursor];
         		      
         		      /* Setting the first addition as min coordinates for all axis */
         		      minCoordinates[0] = atomPositions.xValues[cursor];
         		      minCoordinates[1] = atomPositions.yValues[cursor];
         		      minCoordinates[2] = atomPositions.zValues[cursor];
         		      
         		      
         		      /* Addition other than the first */
         		    }
         		  else
         		    {
         		      
         		      /* Updating max coordinates */
         		      if( maxCoordinates[0] < atomPositions.xValues[cursor] )
         			maxCoordinates[0] = atomPositions.xValues[cursor];
         		      if( maxCoordinates[1] < atomPositions.yValues[cursor] )
         			maxCoordinates[1] = atomPositions.yValues[cursor];
         		      if( maxCoordinates[2] < atomPositions.zValues[cursor] )
         			maxCoordinates[2] = atomPositions.zValues[cursor];
         		      
         		      /* Updating min coordinates */
         		      if( minCoordinates[0] > atomPositions.xValues[cursor] )
         			minCoordinates[0] = atomPositions.xValues[cursor];
         		      if( minCoordinates[1] > atomPositions.yValues[cursor] )
         			minCoordinates[1] = atomPositions.yValues[cursor];
         		      if( minCoordinates[2] > atomPositions.zValues[cursor] )
         			minCoordinates[2] = atomPositions.zValues[cursor];

         		    } // end of else of if( cursor == 0 )
         		  
         		} // end of  if( numLigand == 0 || ( numLigand == 1 && cursor < nbAtomMolA ) 
                           // || ( numLigand == 2 && cursor > nbAtomMolA ) )
               } // END of if (setLIGAND)
	       else // ELSE, LIGAND KW not set (and numLigand=1, default value) : 
                    // ==>  the grid will be set to encompass the smallest fragment
               {
                 if( cursor == 0 ) { // FRAG1.xyz 
                         /* Setting the first addition as max coordinates for all axis */
                         maxCoordinatesA[0] = atomPositions.xValues[cursor];
                         maxCoordinatesA[1] = atomPositions.yValues[cursor];
                         maxCoordinatesA[2] = atomPositions.zValues[cursor];

                         /* Setting the first addition as min coordinates for all axis */
                         minCoordinatesA[0] = atomPositions.xValues[cursor];
                         minCoordinatesA[1] = atomPositions.yValues[cursor];
                         minCoordinatesA[2] = atomPositions.zValues[cursor];
                 } // end of if( cursor == 0 )                            

                 if( cursor == nbAtomMolA ) { // FRAG2.xyz (two fragments are defined)
                         /* Setting the first addition as max coordinates for all axis */
                         maxCoordinatesB[0] = atomPositions.xValues[cursor];
                         maxCoordinatesB[1] = atomPositions.yValues[cursor];
                         maxCoordinatesB[2] = atomPositions.zValues[cursor];

                         /* Setting the first addition as min coordinates for all axis */
                         minCoordinatesB[0] = atomPositions.xValues[cursor];
                         minCoordinatesB[1] = atomPositions.yValues[cursor];
                         minCoordinatesB[2] = atomPositions.zValues[cursor];
                 } // end of if( cursor == nbAtomMolA )                   

                if ((cursor>0) && (cursor<nbAtomMolA)) { // FRAGMENT A
                          /* Updating max coordinates */
                          if( maxCoordinatesA[0] < atomPositions.xValues[cursor] )
                            maxCoordinatesA[0] = atomPositions.xValues[cursor];
                          if( maxCoordinatesA[1] < atomPositions.yValues[cursor] )
                            maxCoordinatesA[1] = atomPositions.yValues[cursor];
                          if( maxCoordinatesA[2] < atomPositions.zValues[cursor] )
                            maxCoordinatesA[2] = atomPositions.zValues[cursor];

                          /* Updating min coordinates */
                          if( minCoordinatesA[0] > atomPositions.xValues[cursor] )
                            minCoordinatesA[0] = atomPositions.xValues[cursor];
                          if( minCoordinatesA[1] > atomPositions.yValues[cursor] )
                            minCoordinatesA[1] = atomPositions.yValues[cursor];
                          if( minCoordinatesA[2] > atomPositions.zValues[cursor] )
                            minCoordinatesA[2] = atomPositions.zValues[cursor];

                }  // end of if ((cursor>0) && (cursor<nbAtomMolA))

                if ( (cursor>nbAtomMolA)  && (cursor<nbAtom) ) { // FRAGMENT B, 2 fragments have been defined
                          /* Updating max coordinates */
                          if( maxCoordinatesB[0] < atomPositions.xValues[cursor] )
                            maxCoordinatesB[0] = atomPositions.xValues[cursor];
                          if( maxCoordinatesB[1] < atomPositions.yValues[cursor] )
                            maxCoordinatesB[1] = atomPositions.yValues[cursor];
                          if( maxCoordinatesB[2] < atomPositions.zValues[cursor] )
                            maxCoordinatesB[2] = atomPositions.zValues[cursor];

                          /* Updating min coordinates */
                          if( minCoordinatesB[0] > atomPositions.xValues[cursor] )
                            minCoordinatesB[0] = atomPositions.xValues[cursor];
                          if( minCoordinatesB[1] > atomPositions.yValues[cursor] )
                            minCoordinatesB[1] = atomPositions.yValues[cursor];
                          if( minCoordinatesB[2] > atomPositions.zValues[cursor] )
                            minCoordinatesB[2] = atomPositions.zValues[cursor];

                }  // end of if ( (cursor>nbAtomMolA)  && (cursor<nbAtom)

              } // end of else of if (setLIGAND)

	    } // END OF PROMOLECULAR MODE .....................................


	} // end of if(!boxSet && !radiusSet)
			    
      /* Updating cursor */
      ++cursor;
			    
    } // end of if( cursor < nbAtom ) 
  else
    {
				
      std::cerr << std::endl;
      std::cerr << "[ERROR] More atoms than expected! ( " << cursor << " / " << nbAtom << " )" << std::endl;
      std::cerr << "[ERROR] The program will now exit." << std::endl;
      exit(EXIT_FAILURE);	
			    
    }

} // end of addAtom method .....................................................
		
void
ProgData::validate(double * increments, double buffer, bool setLIGAND, bool QMmode)
{
   // routine that can be called either from WFN/WFX mode (NCISolver.cpp)
   // or promolecular mode (in reader.cpp)
    
  // input --> increments supplied in angstroms 
  // input --> buffer around the ligand in angstroms 
  // input --> setLIGAND = true if the user has used the LIGAND keywrd to specify the ligand file
  // input --> WFNmode    = true if the QUANTUM mode is enabled

  // method only called for a cubic grid

  /* convert increment from angstroms to Bohr */
  increments[0] = increments[0] / BOHRTOA;
  increments[1] = increments[1] / BOHRTOA;
  increments[2] = increments[2] / BOHRTOA;

  /* If CUBE and RADIUS have not been used */
  if( !boxSet && !radiusSet)
    {
      double value=buffer;

      // If a ligand has been provided by the user together with the buffer value in angstroms //
      // then this buffer value has to be converted to bohrs

      //if( numLigand > 0) {
  	  // EH 07/26/2023: The buffer value has always to be converted in Bohrs
  	  // regardless of the default value of 3 angstroms or the value passed
  	  // by the user through keyword LIGAND
      value/=BOHRTOA;
//	}

      if ( ! QMmode)  { // PROMOLECULAR MODE  ........................................
          if (not setLIGAND) { // LIGAND keyword not set in promolecular mode
                 // ==> find out which of the two fragment grids is the smallest one !
    
                 // the test is based on the volume enclosed by each grid
                 // note that if only one ligand has been supplied, then,
                 // its box volume will be almost 0
               if (nbAtomMolA == nbAtom) // only one FRAGMENT A has been supplied
                                         // no need to test the volumes
                  {
                     maxCoordinates[0] = maxCoordinatesA[0];
                     maxCoordinates[1] = maxCoordinatesA[1];
                     maxCoordinates[2] = maxCoordinatesA[2];
         
                     minCoordinates[0] = minCoordinatesA[0];
                     minCoordinates[1] = minCoordinatesA[1];
                     minCoordinates[2] = minCoordinatesA[2];
                  } // end of if (nbAtomMolA == nbAtom)
    
                else if (nbAtomMolA < nbAtom) {
                    // compute current volume of box of FRAGMENTS A and B
                    // the buffer is added to each dimension to avoid any possible 0 dimension
             double volA = (maxCoordinatesA[0] - minCoordinatesA[0] + 2*value) *
                           (maxCoordinatesA[1] - minCoordinatesA[1] + 2*value) *
                           (maxCoordinatesA[2] - minCoordinatesA[2] + 2*value);
         
             double volB = (maxCoordinatesB[0] - minCoordinatesB[0] + 2*value) *
                           (maxCoordinatesB[1] - minCoordinatesB[1] + 2*value) *
                           (maxCoordinatesB[2] - minCoordinatesB[2] + 2*value);
    
                    if (volB > volA) {
                     maxCoordinates[0] = maxCoordinatesA[0];
                     maxCoordinates[1] = maxCoordinatesA[1];
                     maxCoordinates[2] = maxCoordinatesA[2];
    
                     minCoordinates[0] = minCoordinatesA[0];
                     minCoordinates[1] = minCoordinatesA[1];
                     minCoordinates[2] = minCoordinatesA[2];
                    } // end of if (volB > volA) 
                    else { //volA >= volB
                     maxCoordinates[0] = maxCoordinatesB[0];
                     maxCoordinates[1] = maxCoordinatesB[1];
                     maxCoordinates[2] = maxCoordinatesB[2];
    
                     minCoordinates[0] = minCoordinatesB[0];
                     minCoordinates[1] = minCoordinatesB[1];
                     minCoordinates[2] = minCoordinatesB[2];
                    }// end of else of if (volB > volA)
         
                } // end of else if (nbAtomMolA < nbAtom) 
    
         } // end of if (setLIGAND)
         // ELSE: the minCoordinates and maxCoordinates are already set (in addAtom() )

     } // end of if( ! WFNmode)  .... end of PROMOLECULAR MODE
     // ELSE: WFN mode: the minCoordinates and maxCoordinates are already set (in addAtom() )

     // put a buffer around the molecule 
     maxCoordinates[0] += value;
     maxCoordinates[1] += value;
     maxCoordinates[2] += value;

     minCoordinates[0] -= value;
     minCoordinates[1] -= value;
     minCoordinates[2] -= value;
	  
    } // end of  if( !boxSet && !radiusSet){
  
  /* Setting the steps */
  int n0, n1, n2;
  n0 = static_cast<int>( std::ceil( (maxCoordinates[0] - minCoordinates[0]) / increments[0] ) );
  n1 = static_cast<int>( std::ceil( (maxCoordinates[1] - minCoordinates[1]) / increments[1] ) );
  n2 = static_cast<int>( std::ceil( (maxCoordinates[2] - minCoordinates[2]) / increments[2] ) );
  this->setNbSteps(n0,n1,n2);

  if(cursor != nbAtom)
    {
      std::cerr << "The number of atoms read is different from the number of atoms that should have been read. Please verify the .xyz files." << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
    }
 
} // end of Validate method .................................................................

void
ProgData::setNbSteps(int nbStepsX, int nbStepsY, int nbStepsZ)
{
  nbSteps[0] = nbStepsX;
  nbSteps[1] = nbStepsY;
  nbSteps[2] = nbStepsZ;
}
		
void
ProgData::setCube( double * cubeParam )
{

/* cube definition has already been checked upon reading the data */
/* cube definition has been given by the user in angstroms -> convert to Bohr */
			
  this->boxSet = cubeParam != NULL;
			
  /* If the cube is not NULL */
  if( cubeParam ){

      /* Setting min and max using the current cube definition (2 diagonal points) */
      minCoordinates[0] = cubeParam[0] / BOHRTOA;
      minCoordinates[1] = cubeParam[1] / BOHRTOA;
      minCoordinates[2] = cubeParam[2] / BOHRTOA;
      maxCoordinates[0] = cubeParam[3] / BOHRTOA;
      maxCoordinates[1] = cubeParam[4] / BOHRTOA;
      maxCoordinates[2] = cubeParam[5] / BOHRTOA;

					
  } // end of if( cubeParam ){
			
} // end of method setCube
		
		
void
ProgData::setRadius( double * radiusParam )
{
/* sphere definition has already been checked upon reading the data */
			
  this->radiusSet = radiusParam != NULL;
			
  /* If the cube is not NULL */
  if( radiusParam ){
				
    /* Setting min and max using the cube */
    minCoordinates[0] = (radiusParam[0] - radiusParam[3]) / BOHRTOA;
    minCoordinates[1] = (radiusParam[1] - radiusParam[3]) / BOHRTOA;
    minCoordinates[2] = (radiusParam[2] - radiusParam[3]) / BOHRTOA;
    maxCoordinates[0] = (radiusParam[0] + radiusParam[3]) / BOHRTOA;
    maxCoordinates[1] = (radiusParam[1] + radiusParam[3]) / BOHRTOA;
    maxCoordinates[2] = (radiusParam[2] + radiusParam[3]) / BOHRTOA;
  }
			
} // end of method setRadius
		
// number of bonds studied (IBSI, CAF, bond length, ...)
unsigned int
ProgData::getNbBond()
{
  return nbBond;
}

int
ProgData::getNbAtom()
{
  return nbAtom;
}

int
ProgData::getNbAtomMolA()
{
  return nbAtomMolA;
}
		
int
ProgData::getNbAtomMolB()
{
  return nbAtomMolB;
}
		
double
ProgData::getMaxCoord(int axis)
{
  return maxCoordinates[axis];
}
		
double
ProgData::getMinCoord(int axis)
{
  return minCoordinates[axis];
}

int
ProgData::getNbSteps(int axis)
{
  return nbSteps[axis];
}

std::string
ProgData::getGridBoxDefinition()
{
  if (boxSet)
    {
      return "CUBE";
    }
  
  if (radiusSet)
    {
      return "RADIUS";
    }

  return "LIGAND";
}

void
ProgData::setNbAtomMolA(int newValue)
{
  nbAtomMolA=newValue;
}

void
ProgData::setNbAtomMolB(int newValue)
{
  nbAtomMolB=newValue;
}


/* ============ storeBondLength ============================ */
void
ProgData::storeBondLength(unsigned int ibond, double length)
{
  if (length<0) 
  {
  /* cannot accept a negative bond length */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid (negative) bond length passed to function storeBondLength in ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE); 
  }
  else if ( ibond>nbBond) // remind that ibond is unsigned ! (is only >0)
  {
  /* cannot accept out of range ibond */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid bond index (out of range) passed to function storeBondLength in  ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
  }
  else 
  {
     bonds[ibond].length=length;
  }
} // end of setBondLength ...................................

void ProgData::storeBondVector(unsigned int ibond, double ABx, double ABy, double ABz)
{
  if ( (  (ABx ==0.0) and ((ABy ==0.0))
    and   (ABz ==0.0) 
       )
     )
  {
  /* cannot accept zero vector */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid (zero) bond vector passed to function storeBondVector in ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
  }
  else
  {
     bonds[ibond].ABx=ABx;
     bonds[ibond].ABy=ABy;
     bonds[ibond].ABz=ABz;
  }
} // end of setBondVector ...................................


/* ============ setIBSI ==================================== */
void
ProgData::setIBSI(unsigned int ibond, double newValue)
{
 if (newValue<0)
  {
  /* cannot accept a negative IBSI  */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid (negative) IBSI value passed to function setIBSI in ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
  }
  else if ( ibond>nbBond ) // remind that ibond is unsigned ! (is only >0)
  {
  /* cannot accept out of range ibond */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid bond index (out of range) passed to function setIBSI in ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
  }
  else
  {
  bonds[ibond].ibsi = newValue;
  }
} // end of setIBSI .........................................

/* ============ setBAF  ==================================== */
void
ProgData::setBAF(unsigned int ibond, double newValue)
{
  if ( ibond>nbBond ) // remind that ibond is unsigned ! (is only >0)
  {
  /* cannot accept out of range ibond */
      std::cerr << std::endl;
      std::cerr << "[ERROR] Invalid bond index (out of range) passed to function setBAF in ProgData " << std::endl;
      std::cerr << "The program will now exit." << std::endl;
      exit(EXIT_FAILURE);
  }
  else
  {
  bonds[ibond].baf = newValue;
  }
} // end of setIBSI .........................................

double
ProgData::getIBSI(int ibond)
{
  return bonds[ibond].ibsi;
}

double
ProgData::getBAF(int ibond)
{
  return bonds[ibond].baf;
}


#endif
