// -*- mode: c++ -*-
// WangLandau.hpp replaces mjson with libjson C++ class for JSON parser
#ifndef LSMS_WANG_LANDAU_H
#define LSMS_WANG_LANDAU_H

#include <cstdio>
#include <string>
#include <vector>
#include <utility>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
// we use BOOST for the random number generator
// #include <boost/random.hpp>
#include <random>
#include "../../libjson/json.hpp"
// #include "../../mjson/json.h"
#include "EvecGenerator.h"
#include "Graph1dMoments.hpp"
#include "SystemParameters.hpp"
#include "../Potential/PotentialShifter.hpp"

void inline performGlobalUpdate(Graph1dMoments<double,double> &g, double kappa, double lambda, double omega)
{
  for(int i=0; i<g.getN(); i++)
    if(g[i]>omega) g[i]+=kappa*std::exp(-lambda/(g[i]-omega));
}

class StatesWriter
{
public:
  StatesWriter(const char *filename=NULL)
  {
    if(filename==NULL) writeFlag=false;
    else {writeFlag=true; of.open(filename);
      of.setf(std::ios::scientific,std::ios::floatfield);
      of.precision(17);}
  }
  ~StatesWriter() {if(writeFlag) of.close();}
  void writeHeader(double lnF, int numWalk, int numSpin, double **spins)
  {
    if(writeFlag)
      {
	of<<lnF<<" "<<numWalk<<" "<<numSpin<<std::endl;
	for(int i=0; i<numWalk; i++)
	  {
	    of<<i;
	    for(int j=0; j<3*numSpin; j++) of<<" "<<spins[i][j];
	    of<<std::endl;
	  }
      }
  }
  void writeChange(int iWalk, int numRet, int ispin, double *ev, double E)
  {
    if(writeFlag)
      of<<iWalk<<" "<<numRet<<" "<<ispin<<" "<<ev[0]<<" "<<ev[1]<<" "<<ev[2]<<" "<<E<<std::endl;
  }
  void writeChangeOcc(int iWalk, int numRet, int i, int j, int occ_i, int occ_j, double E)
  {
    if(writeFlag)
      of<<iWalk<<" "<<numRet<<" ("<<i<<"<-->"<<j<<") "<<occ_i<<" "<< occ_j <<" "<<E<<std::endl;
  }
  void newFile(const char *filename=NULL)
  {
    if(writeFlag) of.close();
    writeFlag=false;
    if(filename!=NULL){
      writeFlag=true;
      of.open(filename);
      of.setf(std::ios::scientific,std::ios::floatfield);
      of.precision(8);
    }
  }
private:
  bool writeFlag;
  std::ofstream of;
};

template<class RNG = std::mt19937>
// template<class RNG = boost::mt19937>
class WL1dEvecGenerator : public EvecGenerator
{
 public:
  WL1dEvecGenerator(int num_spins, int num_instances, double ** ev_p,
                    PotentialShifter &potentialShifter,
                    const char *init_file_name=NULL, const char *out_file_name=NULL,
                    const char *states_filename=NULL);

  bool determineAcceptance(int instance, double energy);
  bool determineAcceptance(int instance, double energy, double magnetization);

  bool updateHistogram(int instance, double *evecs, bool accepted);
/*
  bool updateHistogram(int instance, double *evecs, double energy, bool *accepted);
  bool updateHistogram(int instance, double *evecs, double energy, double magnetization, bool *accepted);
  bool updateHistogram(int instance, double *evecs, double energy) {bool h; return updateHistogram(instance, evecs, energy, &h);}
  bool updateHistogram(int instance, double *evecs) {std::cerr<<"Need energy for WL1dEvecGenerator\n"; exit(1);}
*/

  void generateEvec(int instance, double *evecs, bool accepted);
  //void generateEvec(int instance, double *evecs, double energy);
  void generatePotentialShift(int instance, double *potentialShifts, bool accepted);

  void initializeEvecAndPotentialShift(int inst, double *evecs, double *potentialShifts);
  void initializeEvec(int instance, double *evecs);

  // Wang-Landau for occupancy variables
  MoveChoice_t selectMoveType(bool isSpinSim, bool isOccSim);
  void setAlloyClasses(AlloyMixingDesc&, int* siteclass);
  void generateOccupancies(int instance, int *occ, bool acceptedOcc);
  void generateUnsampledOcc(int inst, int *occ);
  void initializeOccupancies(int inst, int *occ);
  void updateLog(int instance, double *evecs, int * occ, bool accepted, MoveChoice_t MoveChoice);

  void generateUnsampledEvec(int instance, double *evecs, double energy)
  {
    initializeEvec(instance, evecs);
    //return false;
  }

  void startSampling(void)
  { sw.writeHeader(gamma, n_walkers, n_spins, evecs_pointer); }

  void writeState(const char *name);
  void writeDos(const char *name);

 private:
  size_t n_walkers;
  int n_spins;
  double ** evecs_pointer;
  int n_initialized_from_file;

  std::string dos_out_name;

  int stepsSinceLastHistogramUpdate;
  int numberOfUpdatesSinceLastBoost;
  int cycleCount;
  int modificationFactorChanges;

  // Random number generator and distribution:
  RNG rng;
  // boost::uniform_real<double> rnd; //, rnd11(-1.0,1.0),rnd0pi(0.0,2.0*M_PI);
  std::uniform_real_distribution<double> rnd; //, rnd11(-1.0,1.0),rnd0pi(0.0,2.0*M_PI);
  std::uniform_real_distribution<double> rnd01; //(0.0,1.0);

  /*
  // Histogramm and dos:
  double xMin, xMax, interval;
  int nX;
  double *dos; // std::vector<double> dos;
  int *histo; // std::vector<int> histo;
  int hMinimum;
  */

  double hMinimum;
  Graph1dMoments<double,double> dos, histo;
  Kernel1d<double,double> dosKernel, histoKernel, nullKernel;
  KernelType kernelType;

  unsigned long accept, reject, acceptSinceLastChange;
  double flatnessCriterion;

  double gamma, gammaFinal;
  int flipPerUpdate, updateCycle;

  // instance specific:
  std::vector<long> ref0, ref1;
  std::vector<double> position, magnetizationAtPosition;
  std::vector<bool> out;
  std::vector<int> lastChange;
  std::vector<int> lastChangePotentialShift;
  std::vector<int> lastAccepted;
  std::vector<int> lastAcceptedPotentialShiftIndex;
  std::vector<double> lastAcceptedEnergy;
  std::vector<double> lastAcceptedEvec;
  std::vector<double> oldSpin;  // oldSpin[instance*3 + {x=0, y=1, z=2}]
  std::vector<double> oldPotentialShift;
  std::vector<double> lastAcceptedPotentialShift;

  // changes to accomodate alloying (i.e. variable site occupancies)
  std::vector< std::pair<int,int> > lastSwapOcc;
  std::vector< std::pair<int,int> > lastAcceptedSwap;
  AlloyMixingDesc& alloyDesc;
  std::vector<int> siteAlloyClass;
  std::vector<int> numSites_per_AlloyClass;

  std::vector<int> numRetentions;

  char *statesFile;
  StatesWriter sw;

  int changeMode;
  bool histogramUpdateMode;
  int updatesPerBin;

  struct {double kappa, lambda, omega; int frequency, changes;} globalUpdate;

#ifdef ISING
    void inline random_evec_1(double ev[3])
  {
    ev[0]=ev[1]=0.0;
    ev[2]=1.0;
    if(rng()%2 == 0) ev[2]=-ev[2];
  }
#else
  void inline random_evec_1(double ev[3])
  {
    double x,y,z;
    do {
      x = rnd(rng);
      y = rnd(rng);
    } while(x*x+y*y>1);
    z = rnd(rng);
    double r = sqrt((1-z*z)/(x*x+y*y));
    x *= r;
    y *= r;
    if (rng() % 2 == 0) x = -x;
    if (rng() % 2 == 0) y = -y;
    if (rng() % 2 == 0) z = -z;
    r=1.0/sqrt(x*x+y*y+z*z);
    ev[0]=x*r; ev[1]=y*r; ev[2]=z*r;
  }
#endif

#ifdef ISING
  void inline random_evec(double ev[3])
  {
    ev[2]=-ev[2];
  }
#else
  void inline random_evec(double ev[3])
  {
    double x, y, z;
    do {
      x = rnd(rng); y = rnd(rng);
    } while(x*x+y*y>1);
    z = rnd(rng);
    double r = sqrt((1-z*z)/(x*x+y*y));
    x *= r; y*= r;
    if (rng() % 2 == 0) x = -x;
    if (rng() % 2 == 0) y = -y;
    if (rng() % 2 == 0) z = -z;
    // Project out the parallel component;
    r = x*ev[0] + y*ev[1] + z*ev[2];
    x -= r*ev[0]; y -= r*ev[1]; z -= r*ev[2];
    r = x*x + y*y + z*z;
    double t = 1-0.3*rnd(rng);
    ev[0] *= t; ev[1] *= t; ev[2] *= t;
    r = sqrt((1-t*t)/r);
    ev[0] += x*r; ev[1] += y*r; ev[2] += z*r;
    r=1.0/sqrt(ev[0]*ev[0]+ev[1]*ev[1]+ev[2]*ev[2]);
    ev[0]*=r; ev[1]*=r; ev[2]*=r;

    /*
    ev[2]=1.0-2.0*rnd(rng);
    // ev[2]=rnd11(rng);
    double phi=2.0*M_PI*rnd(rng);
    // double phi=rnd0pi(rng);
    double cos_theta=sqrt(1-ev[2]*ev[2]);
    ev[0]=cos_theta*cos(phi);
    ev[1]=cos_theta*sin(phi);
    */
  }
#endif

  double minVxShift {0.0}, maxVxShift {0.0}, rangeVxShift {0.0};

  double inline randomPotentialShift()
  {
    return minVxShift + rangeVxShift * rnd01(rng);
  }
};

template<typename T>
void getValue(JSON::Value &data, const char *key, T &val)
{ if(data[key].type()!=JSON::NIL) val=(T)data[key]; }

template<class RNG>
WL1dEvecGenerator<RNG>::WL1dEvecGenerator(int num_spins, int num_instances, double ** ev_p,
                                          PotentialShifter &potentialShifter,
					  const char *init_file_name, const char *out_file_name,
                                          const char *states_filename)
: sw(states_filename)
{

  printf("Running WL1D constructor!\n");

  if (potentialShifter.vSpinShiftFlag) {
    minVxShift = potentialShifter.minShift;
    maxVxShift = potentialShifter.maxShift;
    rangeVxShift = maxVxShift - minVxShift;
  }

  verbosity=3;

  changeMode = 8+16;

  globalUpdate.frequency=0;
  globalUpdate.changes=0;
  globalUpdate.kappa=1.0;
  globalUpdate.lambda=1.0;
  globalUpdate.omega=0.5;

  long nX=-1;
  histogramUpdateMode=false;
  updatesPerBin=100;
  double interval=0.01;
  double kernelWidth=0.1;
  double xMin=-HUGE;
  double xMax=1.0;
  bool readPositions=false;
  n_spins=num_spins;
  n_walkers = num_instances;
  n_initialized_from_file = 0;
  evecs_pointer = ev_p;
  ref0.resize(n_walkers);  for(int i=0; i<n_walkers; i++) ref0[i]=-1; // ref0[i]=HUGE;
  ref1.resize(n_walkers);
  position.resize(n_walkers);
  magnetizationAtPosition.resize(n_walkers);
  out.resize(n_walkers);
  lastChange.resize(n_walkers);
  lastAccepted.resize(n_walkers);
  lastAcceptedEnergy.resize(n_walkers);
  lastAcceptedEvec.resize(3*n_walkers);

  oldPotentialShift.resize(n_walkers);

  lastChangePotentialShift.resize(n_walkers);
  lastAcceptedPotentialShiftIndex.resize(n_walkers);
  lastAcceptedPotentialShift.resize(n_walkers);

  lastSwapOcc.resize(n_walkers);
  lastAcceptedSwap.resize(n_walkers);

  for(int i=0; i<n_walkers; i++)
  {
    lastAccepted[i]=-2;
    lastAcceptedPotentialShiftIndex[i] = -2;
    lastAcceptedPotentialShift[i] = 0.0;
  }
  for(int i=0; i<3*n_walkers; i++)  lastAcceptedEvec[i]=0.0;

  for(int i=0; i<n_walkers; i++) initializeEvec(i,evecs_pointer[i]);

  oldSpin.resize(3*n_walkers);

  statesFile=NULL;
  if(states_filename!=NULL)
    {
      statesFile=(char *)malloc(sizeof(char)*(1+strlen(states_filename)));
      strcpy(statesFile,states_filename);
    }

  numRetentions.resize(n_walkers);
  for(int i=0; i<n_walkers; i++) numRetentions[i]=0;

  /*
  nX = -1;
  xMin = -HUGE; xMax= 1.0; interval = 0.01; // (xMax-xMin)/double(nX);
  */
  dos_out_name="dos1d.out";
  stepsSinceLastHistogramUpdate=0;
  numberOfUpdatesSinceLastBoost=0;
  modificationFactorChanges=0;
  cycleCount=0;
  hMinimum= 1;     //   10
  acceptSinceLastChange=accept=reject=0;
  gammaFinal=1.e-6;
  flipPerUpdate=1; //  100
  updateCycle= 5*num_instances;  // 1000
  gamma = 1.0;
  flatnessCriterion = 0.75;
  updatesPerBin =100;

  kernelType=None;

  // special processing flags:
  int clearHistogram = 0;
  int setFirstWalkerToFM = 0;

  // dos = NULL;   // dos.resize(nX);
  // histo = NULL; // histo.resize(nX);
  dos.setDeltaAndClear(interval);
  histo.setDeltaAndClear(interval);

  if(init_file_name!=NULL && init_file_name[0]!=0)
  {
    std::string label, value;

    std::cout<<"Reading Wang-Landau configuration from: "<<init_file_name<<std::endl;

    dos_out_name=std::string(init_file_name)+".out";
    if(out_file_name!=NULL && out_file_name[0]!=0) dos_out_name=out_file_name;
    std::ifstream inp(init_file_name);
    std::ostringstream buff;

    std::string line;
    while(std::getline(inp,line))
      buff << line << std::endl;

    inp.close();

    std::string fileString = buff.str();
    const char* fileChars  = fileString.c_str();

    // json_t *json_root=NULL;
    JSON::Value data=parse_file(fileChars);
    // json_parse_document(&json_root, (char *)fileChars);

    if(data.type() != JSON::OBJECT)
    {
      std::ostringstream message;
      std::cerr << "In WL1dEvecGenerator(" << init_file_name << ") parsing failed (bad format)\n";
      exit(1);
    }

// check if the input file is for start (specifies "interval") or a restart (specifies "nX")

    if(data["nX"].type()==JSON::NIL) // initial WLstart file
    {
      interval=data["interval"].as_float();
    } else {
      xMin=data["xMin"].as_float();
      xMax=data["xMax"].as_float();
      nX=data["nX"].as_int();
      dos.setRangeAndClear(xMin,xMax,nX);
      histo.setRangeAndClear(xMin,xMax,nX);
    }

// common data for both start and restart files
    getValue(data,"gamma",gamma);
    getValue(data,"gammaFinal",gammaFinal);
    getValue(data,"kernelWidth",kernelWidth);
    if(data["kernelType"].type()==JSON::STRING) { std::string str=data["kernelType"].as_string(); kernelType=getKernelType(str); }
    getValue(data,"flipPerUpdate",flipPerUpdate);
    getValue(data,"updateCycle",updateCycle);
    getValue(data,"cycleCount",cycleCount);
    getValue(data,"changeMode",changeMode);
    getValue(data,"flatnessCriterion",flatnessCriterion);
    getValue(data,"histogramMinimum",hMinimum);
    getValue(data,"updatesPerBin",updatesPerBin);
    getValue(data,"globalUpdate.frequency",globalUpdate.frequency);
    getValue(data,"globalUpdate.changes",globalUpdate.changes);
    getValue(data,"globalUpdate.kappa",globalUpdate.kappa);
    getValue(data,"globalUpdate.lambda",globalUpdate.lambda);
    getValue(data,"globalUpdate.omega",globalUpdate.omega);
    if(data["seed"].type()!=JSON::NIL) rng.seed(data["seed"].as_int());
    if(data["rngState"].type()==JSON::STRING)
    {
      std::string str=data["rngState"].as_string();
      std::stringstream strStream(str, std::stringstream::in);
      strStream>>rng;
    }
    getValue(data,"accept",accept);
    getValue(data,"acceptSinceLastChange",acceptSinceLastChange);
    getValue(data,"reject",reject);

    getValue(data,"stepsSinceLastHistogramUpdate",stepsSinceLastHistogramUpdate);
    getValue(data,"numberOfUpdatesSinceLastBoost",numberOfUpdatesSinceLastBoost);
    getValue(data,"modificationFactorChanges",modificationFactorChanges);
    getValue(data,"clearHistogram",clearHistogram);
    getValue(data,"setFirstWalkerToFM",setFirstWalkerToFM);

    if(data["dos"].type()==JSON::ARRAY)
    {
      const JSON::Array &a=data["dos"].as_array();
      if(a.size()!=nX) {std::cout<<"ERROR #(dos) "<<a.size()<<" != nX "<<dos.getN()<<std::endl; exit(1);}
      for(size_t i=0; i<nX; i++) dos[i]=a[i].as_float();
    }

    if(data["histo"].type()==JSON::ARRAY)
    {
      const JSON::Array &a=(JSON::Array)data["histo"];
      if(a.size()!=nX) {std::cout<<"ERROR #(histo) "<<a.size()<<" != nX "<<histo.getN()<<std::endl; exit(1);}
      for(size_t i=0; i<nX; i++) histo[i]=a[i].as_float();
    }

    if(data["moments.k"].type()!=JSON::NIL) dos.setNumberOfMoments(data["moments.k"].as_int());
    if(data["moments"].type()==JSON::ARRAY)
    {
      const JSON::Array &a=(JSON::Array)data["moments"];
      int k=a[0].as_int();
      dos.setNumberOfMoments(k);
      const JSON::Array &b=(JSON::Array)a[1]; // Samples At Index
      for(size_t i=0; i<nX; i++)
        dos.setNumberOfSamplesAtIdx(i,b[i].as_int());
      for(int j=0; j<k; j++)
      {
        const JSON::Array &c=(JSON::Array)a[2+j];
        for(size_t i=0; i<nX; i++)
          dos.setMomentAtIdx(i,j,c[i].as_float());
      }
    }

    if(data["evecs"].type()!=JSON::NIL)
    {
      const JSON::Array &a=(JSON::Array)data["evecs"];
      n_initialized_from_file=std::min(n_walkers,a.size());
      for(int j=0; j<n_initialized_from_file; j++)
      {
        const JSON::Array &b=(JSON::Array)a[j];
        for(size_t i=0; i<3*nX; i++)
          evecs_pointer[j][i]=b[i].as_float();
// // initialize oldSpin and lastChange to point to site 0
//            lastChange[n_initialized_from_file]=0;
//            oldSpin[  3*n_initialized_from_file]=evecs_pointer[n_initialized_from_file][0];
//            oldSpin[1+3*n_initialized_from_file]=evecs_pointer[n_initialized_from_file][1];
//            oldSpin[2+3*n_initialized_from_file]=evecs_pointer[n_initialized_from_file][2];
// //
      }
    }
    if(data["oldSpin"].type()!=JSON::NIL)
    {
      const JSON::Array &a=(JSON::Array)data["oldSpin"];
      int n_initialized=std::min(n_walkers,a.size());
      for(int j=0; j<n_initialized; j++)
      {
        for(int i=0; i<3; i++)
          oldSpin[j*3+i]=a[j][i].as_float();
      }
    }
    if(data["lastChange"].type()!=JSON::NIL)
    {
      const JSON::Array &a=(JSON::Array)data["lastChange"];
      int n_initialized=std::min(n_walkers,a.size());
      for(int i=0; i<n_initialized; i++)
        lastChange[i]=a[i].as_int();
    }
    if(data["position"].type()!=JSON::NIL) // "position" is energy
    {
      const JSON::Array &a=(JSON::Array)data["position"];
      int n_initialized=std::min(n_walkers,a.size());
      for(int i=0; i<n_initialized; i++)
        position[i]=a[i].as_float();
      readPositions=true;
    }
    if(data["magnetizationAtPosition"].type()!=JSON::NIL)
    {
      const JSON::Array &a=(JSON::Array)data["magnetizationAtPosition"];
      int n_initialized=std::min(n_walkers,a.size());
      for(int i=0; i<n_initialized; i++)
        magnetizationAtPosition[i]=a[i].as_float();
      readPositions=true;
    }

    // set xMax and xMin or interval depending on nX:
    if(nX>0)
    {
      interval=(xMax-xMin)/double(nX);
      dos.setRange(xMin,xMax); histo.setRange(xMin,xMax);
    } else {
      dos.setDeltaAndClear(interval); histo.setDeltaAndClear(interval);
    }

    // json_free_value(&json_root);
  }

  dosKernel.setWidthAndClear(interval,kernelWidth);
  histoKernel.setWidthAndClear(interval,kernelWidth);
  nullKernel.setWidthAndClear(interval,kernelWidth);
  initKernel(kernelType,dosKernel);
  dosKernel.scale(gamma/dosKernel(0.0));
  initKernel(kernelType,histoKernel);
  histoKernel.scale(1.0/histoKernel(0.0));
  initKernel(kernelType,nullKernel);
  nullKernel.scale(0.0);
//  histoKernel.scale(interval);

  if(readPositions)
    for(int i=0; i<n_walkers; i++)
      ref0[i]=dos.idx(position[i]);

  if(clearHistogram!=0)
  {
    std::cout<<"Clearing Wang-Landau Histogram.\n";
    histo.clear();
  }

  if(setFirstWalkerToFM!=0)
  {
    std::cout<<"Setting first walker to FM.\n";
    for(int i=0; i<n_spins; i++)
    {
      evecs_pointer[0][  3*i]=0.0;
      evecs_pointer[0][1+3*i]=0.0;
      evecs_pointer[0][2+3*i]=1.0;
    }
// initialize oldSpin and lastChange to point to site 0
   lastChange[0]=0;
   oldSpin[0]=evecs_pointer[0][0];
   oldSpin[1]=evecs_pointer[0][1];
   oldSpin[2]=evecs_pointer[0][2];

// initialize oldPotentialShift and lastChangePotentialShift to point to site 0
    lastChangePotentialShift[0] = 0;
    oldPotentialShift[0] = 0.0;
  }

  if(out_file_name!=NULL && out_file_name[0]!=0) dos_out_name=out_file_name;
  std::cout<<"Wang-Landau output will be written to: "<<dos_out_name<<std::endl;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::initializeEvecAndPotentialShift(int inst, double *evecs, double *potentialShifts)
{
  for (size_t j=0; j<n_spins; j++)
    potentialShifts[j] = 0.0;

  initializeEvec(inst, evecs);
}

template<class RNG>
void WL1dEvecGenerator<RNG>::initializeEvec(int inst, double *evecs)
{
  bool firstFerromagnetic=true;
  if(inst>=n_initialized_from_file)
    {
      if(firstFerromagnetic)
        if(inst==0)
	  for(size_t j=0; j<3*n_spins; j+=3)
	    {evecs[j]=0.0; evecs[j+1]=0.0; evecs[j+2]=1.0;}
        else if(inst==1)
          for(size_t j=0; j<3*n_spins; j+=3)
            {evecs[j+2]= (j/3)%2 ? 1.0 : -1.0 ; evecs[j+1]=0.0; evecs[j]=0.0;}
        else
	  for(size_t j=0; j<3*n_spins; j+=3)
	    random_evec_1(&evecs[j]);
      else
        for(size_t j=0; j<3*n_spins; j+=3)
          random_evec_1(&evecs[j]);
    }

  out[inst]=false;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::writeState(const char* name)
{
  // if(!syncronizeGraphs(dos,histo))
  if(dos.getN()!=histo.getN())
  {
    std::cout<<"Histogramm size dosn't match DOS! Clearing histogramm!\n";
    histo.setRangeAndClear(dos.getMinX(),dos.getMaxX(),dos.getN());
  }
  std::ofstream ofile(name);
  if(ofile)
  {
    ofile.setf(std::ios::scientific,std::ios::floatfield);
    ofile.precision(12);
    ofile<<"{\n";
    ofile<<"\"xMin\" : " << dos.getMinX() << ",\n";
    ofile<<"\"xMax\" : " << dos.getMaxX() << ",\n";
    ofile<<"\"nX\" : " << dos.getN() << ",\n";
    if(kernelType!=None)
      ofile<<"\"kernelWidth\" : " << dosKernel.getWidth() << ",\n";
    std::string kernelName;
    getKernelName(kernelType,kernelName);
    ofile<<"\"kernelType\" : \"" << kernelName << "\",\n";
    ofile<<"\"gamma\" : " << gamma << ",\n";
    ofile<<"\"accept\" : " << accept <<",\n";
    ofile<<"\"acceptSinceLastChange\" : " << acceptSinceLastChange <<",\n";
    ofile<<"\"reject\" : " << reject <<",\n";
    ofile<<"\"gammaFinal\" : " << gammaFinal << ",\n";

    ofile<<"\"changeMode\" : "<< changeMode <<",\n";
    ofile<<"\"flatnessCriterion\" : "<< flatnessCriterion <<",\n";
    ofile<<"\"histogramMinimum\" : "<< hMinimum <<",\n";
    ofile<<"\"updatesPerBin\" : "<< updatesPerBin <<",\n";
    ofile<<"\"flipPerUpdate\" : " << flipPerUpdate << ",\n";
    ofile<<"\"updateCycle\" : " << updateCycle << ",\n";

    ofile<<"\"globalUpdate.frequency\" : "<<  globalUpdate.frequency << ",\n";
    ofile<<"\"globalUpdate.changes\" : "<<  globalUpdate.changes << ",\n";
    ofile<<"\"globalUpdate.kappa\" : "<<  globalUpdate.kappa << ",\n";
    ofile<<"\"globalUpdate.lambda\" : "<<  globalUpdate.lambda << ",\n";
    ofile<<"\"globalUpdate.omega\" : "<<  globalUpdate.omega << ",\n";

    ofile<<"\"dos\" : ["<<std::endl;
    for(int i=0; i<dos.getN(); i++) ofile<<dos[i]<<((i==dos.getN()-1)?"\n":",\n");
    ofile<<"],\n";
    ofile<<"\"histo\" : ["<<std::endl;
    for(int i=0; i<histo.getN(); i++) ofile<<histo[i]<<((i==histo.getN()-1)?"\n":",\n");
    ofile<<"],\n";
    if(dos.getNumberOfMoments()>0)
    {
      ofile<<"\"moments\" : ["<<std::endl;
      ofile<<dos.getNumberOfMoments()<<", ["<<std::endl;
      for(int i=0; i<dos.getN(); i++) ofile<<dos.getNumberOfSamplesAtIdx(i)<<((i==dos.getN()-1)?"\n":",\n");
      ofile<<"], [\n";
      for(int j=0; j<dos.getNumberOfMoments(); j++)
      {
        for(int i=0; i<dos.getN(); i++) ofile<<dos.getMomentAtIdx(i,j)<<((i==dos.getN()-1)?"\n":",\n");
        if(j!=dos.getNumberOfMoments()-1) ofile<<"], [\n";
      }
      ofile<<"] ],\n";
    }
    ofile<<"\"rngState\" : \""<<rng<<"\",\n";
    ofile<<"\"evecs\" : [\n";
    for(int i=0; i<n_walkers; i++)
    {
      ofile<<"[\n";
      for(int j=0; j<3*n_spins; j+=3)
        ofile<<evecs_pointer[i][j]<<", "<<evecs_pointer[i][j+1]
             <<", "<<evecs_pointer[i][j+2]<<((j==3*n_spins-3)?"\n":",\n");
      ofile<<((i==n_walkers-1)?"]\n":"],\n");
    }
    ofile<<"],\n";
    ofile<<"\"oldSpin\" : [\n";
    for(int i=0; i<n_walkers; i++)
    {
      ofile<<"[ "<<oldSpin[3*i]<<", "<<oldSpin[3*i+1]
             <<", "<<oldSpin[3*i+2];
      ofile<<((i==n_walkers-1)?"]\n":"],\n");
    }
    ofile<<"],\n";
    ofile<<"\"lastChange\" : [\n";
    for(int i=0; i<n_walkers; i++)
    {
      ofile<< lastChange[i]<<((i==n_walkers-1)?"\n":",\n");
    }
    ofile<<"],\n";
    ofile<<"\"position\" : [\n";
    for(int i=0; i<n_walkers; i++)
    {
      ofile<< position[i]<<((i==n_walkers-1)?"\n":",\n");
    }
    ofile<<"],\n";
    ofile<<"\"magnetizationAtPosition\" : [\n";
    for(int i=0; i<n_walkers; i++)
    {
      ofile<< magnetizationAtPosition[i]<<((i==n_walkers-1)?"\n":",\n");
    }
    ofile<<"],\n";
    ofile<<"\"stepsSinceLastHistogramUpdate\" : " << stepsSinceLastHistogramUpdate << ",\n";
    ofile<<"\"numberOfUpdatesSinceLastBoost\" : " << numberOfUpdatesSinceLastBoost << ",\n";
    ofile<<"\"modificationFactorChanges\" : " << modificationFactorChanges << ",\n";
    ofile<<"\"cycleCount\" : " << cycleCount << "\n";
    ofile<<"}\n";
    ofile.close();
  } else std::cerr<<"# CAUTION: DoS output file could not be opened!\n";
}

template<class RNG>
void WL1dEvecGenerator<RNG>::writeDos(const char* name)
{
  std::ofstream ofile(name);
  ofile.setf(std::ios::scientific,std::ios::floatfield);
  ofile.precision(12);
  // write dos;
  // we are using JSON as our file format
  if(ofile)
  {
    ofile<<"{\n";
    ofile<<"\"xMin\" : " << dos.getMinX() << "," <<std::endl;
    ofile<<"\"xMax\" : " << dos.getMaxX() << "," <<std::endl;
    ofile<<"\"nX\" : " << dos.getN() << "," <<std::endl;
    ofile<<"\"kernelWidth\" : " << dosKernel.getWidth() << ",\n";
    std::string kernelName;
    getKernelName(kernelType,kernelName);
    ofile<<"\"kernelType\" : \"" << kernelName << "\",\n";
    ofile<<"\"gamma\" : " << gamma <<"," <<std::endl;
    ofile<<"\"gammaFinal\" : " << gammaFinal << "," <<std::endl;
    ofile<<"\"globalUpdate.changes\" : "<<  globalUpdate.changes << ",\n";
    ofile<<"\"flipPerUpdate\" : " << flipPerUpdate << "," <<std::endl;
    ofile<<"\"updateCycle\" : " << updateCycle << "," <<std::endl;
    ofile<<"\"dos\" : ["<<std::endl;
    for(int i=0; i<dos.getN(); i++) ofile<<dos[i]<<((i==dos.getN()-1)?"\n":",\n");
    ofile<<"],\n";
    if(dos.getNumberOfMoments()>0)
    {
      ofile<<"\"moments\" : ["<<std::endl;
      ofile<<dos.getNumberOfMoments()<<", ["<<std::endl;
      for(int i=0; i<dos.getN(); i++) ofile<<dos.getNumberOfSamplesAtIdx(i)<<((i==dos.getN()-1)?"\n":",\n");
      ofile<<"], [\n";
      for(int j=0; j<dos.getNumberOfMoments(); j++)
      {
        for(int i=0; i<dos.getN(); i++) ofile<<dos.getMomentAtIdx(i,j)<<((i==dos.getN()-1)?"\n":",\n");
        if(j!=dos.getNumberOfMoments()-1) ofile<<"], [\n";
      }
      ofile<<"] ],\n";
    }
    ofile<<"\"histo\" : ["<<std::endl;
    for(int i=0; i<histo.getN(); i++) ofile<<histo[i]<<((i==histo.getN()-1)?"\n":",\n");
    ofile<<"]\n}\n";
    ofile.close();
  } else std::cerr<<"# CAUTION: DoS output file could not be opened!\n";
}

template<class RNG>
bool WL1dEvecGenerator<RNG>::determineAcceptance(int instance, double energy)
{
  return determineAcceptance(instance, energy, 0.0);
}

template<class RNG>
bool WL1dEvecGenerator<RNG>::determineAcceptance(int instance, double energy, double magnetization)
{

  bool accept_step;
  // +++++++++ Added by Odbadrakh and Don on Aug 11, 2010
  // +++ If a flip of spin results in energy in the same bin as previous accepted
  // +++ state, it is accepted all the time. We change it by accepting if the
  // +++ new state has lower density of states according to a linear spline of DOS

  double dos_differ;
  double energy_differ;
  double to_go_or_not;

  // +++++++++ end of modification. Follow the new variables to see the actual changes
  // energy between xMin and xMax ?
  int grid = dos.idx(energy);

  // dos.test();

  stepsSinceLastHistogramUpdate++; // counter[0]++

  // record initial energy for step out
  if (lastAccepted[instance] == -2)
  {
    position[instance] = lastAcceptedEnergy[instance] = energy;
    lastAccepted[instance] = -1;
  }

  if (grid < 0 || grid > dos.getN()-1)
  {
    dos.extendTo(energy - dosKernel.getWidth()); histo.extendTo(energy - histoKernel.getWidth());
    dos.extendTo(energy + dosKernel.getWidth()); histo.extendTo(energy + histoKernel.getWidth());
    grid = dos.idx(energy);
    // we need to adjust the grid point of the walker positions (ref0) for all walkers
    for (long ii=0; ii<n_walkers; ii++)
      if (ref0[ii] >= 0) ref0[ii] = dos.idx(position[ii]);
  }
  numRetentions[instance]++;
  out[instance] = false;
  ref1[instance] = grid;
  if (ref0[instance] < 0)
  {
      accept_step = true;
      ref0[instance] = ref1[instance];
      position[instance] = energy;
      magnetizationAtPosition[instance] = magnetization;
  }
  else
  {
    if (abs(ref1[instance] - ref0[instance]) < 1 )
    {
// Actual change made by Odbadrakh, Aug 30, 2010
      dos_differ = dos[ref0[instance]-1] - dos[ref0[instance]];
      energy_differ = energy - lastAcceptedEnergy[instance];
      to_go_or_not = dos_differ * energy_differ;
      if (to_go_or_not >= 0.0)        // accepts all downhill changes
      {
        accept_step = true;
        ref0[instance] = ref1[instance];
        position[instance] = energy;
        magnetizationAtPosition[instance] = magnetization;
      }
      else       // uphill moves
      {
        if(rnd(rng) < exp(to_go_or_not/dos.getDelta()))
        { //std::cout<<" dos "<<instance<<"  weight "<<dos.getDelta()<<std::endl;
          accept_step = true;
          ref0[instance] = ref1[instance];
          position[instance] = energy;
          magnetizationAtPosition[instance] = magnetization;
        }
        else
        {
          accept_step = false;
        }
      }
    }
    else
    {
      if (dos[ref1[instance]] <= dos[ref0[instance]])
      {
        accept_step = true;
        ref0[instance] = ref1[instance];
        position[instance] = energy;
        magnetizationAtPosition[instance] = magnetization;
      }
      else
      {
        if(rnd(rng) < exp(dos[ref0[instance]] - dos[ref1[instance]]))
        {
          accept_step = true;
          ref0[instance] = ref1[instance];
          position[instance] = energy;
          magnetizationAtPosition[instance] = magnetization;
        }
        else
        {
          accept_step = false;
        }
      }

    }

 }

// End of change made on Aug 30, 2010
  if (verbosity > 2)
    std::cout << "WangLandau 1d EvecGenerator step "
              << modificationFactorChanges << ":" << numberOfUpdatesSinceLastBoost << ":"
              << stepsSinceLastHistogramUpdate << " nX=" << dos.getN()
	      << " [" << dos.getMinX() << ", " << dos.getMaxX() << "] "
              << (accept_step ? "accepted" : "rejected")
              << " Energy = " << energy << ", Instance " << instance << std::endl;

  return accept_step;

}

/*
template<class RNG>
bool WL1dEvecGenerator<RNG>::updateHistogram(int instance, double *evecs, double energy, bool *accepted)
{
  return updateHistogram(instance, evecs, energy, 0.0, accepted);
}
*/

template<class RNG>
bool WL1dEvecGenerator<RNG>::updateHistogram(int instance, double *evecs, bool accepted)
{

// Update histogram and DOS
  if(stepsSinceLastHistogramUpdate >= flipPerUpdate)
  {
    stepsSinceLastHistogramUpdate = 0;       // counter[0]
    numberOfUpdatesSinceLastBoost++;         // counter[1]
    cycleCount++;
    // modify the DoS
    if(!out[instance])
    {
      // addKernel(dos,dosKernel,energy);
      if (!histogramUpdateMode)             // standard Wang-Landau
      {
        addKernel(dos, dosKernel, position[instance]);
        dos.addMomentsAtIdx(dos.idx(position[instance]), magnetizationAtPosition[instance]);
        // if(accepted) addKernel(histo,histoKernel,position[instance]);
        addKernel(histo, histoKernel, position[instance]);
        // addKernel(dos, dosKernel, energy);
        // addKernel(histo, histoKernel, energy);
      }
      else
      {
        // addKernel(dos, nullKernel, position[instance]);
        if(accepted) addKernel(histo, histoKernel, position[instance]);
      }
    }
    else
    {
      std::cerr << "ATTENTION: We should never reach this place in WL1dEvecGenerator!\n";
      exit(1);
    }
  }

// 1/t algorithm, change gamma at every step
// Reference: Phys. Rev. E 75, 046701 (2007).
  if(changeMode & (8+16+32))
  {
    long n = accept + reject;
    double dn;

    if ((changeMode &  (8+16)) == 8) n = accept;
    else if ((changeMode &  (8+16)) == 16) n = reject;
    else if ((changeMode &  (8+16)) == 8+16) n = accept + reject;

    if (changeMode & 32) dn = double(n) / double(n_walkers);
    else dn = double(n);

    gamma = 1.0 / dn;
    if (gamma > 1.0) gamma = 1.0;

    initKernel(kernelType, dosKernel);
    dosKernel.scale(gamma);
  }

// 1. write configuration
// 2. check histogram flatness
// 3. perform global update
  if (cycleCount >= updateCycle)
  {
    cycleCount = 0;
    // syncronizeGraphs(dos, histo);
    if (dos.getN() != histo.getN())
    {
      std::cout << "Histogramm size dosn't match DOS! Clearing histogramm!\n";
      histo.setRangeAndClear(dos.getMinX(), dos.getMaxX(), dos.getN());
    }

    writeState("WL1d.state");

    if (!histogramUpdateMode)        // Normal Wang-Landau
    {
      // calculate minimum nonzero histogram
      // we look only at the histogram inside the energy interval that was actually sampled if we use kernel updates
      double hMin, hMax, hMean;

      if (kernelType == None)
      {
        hMean = histo.getMeanY();
        histo.getMinMaxY(hMin,hMax);
      }
      else
      {
        hMean = histo.getMeanYInInterval(dos.getMinX() + dosKernel.getWidth(),
                                         dos.getMaxX() - dosKernel.getWidth());
        histo.getMinMaxYInInterval(dos.getMinX() + dosKernel.getWidth(),
                                   dos.getMaxX() - dosKernel.getWidth(),
                                   hMin, hMax);
      }

      // double currentFlatnessCriterion = double(hMin-hMax) / hMean;
      double currentFlatnessCriterion = double(hMin) / hMean;

      std::cout << "# acceptence ratio = " << double(accept) / double(accept+reject) << "\n";
      std::cout << "# current flatness = " << currentFlatnessCriterion << (changeMode & 4 ? " *":"") << "\n";
      std::cout << "# current histogram minimum = " << hMin << (changeMode & 2 ? " *":"") << "\n";
      std::cout << "# average accepted steps/bin since last gamma change = "
                << double(acceptSinceLastChange) / double(histo.getN())
                << (changeMode & 1 ? " *":"") << "\n";

      if (changeMode != 0 && changeMode < 8)
      {
        // perform global update
        if (globalUpdate.frequency > 0 && (globalUpdate.frequency*histo.getN()) < acceptSinceLastChange)
        {
          char fn[256];
          snprintf(fn,255,"Dos_Global_%02d_Changes_%03d.jsn",globalUpdate.changes,modificationFactorChanges);
          writeDos(fn);
          globalUpdate.changes++;
          std::cout<<"# global update "<<globalUpdate.changes<<std::endl;
          performGlobalUpdate(dos,globalUpdate.kappa,globalUpdate.lambda,globalUpdate.omega);
          histo.clear();
          acceptSinceLastChange=0;
        }
        else if (((acceptSinceLastChange>=histo.getN()*updatesPerBin || !(changeMode &1)) &&
                  (hMin >= hMinimum || !(changeMode & 2)) &&
                  (currentFlatnessCriterion>flatnessCriterion || !(changeMode & 4)) ))
        {
          std::cout << "# level " << modificationFactorChanges << " with gamma = " << gamma << " is finished.\n";
          modificationFactorChanges++; // counter[2]

          // write dos;
          // we are using JSON as our file format
          char fn[256];
          snprintf(fn,255,"Dos_Global_%02d_Changes_%03d.jsn",globalUpdate.changes,modificationFactorChanges);
          writeDos(fn);
          // writeDos(dos_out_name.data());

          // clear the Histogram
          histo.clear();
          acceptSinceLastChange = 0;

          // change gamma
          dosKernel.scale(0.5);
          gamma = 0.5 * gamma;
          std::cout << "# level " << modificationFactorChanges << " with gamma = " << gamma << " begins.\n";
          if(statesFile != NULL)
	  {
            // char fn[256];
            snprintf(fn,255,"%s_%02d",statesFile,modificationFactorChanges);
            sw.newFile(fn);
            sw.writeHeader(gamma,n_walkers,n_spins,evecs_pointer);
          }
        }
      }
    }
    else            // histogram update mode != 0
    {
      if (acceptSinceLastChange >= histo.getN() * updatesPerBin)
      {
        acceptSinceLastChange = 0;
        for (int ix=0; ix<dos.getN(); ix++)
          dos[ix] += gamma * histo[ix];
        histo.clear();
        gamma = 0.5 * gamma;

        std::cout << "# level " << modificationFactorChanges << " with gamma = " << gamma << " begins.\n";
        if (statesFile != NULL)
        {
          char fn[256];
          snprintf(fn,255,"%s_%02d",statesFile,modificationFactorChanges);
          sw.newFile(fn);
          sw.writeHeader(gamma,n_walkers,n_spins,evecs_pointer);
        }
      }
    }
  }

  if (accepted)
  {
    // suffian: moved to routine updateLog(), see below
/*    sw.writeChange(instance, numRetentions[instance], lastAccepted[instance], &lastAcceptedEvec[3*instance], lastAcceptedEnergy[instance]);
    lastAccepted[instance] = lastChange[instance];
    lastAcceptedEnergy[instance] = position[instance];
    //lastAcceptedEnergy[instance] = energy;
    lastAcceptedEvec[  3*instance] = evecs[  3*lastChange[instance]];
    lastAcceptedEvec[1+3*instance] = evecs[1+3*lastChange[instance]];
    lastAcceptedEvec[2+3*instance] = evecs[2+3*lastChange[instance]]; */
    accept++;
    acceptSinceLastChange++;
  }
  else
    reject++;

  if (gamma < gammaFinal) return true;
  else return false;

}
template<class RNG>
void WL1dEvecGenerator<RNG>::updateLog(int instance, double *evecs, int * occ, bool accepted, MoveChoice_t MoveChoice) {

  // use static variables to keep track of the last move type
  static bool firstrun = true;
  static std::vector<MoveChoice_t> lastAcceptedMove;
  if( firstrun ) {
    lastAcceptedMove.resize(n_walkers);
    firstrun = false;
  }

  // return unless state changed
  if( !accepted ) return;

  // print output corresponding to previous state
  if( lastAcceptedMove[instance] == SpinMove )
    sw.writeChange(instance, numRetentions[instance], lastAccepted[instance], &lastAcceptedEvec[3*instance], lastAcceptedEnergy[instance]);
  else
    sw.writeChangeOcc(instance, numRetentions[instance],
        lastAcceptedSwap[instance].first,
        lastAcceptedSwap[instance].second,
        lastAcceptedOcc[instance].first,
        lastAcceptedOcc[instance].second,
        lastAcceptedEnergy[instance]);

  // remember the new configuration
  lastAcceptedEnergy[instance] = position[instance];
  if( MoveChoice == SpinMove ) {
    lastAccepted[instance] = lastChange[instance];
    //lastAcceptedEnergy[instance] = energy;
    lastAcceptedEvec[  3*instance] = evecs[  3*lastChange[instance]];
    lastAcceptedEvec[1+3*instance] = evecs[1+3*lastChange[instance]];
    lastAcceptedEvec[2+3*instance] = evecs[2+3*lastChange[instance]];
  }
  else {
    lastAcceptedSwap[instance].first  = lastSwapOcc[instance].first;
    lastAcceptedSwap[instance].second = lastSwapOcc[instance].second;
    lastAcceptedOcc[instance].first  = occ[ lastSwapOcc[instance].first ];
    lastAcceptedOcc[instance].second = occ[ lastSwapOcc[instance].second ];
  }

  lastAcceptedMove[instance] = MoveChoice;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::generatePotentialShift(int instance, double *potentialShifts, bool accepted)
{

  if (accepted)
  {
    lastChangePotentialShift[instance] = int(rnd(rng)*n_spins);
    oldPotentialShift[instance] = potentialShifts[lastChangePotentialShift[instance]];
    potentialShifts[lastChangePotentialShift[instance]] = randomPotentialShift();
  } else {
    potentialShifts[lastChangePotentialShift[instance]] = oldPotentialShift[instance];
    lastChangePotentialShift[instance] = int(rnd(rng)*n_spins);
    oldPotentialShift[instance] = potentialShifts[lastChangePotentialShift[instance]];
    potentialShifts[lastChangePotentialShift[instance]] = randomPotentialShift();
  }

}

template<class RNG>
void WL1dEvecGenerator<RNG>::generateEvec(int instance, double *evecs, bool acceptedEvec)
{
  if (acceptedEvec)
  {
    lastChange[instance] = int(rnd(rng)*n_spins);
    oldSpin[  3*instance] = evecs[  3*lastChange[instance]];
    oldSpin[1+3*instance] = evecs[1+3*lastChange[instance]];
    oldSpin[2+3*instance] = evecs[2+3*lastChange[instance]];
    random_evec(&evecs[3*lastChange[instance]]);
    numRetentions[instance] = 0;
  }
  else
  {
    evecs[  3*lastChange[instance]] = oldSpin[  3*instance];
    evecs[1+3*lastChange[instance]] = oldSpin[1+3*instance];
    evecs[2+3*lastChange[instance]] = oldSpin[2+3*instance];
    lastChange[instance] = int(rnd(rng)*n_spins);
    oldSpin[  3*instance] = evecs[  3*lastChange[instance]];
    oldSpin[1+3*instance] = evecs[1+3*lastChange[instance]];
    oldSpin[2+3*instance] = evecs[2+3*lastChange[instance]];
    random_evec(&evecs[3*lastChange[instance]]);
  }

}

// additions for Wang-Landau for alloying
// swapping atoms preserves concentration

template<class RNG>
MoveChoice_t WL1dEvecGenerator<RNG>::selectMoveType(bool isSpinSim, bool isOccSim) {

  printf("inside correct 'selectMoveType'\n");

  // choose between spin and occupancy equally
  // is this a good choice??
  if( isSpinSim && isOccSim ) {
    int c = int(rnd(rng)*2);
    if( c == 0 )
      return SpinMove
    else
      return OccupancyMove;
  }

  if( isSpinSim )
    return SpinMove;
  if( isOccSim )
    return OccupancyMove;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::setAlloyClasses(const AlloyMixingDesc& alloyDesc, int *siteclass) {
  this->alloyDesc = alloyDesc;

  siteAlloyClass.resize(n_spins);
  for(int i = 0; i < n_spins; i++)
    siteAlloyClass[i] = siteclass[i];

  // remember number of sites per alloy class
  int nclasses = alloyDesc.size();
  numSites_per_AlloyClass.resize(nclasses);
  int *nsites = numSites_per_AlloyClass.data();
  for(int i = 0; i < nclasses; i++)
    nsites[i] = 0;
  for(int i = 0; i < n_spins; i++)
    nsites[ siteAlloyClass[i] ]++;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::generateOccupancies(int instance, int *occ, bool acceptedOcc) {

  int temp, atom_i, atom_j;

  // if previous move not accepted,
  // revert occupancies
  if( !acceptedOcc ) {
    atom_i = lastSwapOcc[instance].first;
    atom_j = lastSwapOcc[instance].second;

    temp = occ[atom_i];
    occ[atom_i] = occ[atom_j];
    occ[atom_j] = temp;
  }
  else
    numRetentions[instance] = 0;

  // pick an alloy class
  int ac = int(rnd(rng)*alloyDesc.size());

  // pick two distinct atoms within mixing class
  // here we assume there are few alloy classes
  do{ atom_i = int(rnd(rng)*n_spins) }
    while( siteAlloyClass[atom_i] != ac );
  do{ atom_j = int(rnd(rng)*n_spins) }
    while( siteAlloyClass[atom_j] != ac || occ[atom_j] == occ[atom_i] );

  // swap atoms
  temp = occ[atom_i];
  occ[atom_i] = occ[atom_j];
  occ[atom_j] = temp;

  // remember which atoms were swapped
  lastSwapOcc[instance].first  = atom_i;
  lastSwapOcc[instance].second = atom_j;
}

template<class RNG>
void WL1dEvecGenerator<RNG>::generateUnsampledOcc(int inst, int *occ) {
  initializeOccupancies(inst, occ);
}

template<class RNG>
void WL1dEvecGenerator<RNG>::initializeOccupancies(int inst, int *occ) {

  printf("inside correct 'initializeOccupancies'\n");

  // determine number atoms of each component type given concentration
  std::vector< std::vector<int> > atomsLeft;
  atomsLeft.resize(alloyDesc.size());
  for(int i = 0; i < atomsLeft.size(); i++) {
    atomsLeft[i].resize(alloyDesc[i].size());
    for(int j = 0; j < atomsLeft[i].size(); j++) {

      // how many sites for this alloy class and component?
      double nfrac = alloyDesc[i][j].conc * numSites_per_AlloyClass[i];
      double error = fmod(nfrac, 1.0);

      // ensure concentrations commensurate with number of sites
      const double tol = 1.e-06;
      if( tol < error && error < 1.0-tol ) {
        printf("error: alloy concentrations are incommensurate with supercell\n");
        printf("alloy class = %d, component = %d\, concentration = %f\n",
            i+1, j+1, alloyDesc[i][j].conc);
        printf("desired sites = %f\n", nfrac);
        exit(1);
      }

      atomsLeft[i][j] = int(nfrac + 0.5);
    }
  }

  // distribute atoms across sites
  for(int i = 0; i < n_spins; i++) {

     // pick an atom from the right class
     int type, ac = siteAlloyClass[i];
     do { type = int( rnd(rng) * alloyDesc[ac].size() ) }
       while( atomsLeft[ac][type] == 0 )

     // assign to site
     occ[i] = type; atomsLeft[ac][type]--;
  }
}

#endif


// $Id$
//
//  Copyright (C) 2003-2012 greg Landrum and Rational Discovery LLC
//
//  @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#include "BitVects.h"
#include "BitOps.h"
#include <math.h>
#include <string>
#include <iostream>
#include <RDGeneral/StreamOps.h>
#include <RDGeneral/types.h>
#include <RDGeneral/Exceptions.h>
#include <sstream>
#include <cstdlib>

#include <boost/lexical_cast.hpp>

using namespace RDKit;

int getBitId(const char*& text, int format, int size, int curr) {
  PRECONDITION(text, "no text");
  int res = -1;
  if ((format == 0) ||
      ((format == 1) && (size >= std::numeric_limits<unsigned short>::max()))) {
    int tmp;
    tmp = EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)text);
    text += sizeof(tmp);
    res = tmp;
  } else if (format == 1) {  // version 16 and on bits sotred as short ints
    unsigned short tmp;
    tmp = EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(
        *(unsigned short*)text);
    text += sizeof(tmp);
    res = tmp;
  } else if (format == 2) {  // run length encoded format
    res = curr + RDKit::pullPackedIntFromString(text);
  }
  return res;
}

bool AllProbeBitsMatch(const std::string& probe, const std::string& ref) {
  return AllProbeBitsMatch(probe.c_str(), ref.c_str());
}

bool AllProbeBitsMatch(const char* probe, const char* ref) {
  PRECONDITION(probe, "no probe text");
  PRECONDITION(ref, "no probe text");
  int probeFormat = 0;
  int refFormat = 0;
  int version = 0;

  int probeSize =
      EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)probe);
  probe += sizeof(probeSize);
  if (probeSize < 0) {
    version = -1 * probeSize;
    if (version == 16) {
      probeFormat = 1;
    } else if (version == 32) {
      probeFormat = 2;
    } else {
      throw("Unknown version type for the encode bit vect");
    }
    probeSize =
        EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)probe);
    probe += sizeof(probeSize);
  }

  int refSize =
      EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)ref);
  ref += sizeof(refSize);
  if (refSize < 0) {
    version = -1 * refSize;
    if (version == 16) {
      refFormat = 1;
    } else if (version == 32) {
      refFormat = 2;
    } else {
      throw("Unknown version type for the encode bit vect");
    }
    refSize =
        EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)ref);
    ref += sizeof(refSize);
  }

  int nProbeOn =
      EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)probe);
  probe += sizeof(nProbeOn);
  int nRefOn =
      EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)ref);
  ref += sizeof(nRefOn);

  int currProbeBit = 0;
  currProbeBit = getBitId(probe, probeFormat, probeSize, currProbeBit);
  nProbeOn--;

  int currRefBit = 0;
  currRefBit = getBitId(ref, refFormat, refSize, currRefBit);
  nRefOn--;

  while (nProbeOn) {
    while (currRefBit < currProbeBit && nRefOn > 0) {
      if (refFormat == 2) currRefBit++;
      currRefBit = getBitId(ref, refFormat, refSize, currRefBit);
      nRefOn--;
    }
    if (currRefBit != currProbeBit) return false;
    if (probeFormat == 2) currProbeBit++;
    currProbeBit = getBitId(probe, probeFormat, probeSize, currProbeBit);
    nProbeOn--;
  }
  return true;
}

template <typename T1>
bool AllProbeBitsMatch(const T1& probe, const std::string& pkl) {
  const char* text = pkl.c_str();
  int format = 0;
  int nOn = 0, size, version = 0;
  size = EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)text);
  text += sizeof(size);
  if (size < 0) {
    version = -1 * size;
    if (version == 16) {
      format = 1;
    } else if (version == 32) {
      format = 2;
    } else {
      throw("Unknown version type for the encode bit vect");
    }
    size = EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)text);
    text += sizeof(size);
  }
  nOn = EndianSwapBytes<LITTLE_ENDIAN_ORDER, HOST_ENDIAN_ORDER>(*(int*)text);
  text += sizeof(nOn);

  int currBit = 0;
  currBit = getBitId(text, format, size, currBit);
  nOn--;
  std::vector<int> obl;
  probe.getOnBits(obl);

  // for(int i=0;i<probe.getNumBits();i++){
  //  if(probe.getBit(i)){
  for (std::vector<int>::const_iterator i = obl.begin(); i != obl.end(); i++) {
    while (currBit < *i && nOn > 0) {
      if (format == 2) currBit++;
      currBit = getBitId(text, format, size, currBit);
      nOn--;
    }
    if (currBit != *i) return false;
    //}
  }
  return true;
}
template bool AllProbeBitsMatch(const SparseBitVect& bv1,
                                const std::string& pkl);
template bool AllProbeBitsMatch(const ExplicitBitVect& bv1,
                                const std::string& pkl);
template <typename T1>
bool AllProbeBitsMatch(const T1& probe, const T1& ref) {
  for (unsigned int i = 0; i < probe.getNumBits(); ++i) {
    if (probe.getBit(i) && !ref.getBit(i)) {
      return false;
    }
  }
  return true;
}
template bool AllProbeBitsMatch(const SparseBitVect& bv1,
                                const SparseBitVect& bv2);
// template bool AllProbeBitsMatch(const ExplicitBitVect& bv1,const
// ExplicitBitVect &bv2);

bool AllProbeBitsMatch(const ExplicitBitVect& probe,
                       const ExplicitBitVect& ref) {
  return probe.dp_bits->is_subset_of(*(ref.dp_bits));
}

// """ -------------------------------------------------------
//
//  NumOnBitsInCommon(T1,T2)
//  Returns the number of on bits which are set in both T1 and T2.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
int NumOnBitsInCommon(const T1& bv1, const T2& bv2) {
  return static_cast<int>(OnBitsInCommon(bv1, bv2).size());
}

int NumOnBitsInCommon(const ExplicitBitVect& bv1, const ExplicitBitVect& bv2) {
  return static_cast<int>(((*bv1.dp_bits) & (*bv2.dp_bits)).count());
}

// In all these similarity metrics the notation is selected to be
//   consistent with J.W. Raymond and P. Willett, JCAMD _16_ 59-71 (2002)
// """ -------------------------------------------------------
//
//  TanimotoSimilarity(T1,T2)
//   returns the Tanamoto similarity between T1 and T2, a double.
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator&, getNumBits()
//  and getOnBits().
//
//  Python Notes: T1 and T2 are BitVects.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
double TanimotoSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();
  if ((y + z - x) == 0.0)
    return 1.0;
  else
    return x / (y + z - x);
}

template <typename T1, typename T2>
double TverskySimilarity(const T1& bv1, const T2& bv2, double a, double b) {
  RANGE_CHECK(0, a, 1);
  RANGE_CHECK(0, b, 1);
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();
  double denom = a * y + b * z + (1 - a - b) * x;
  if (denom == 0.0)
    return 1.0;
  else
    return x / denom;
}

template <typename T1, typename T2>
double CosineSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (y * z > 0.0) {
    return x / sqrt(y * z);
  } else {
    return 0.0;
  }
}

template <typename T1, typename T2>
double KulczynskiSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (y * z > 0.0) {
    return x * (y + z) / (2 * y * z);
  } else {
    return 0.0;
  }
}

template <typename T1, typename T2>
double DiceSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (y + z > 0.0) {
    return 2 * x / (y + z);
  } else {
    return 0.0;
  }
}

template <typename T1, typename T2>
double SokalSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  return x / (2 * y + 2 * z - 3 * x);
}

template <typename T1, typename T2>
double McConnaugheySimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (y * z > 0.0) {
    return (x * (y + z) - (y * z)) / (y * z);
  } else {
    return 0.0;
  }
}

template <typename T>
inline T tmin(T v1, T v2) {
  if (v1 < v2) return v1;
  return v2;
}

template <typename T>
inline T tmax(T v1, T v2) {
  if (v1 > v2) return v1;
  return v2;
}

template <typename T1, typename T2>
double AsymmetricSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (tmin(y, z) > 0) {
    return x / tmin(y, z);
  } else {
    return 0.0;
  }
}

template <typename T1, typename T2>
double BraunBlanquetSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();

  if (tmax(y, z) > 0) {
    return x / tmax(y, z);
  } else {
    return 0.0;
  }
}

template <typename T1, typename T2>
double RusselSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  return x / bv1.getNumBits();
}

template <typename T1, typename T2>
double RogotGoldbergSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  double x = NumOnBitsInCommon(bv1, bv2);
  double y = bv1.getNumOnBits();
  double z = bv2.getNumOnBits();
  double l = bv1.getNumBits();
  double d = l - y - z + x;
  if ((x == l) || (d == l))
    return 1.0;
  else
    return (x / (y + z) + (d) / (2 * l - y - z));
}

// """ -------------------------------------------------------
//
//  OnBitSimilarity(T1,T2)
//  Returns the percentage of possible on bits in common
//  between T1 and T2 (a double)
//
//  C++ Notes: T1 and T2 must support operator|, operator&
//  and getOnBits().
//
//  Python Notes: T1 and T2 are BitVects.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
double OnBitSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");

  double num = NumOnBitsInCommon(bv1, bv2);
  double denom = (bv1 | bv2).getNumOnBits();

  if (denom > 0) {
    return num / denom;
  } else {
    return 0;
  }
}

// """ -------------------------------------------------------
//
//  NumBitsInCommon(T1,T2)
//  Returns the number of bits in common (on and off)
//  between T1 and T2 (an int)
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator^, getNumBits().
//
//  Python Notes: T1 and T2 are BitVects.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
int NumBitsInCommon(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");

  return bv1.getNumBits() - (bv1 ^ bv2).getNumOnBits();
}

int NumBitsInCommon(const ExplicitBitVect& bv1, const ExplicitBitVect& bv2) {
  return bv1.getNumBits() - static_cast<int>(((*bv1.dp_bits) ^ (*bv2.dp_bits)).count());
}

// """ -------------------------------------------------------
//
//  AllBitSimilarity(T1,T2)
//  Returns the percentage of bits in common (on and off)
//  between T1 and T2 (a double)
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator^, getNumBits()
//  and getNumOnBits().
//
//  Python Notes: T1 and T2 are BitVects.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
double AllBitSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");

  return double(NumBitsInCommon(bv1, bv2)) / bv1.getNumBits();
}

// """ -------------------------------------------------------
//
//  OnBitsInCommon(T1,T2)
//  Returns the on bits which are set in both T1 and T2.
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator&, getNumBits()
//  and getOnBits(), the return value is an IntVect.
//
//  Python Notes: T1 and T2 are BitVects, the return value
//  is a tuple of ints.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
IntVect OnBitsInCommon(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  IntVect res;
  (bv1 & bv2).getOnBits(res);
  return res;
}

// """ -------------------------------------------------------
//
//  OffBitsInCommon(T1,T2)
//  Returns the off bits which are set in both T1 and T2.
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator|, operator~,
// getNumBits() and getOnBits(), the return value is an IntVect.
//
//  Python Notes: T1 and T2 are BitVects, the return value
//  is a tuple of ints.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
IntVect OffBitsInCommon(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  IntVect res;
  (~(bv1 | bv2)).getOnBits(res);
  return res;
}

// """ -------------------------------------------------------
//
//  OnBitProjSimilarity(T1,T2)
//  Returns the projected similarity between the on bits of
//  T1 and T2.
//
//  The on bit projected similarity of T1 onto T2 is the
//  percentage of T1's on bits which are on in T2.
//
//  This type of measure may be useful for substructure-type
//  searches.
//
//  Two values are returned, the projection of T1 onto T2
//  and the projection of T2 onto T1
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator&, getNumBits()
//  and getNumOnBits(), the return value is an DoubleVect with
//  two elements.
//
//  Python Notes: T1 and T2 are BitVects, the return value
//  is a 2-tuple of doubles.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
DoubleVect OnBitProjSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  DoubleVect res(2, 0.0);
  double num = NumOnBitsInCommon(bv1, bv2);
  if (num) {
    res[0] = num / bv1.getNumOnBits();
    res[1] = num / bv2.getNumOnBits();
  }
  return res;
}

// """ -------------------------------------------------------
//
//  OffBitProjSimilarity(T1,T2)
//  Returns the projected similarity between the off bits of
//  T1 and T2.
//
//  The off bit projected similarity of T1 onto T2 is the
//  percentage of T1's off bits which are off in T2.
//
//  This type of measure may be useful for substructure-type
//  searches.
//
//  Two values are returned, the projection of T1 onto T2
//  and the projection of T2 onto T1
//
//  T1 and T2 should be the same length.
//
//  C++ Notes: T1 and T2 must support operator|, getNumBits()
//  and getNumOffBits(), the return value is an DoubleVect with
//  two elements.
//
//  Python Notes: T1 and T2 are BitVects, the return value
//  is a 2-tuple of doubles.
//
// """ -------------------------------------------------------
template <typename T1, typename T2>
DoubleVect OffBitProjSimilarity(const T1& bv1, const T2& bv2) {
  if (bv1.getNumBits() != bv2.getNumBits())
    throw ValueErrorException("BitVects must be same length");
  DoubleVect res(2, 0.0);
  double num = (bv1 | bv2).getNumOffBits();
  if (num) {
    res[0] = num / bv1.getNumOffBits();
    res[1] = num / bv2.getNumOffBits();
  }
  return res;
}

template <typename T1>
T1* FoldFingerprint(const T1& bv1, unsigned int factor) {
  if (factor <= 0 || factor >= bv1.getNumBits())
    throw ValueErrorException("invalid fold factor");

  int initSize = bv1.getNumBits();
  int resSize = initSize / factor;
  T1* res = new T1(resSize);

  IntVect onBits;
  bv1.getOnBits(onBits);
  for (IntVectIter iv = onBits.begin(); iv != onBits.end(); iv++) {
    int pos = (*iv) % resSize;
    res->setBit(pos);
  }
  return res;
}

template <typename T1>
std::string BitVectToText(const T1& bv1) {
  std::string res(bv1.getNumBits(), '0');
  for (unsigned int i = 0; i < bv1.getNumBits(); i++) {
    if (bv1.getBit(i)) res[i] = '1';
  }
  return res;
}

const char bin2Hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
template <typename T1>
std::string BitVectToFPSText(const T1& bv1) {
  unsigned int size =
      2 * (bv1.getNumBits() / 8 + (bv1.getNumBits() % 8 ? 1 : 0));
  std::string res(size, 0);
  unsigned char c = 0;
  unsigned int byte = 0;
  for (unsigned int i = 0; i < bv1.getNumBits(); i++) {
    if (bv1.getBit(i)) {
      c |= 1 << (i % 8);
    }
    if (!((i + 1) % 8)) {
      res[byte++] = bin2Hex[(c >> 4) % 16];
      res[byte++] = bin2Hex[c % 16];
      c = 0;
    }
  }
  if (byte < size) {
    res[byte++] = bin2Hex[(c >> 4) % 16];
    res[byte++] = bin2Hex[c % 16];
  }
  return res;
}

template <typename T1>
std::string BitVectToBinaryText(const T1& bv1) {
  std::string res(bv1.getNumBits() / 8 + (bv1.getNumBits() % 8 ? 1 : 0), 0);
  unsigned char c = 0;
  unsigned int byte = 0;
  for (unsigned int i = 0; i < bv1.getNumBits(); i++) {
    if (bv1.getBit(i)) {
      c |= 1 << (i % 8);
    }
    if (!((i + 1) % 8)) {
      res[byte++] = c;
      c = 0;
    }
  }
  if (bv1.getNumBits() % 8) {
    res[byte] = c;
  }
  return res;
}

template <typename T1>
void UpdateBitVectFromFPSText(T1& bv1, const std::string& fps) {
  PRECONDITION(fps.length() * 4 >= bv1.getNumBits(), "bad FPS length");
  PRECONDITION(fps.length() % 2 == 0, "bad FPS length");
  unsigned int bitIdx = 0;
  char tptr[3];
  tptr[2] = (char)0;
  for (unsigned int i = 0; i < fps.size() && bitIdx < bv1.getNumBits();
       i += 2) {
    unsigned short c = 0;
    try {
      tptr[0] = fps[i];
      tptr[1] = fps[i + 1];
      c = static_cast<unsigned short>(strtol(tptr, NULL, 16));
    } catch (...) {
      std::ostringstream errout;
      errout << "Cannot convert FPS word: " << fps.substr(i, 2) << " to int";
      std::cerr << errout.str() << std::endl;
      throw ValueErrorException(errout.str());
    }
    for (unsigned int bit = 0; bit < 8 && bitIdx < bv1.getNumBits();
         ++bit, ++bitIdx) {
      if (c & (1 << bit)) bv1.setBit(bitIdx);
    }
  }
}

template <typename T1>
void UpdateBitVectFromBinaryText(T1& bv1, const std::string& fps) {
  PRECONDITION(fps.length() * 8 >= bv1.getNumBits(), "bad FPS length");
  unsigned int bitIdx = 0;
  for (unsigned int i = 0; i < fps.size() && bitIdx < bv1.getNumBits(); i++) {
    unsigned short c = fps[i];
    for (unsigned int bit = 0; bit < 8 && bitIdx < bv1.getNumBits();
         ++bit, ++bitIdx) {
      if (c & (1 << bit)) bv1.setBit(bitIdx);
    }
  }
}

template double TanimotoSimilarity(const SparseBitVect& bv1,
                                   const SparseBitVect& bv2);
template double TverskySimilarity(const SparseBitVect& bv1,
                                  const SparseBitVect& bv2, double a, double b);
template double CosineSimilarity(const SparseBitVect& bv1,
                                 const SparseBitVect& bv2);
template double KulczynskiSimilarity(const SparseBitVect& bv1,
                                     const SparseBitVect& bv2);
template double DiceSimilarity(const SparseBitVect& bv1,
                               const SparseBitVect& bv2);
template double SokalSimilarity(const SparseBitVect& bv1,
                                const SparseBitVect& bv2);
template double McConnaugheySimilarity(const SparseBitVect& bv1,
                                       const SparseBitVect& bv2);
template double AsymmetricSimilarity(const SparseBitVect& bv1,
                                     const SparseBitVect& bv2);
template double BraunBlanquetSimilarity(const SparseBitVect& bv1,
                                        const SparseBitVect& bv2);
template double RusselSimilarity(const SparseBitVect& bv1,
                                 const SparseBitVect& bv2);
template double RogotGoldbergSimilarity(const SparseBitVect& bv1,
                                        const SparseBitVect& bv2);
template double OnBitSimilarity(const SparseBitVect& bv1,
                                const SparseBitVect& bv2);
template int NumBitsInCommon(const SparseBitVect& bv1,
                             const SparseBitVect& bv2);
template double AllBitSimilarity(const SparseBitVect& bv1,
                                 const SparseBitVect& bv2);
template int NumOnBitsInCommon(const SparseBitVect& bv1,
                               const SparseBitVect& bv2);
template IntVect OnBitsInCommon(const SparseBitVect& bv1,
                                const SparseBitVect& bv2);
template IntVect OffBitsInCommon(const SparseBitVect& bv1,
                                 const SparseBitVect& bv2);
template DoubleVect OnBitProjSimilarity(const SparseBitVect& bv1,
                                        const SparseBitVect& bv2);
template DoubleVect OffBitProjSimilarity(const SparseBitVect& bv1,
                                         const SparseBitVect& bv2);

template double TanimotoSimilarity(const ExplicitBitVect& bv1,
                                   const ExplicitBitVect& bv2);
template double TverskySimilarity(const ExplicitBitVect& bv1,
                                  const ExplicitBitVect& bv2, double a,
                                  double b);
template double CosineSimilarity(const ExplicitBitVect& bv1,
                                 const ExplicitBitVect& bv2);
template double KulczynskiSimilarity(const ExplicitBitVect& bv1,
                                     const ExplicitBitVect& bv2);
template double DiceSimilarity(const ExplicitBitVect& bv1,
                               const ExplicitBitVect& bv2);
template double SokalSimilarity(const ExplicitBitVect& bv1,
                                const ExplicitBitVect& bv2);
template double McConnaugheySimilarity(const ExplicitBitVect& bv1,
                                       const ExplicitBitVect& bv2);
template double AsymmetricSimilarity(const ExplicitBitVect& bv1,
                                     const ExplicitBitVect& bv2);
template double BraunBlanquetSimilarity(const ExplicitBitVect& bv1,
                                        const ExplicitBitVect& bv2);
template double RusselSimilarity(const ExplicitBitVect& bv1,
                                 const ExplicitBitVect& bv2);
template double RogotGoldbergSimilarity(const ExplicitBitVect& bv1,
                                        const ExplicitBitVect& bv2);
template double OnBitSimilarity(const ExplicitBitVect& bv1,
                                const ExplicitBitVect& bv2);
template int NumBitsInCommon(const ExplicitBitVect& bv1,
                             const ExplicitBitVect& bv2);
template double AllBitSimilarity(const ExplicitBitVect& bv1,
                                 const ExplicitBitVect& bv2);
template IntVect OnBitsInCommon(const ExplicitBitVect& bv1,
                                const ExplicitBitVect& bv2);
template IntVect OffBitsInCommon(const ExplicitBitVect& bv1,
                                 const ExplicitBitVect& bv2);
template DoubleVect OnBitProjSimilarity(const ExplicitBitVect& bv1,
                                        const ExplicitBitVect& bv2);
template DoubleVect OffBitProjSimilarity(const ExplicitBitVect& bv1,
                                         const ExplicitBitVect& bv2);

template SparseBitVect* FoldFingerprint(const SparseBitVect&, unsigned int);
template ExplicitBitVect* FoldFingerprint(const ExplicitBitVect&, unsigned int);

template std::string BitVectToText(const SparseBitVect&);
template std::string BitVectToText(const ExplicitBitVect&);

template std::string BitVectToFPSText(const SparseBitVect&);
template std::string BitVectToFPSText(const ExplicitBitVect&);
template void UpdateBitVectFromFPSText(SparseBitVect&, const std::string&);
template void UpdateBitVectFromFPSText(ExplicitBitVect&, const std::string&);

template std::string BitVectToBinaryText(const SparseBitVect&);
template std::string BitVectToBinaryText(const ExplicitBitVect&);
template void UpdateBitVectFromBinaryText(SparseBitVect&, const std::string&);
template void UpdateBitVectFromBinaryText(ExplicitBitVect&, const std::string&);

// from here:
// http://stackoverflow.com/questions/3849337/msvc-equivalent-to-builtin-popcount
// but corrected to get the ifdef right
#ifdef _MSC_VER
#include <intrin.h>
#ifdef _WIN64
#define BUILTIN_POPCOUNT_INSTR __popcnt64
#define BUILTIN_POPCOUNT_TYPE boost::uint64_t
#else
#define BUILTIN_POPCOUNT_INSTR __popcnt
#define BUILTIN_POPCOUNT_TYPE boost::uint32_t
#endif
#else
#define BUILTIN_POPCOUNT_INSTR __builtin_popcountll
#define BUILTIN_POPCOUNT_TYPE boost::uint64_t
#endif

// the Bitmap Tanimoto and Dice similarity code is adapted
// from Andrew Dalke's chem-fingerprints code
// http://code.google.com/p/chem-fingerprints/
namespace {
static int byte_popcounts[] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
    2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
    2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
    4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
    3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
    4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
}
unsigned int CalcBitmapPopcount(const unsigned char* afp, unsigned int nBytes) {
  PRECONDITION(afp, "no afp");
  unsigned int popcount = 0;
#ifndef USE_BUILTIN_POPCOUNT
  for (unsigned int i = 0; i < nBytes; i++) {
    popcount += byte_popcounts[afp[i]];
  }
#else
  unsigned int eidx = nBytes / sizeof(BUILTIN_POPCOUNT_TYPE);
  for (unsigned int i = 0; i < eidx; ++i) {
    popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i]));
  }
  for (unsigned int i = eidx * sizeof(BUILTIN_POPCOUNT_TYPE); i < nBytes; ++i) {
    popcount += byte_popcounts[afp[i]];
  }
#endif
  return popcount;
}
double CalcBitmapTanimoto(const unsigned char* afp, const unsigned char* bfp,
                          unsigned int nBytes) {
  PRECONDITION(afp, "no afp");
  PRECONDITION(bfp, "no bfp");
  unsigned int union_popcount = 0, intersect_popcount = 0;
#ifndef USE_BUILTIN_POPCOUNT
  for (unsigned int i = 0; i < nBytes; i++) {
    union_popcount += byte_popcounts[afp[i] | bfp[i]];
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
  }
#else
  BUILTIN_POPCOUNT_TYPE eidx = nBytes / sizeof(BUILTIN_POPCOUNT_TYPE);
  for (BUILTIN_POPCOUNT_TYPE i = 0; i < eidx; ++i) {
    union_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i] |
                                           ((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
    intersect_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i] &
                                               ((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
  }
  for (BUILTIN_POPCOUNT_TYPE i = eidx * sizeof(BUILTIN_POPCOUNT_TYPE); i < nBytes; ++i) {
    union_popcount += byte_popcounts[afp[i] | bfp[i]];
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
  }
#endif
  if (union_popcount == 0) {
    return 0.0;
  }
  return (intersect_popcount + 0.0) /
         union_popcount; /* +0.0 to coerce to double */
}

double CalcBitmapDice(const unsigned char* afp, const unsigned char* bfp,
                      unsigned int nBytes) {
  PRECONDITION(afp, "no afp");
  PRECONDITION(bfp, "no bfp");
  unsigned int intersect_popcount = 0, a_popcount = 0, b_popcount = 0;

#ifndef USE_BUILTIN_POPCOUNT
  for (unsigned int i = 0; i < nBytes; i++) {
    a_popcount += byte_popcounts[afp[i]];
    b_popcount += byte_popcounts[bfp[i]];
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
  }
#else
  BUILTIN_POPCOUNT_TYPE eidx = nBytes / sizeof(BUILTIN_POPCOUNT_TYPE);
  for (BUILTIN_POPCOUNT_TYPE i = 0; i < eidx; ++i) {
    a_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i]));
    b_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
    intersect_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i] &
                                               ((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
  }
  for (BUILTIN_POPCOUNT_TYPE i = eidx * sizeof(BUILTIN_POPCOUNT_TYPE); i < nBytes; ++i) {
    a_popcount += byte_popcounts[afp[i]];
    b_popcount += byte_popcounts[bfp[i]];
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
  }
#endif

  if (a_popcount + b_popcount == 0) {
    return 0.0;
  }
  return (2.0 * intersect_popcount) / (a_popcount + b_popcount);
}

double CalcBitmapTversky(const unsigned char* afp, const unsigned char* bfp,
                         unsigned int nBytes, double ca, double cb) {
  PRECONDITION(afp, "no afp");
  PRECONDITION(bfp, "no bfp");
  unsigned int intersect_popcount = 0, acount = 0, bcount = 0;

#ifndef USE_BUILTIN_POPCOUNT
  for (unsigned int i = 0; i < nBytes; i++) {
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
    acount += byte_popcounts[afp[i]];
    bcount += byte_popcounts[bfp[i]];
  }
#else
  BUILTIN_POPCOUNT_TYPE eidx = nBytes / sizeof(BUILTIN_POPCOUNT_TYPE);
  for (BUILTIN_POPCOUNT_TYPE i = 0; i < eidx; ++i) {
    intersect_popcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i] &
                                               ((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
    acount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)afp)[i]));
    bcount += static_cast<unsigned int>(BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)bfp)[i]));
  }
  for (BUILTIN_POPCOUNT_TYPE i = eidx * sizeof(BUILTIN_POPCOUNT_TYPE); i < nBytes; ++i) {
    intersect_popcount += byte_popcounts[afp[i] & bfp[i]];
    acount += byte_popcounts[afp[i]];
    bcount += byte_popcounts[bfp[i]];
  }
#endif
  double denom = ca * acount + cb * bcount + (1 - ca - cb) * intersect_popcount;
  if (denom == 0.0) {
    return 0.0;
  }
  return intersect_popcount / denom;
}

bool CalcBitmapAllProbeBitsMatch(const unsigned char* probe,
                                 const unsigned char* ref,
                                 unsigned int nBytes) {
  PRECONDITION(probe, "no probe");
  PRECONDITION(ref, "no ref");

#ifndef USE_BUILTIN_POPCOUNT
  for (unsigned int i = 0; i < nBytes; i++) {
    if (byte_popcounts[probe[i] & ref[i]] != byte_popcounts[probe[i]]) {
      return false;
    }
  }
#else
  unsigned int eidx = nBytes / sizeof(BUILTIN_POPCOUNT_TYPE);
  for (unsigned int i = 0; i < eidx; ++i) {
    if (BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)probe)[i] &
                           ((BUILTIN_POPCOUNT_TYPE*)ref)[i]) !=
        BUILTIN_POPCOUNT_INSTR(((BUILTIN_POPCOUNT_TYPE*)probe)[i])) {
      return false;
    }
  }
  for (unsigned int i = eidx * sizeof(BUILTIN_POPCOUNT_TYPE); i < nBytes; ++i) {
    if (byte_popcounts[probe[i] & ref[i]] != byte_popcounts[probe[i]]) {
      return false;
    }
  }
#endif
  return true;
}


/*
  Author(s):      Matthew Celnik (msc37)
  Project:        sweepc (population balance solver)
  Sourceforge:    http://sourceforge.net/projects/mopssuite

  Copyright (C) 2008 Matthew S Celnik.

  File purpose:
    Implementation of the Process class declared in the
    swp_process.h header file.

  Licence:
    This file is part of "sweepc".

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

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

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  Contact:
    Dr Markus Kraft
    Dept of Chemical Engineering
    University of Cambridge
    New Museums Site
    Pembroke Street
    Cambridge
    CB2 3RA
    UK

    Email:       mk306@cam.ac.uk
    Website:     http://como.cheng.cam.ac.uk
*/

#include "swp_process.h"

#include "swp_params.h"
#include "swp_mechanism.h"
#include "swp_sprog_idealgas_wrapper.h"

#include <stdexcept>
#include <cmath>
#include <boost/random/bernoulli_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace Sweep;
using namespace Sweep::Processes;
using namespace std;

// CONSTRUCTORS AND DESTRUCTORS.

// Default constructor.
Process::Process(void)
: m_name(""), m_mech(NULL), m_a(1.0)
{
}

// Initialising constructor.
Process::Process(const Sweep::Mechanism &mech)
: m_name(""), m_mech(&mech), m_a(1.0)
{
}

// Copy constructor.
Process::Process(const Process &copy)
{
    *this = copy;
}

// Default destructor.
Process::~Process(void)
{
}

// OPERATOR OVERLOADS.

// Assigment operator.
Process &Process::operator=(const Process &rhs)
{
    if (this != &rhs) {
        m_name = rhs.m_name;
        m_mech = rhs.m_mech;

        m_a = rhs.m_a;

        // Copy reactants.
        Sprog::StoichMap::const_iterator i;
        for (i=rhs.m_reac.begin(); i!=rhs.m_reac.end(); ++i) {
            m_reac[i->first] = i->second;
        }

        // Copy products.
        for (i=rhs.m_prod.begin(); i!=rhs.m_prod.end(); ++i) {
            m_prod[i->first] = i->second;
        }
    }
    return *this;
}

// PROCESS NAME/DESCRIPTION.

// Returns the process name.
const std::string &Process::Name(void) const {return m_name;}

// Sets the process name.
void Process::SetName(const std::string &name) {m_name = name;}

// PARENT MECHANISM.

// Returns reference to parent mechanism.
const Sweep::Mechanism *const Process::Mechanism() const
{
    return m_mech;
}

// Sets the parent mechanism
void Process::SetMechanism(const Sweep::Mechanism &mech)
{
    m_mech = &mech;
}

// REACTANTS.

// Returns the reactant count.
unsigned int Process::ReactantCount() const
{
    return m_reac.size();
}

// Returns the stoichiometric reactant coefficients.
const Sprog::StoichMap &Process::Reactants(void) const
{
    return m_reac;
}

// Returns the stoichiometry of the ith reactant.
int Process::Reactants(unsigned int i) const
{
    Sprog::StoichMap::const_iterator j = m_reac.find(i);
    if (j != m_reac.end()) {
        return j->second;
    } else {
        return 0;
    }
}

// Adds a reactant to the reaction.
void Process::AddReactant(unsigned int isp, int mu)
{
    m_reac[isp] = mu;
}

// Adds a reactant given the species name.
void Process::AddReactant(const std::string &name, int mu)
{
    // Locate the species.
    unsigned int i;
    for (i=0; i!=m_mech->Species()->size(); ++i) {
        if (name.compare((*m_mech->Species())[i]->Name()) == 0) {
            // Set reactant.
            m_reac[i] = mu;
            return;
        }
    }
}

// Removes a reactant, given by name, from the reaction.
void Process::RemoveReactant(const std::string &name)
{
    // Locate the species.
    unsigned int i;
    for (i=0; i!=m_mech->Species()->size(); ++i) {
        if (name.compare((*m_mech->Species())[i]->Name()) == 0) {
            // Delete reactant.
            m_reac.erase(i);
            return;
        }
    }
}

// PRODUCTS.

// Returns the product count.
unsigned int Process::ProductCount() const
{
    return m_prod.size();
}

// Returns the stoichiometric product coefficients.
const Sprog::StoichMap &Process::Products(void) const
{
    return m_prod;
}

// Returns the stoichiometry of the ith product.
int Process::Products(unsigned int i) const
{
    Sprog::StoichMap::const_iterator j = m_prod.find(i);
    if (j != m_prod.end()) {
        return j->second;
    } else {
        return 0;
    }
}

// Adds a product to the reaction.
void Process::AddProduct(unsigned int isp, int mu)
{
    m_prod[isp] = mu;
}

/*!
 * Virtual parent class function.
 *
 * @param t     Current time of the system
 * @param dt    Time to remove particles over
 * @param sys   The system to do transport for
 * @param local_geom    Geometry of the process
 * @param rng   Random number generator
 */
void Process::PerformDT (
        const double t,
        const double dt,
        Sweep::Cell &sys,
        const Geometry::LocalGeometry1d& local_geom,
        rng_type &rng) const {
    assert(dt > 0.0);
}

// FICTICIOUS EVENTS.

/*!
 * @param[in]       majr        Majorant rate for event
 * @param[in]       truer       True rate for event
 * @param[in,out]   rng         Random number generator
 *
 * @pre     truer <= majr, if truer > majr, cerr a warning and return false
 *
 * @return      True with probability 1-truer/majr, otherwise false
 */
bool Process::Fictitious(double majr, double truer, rng_type &rng)
{
    // ensure truer <= maj, otherwise it will crash the program.
    if (truer>majr)
    {
        // if the following warning msg show frequently, a better solution ensuring truer <= maj should be considered.
        std::cerr<<"maj is still smaller than true"<<std::endl;
        return false;
    }
    typedef boost::bernoulli_distribution<double> bernoulli_distrib;
    bernoulli_distrib fictitiousDistrib(1.0 - truer/majr);
    boost::variate_generator<rng_type&, bernoulli_distrib> fictitiousGenerator(rng, fictitiousDistrib);
    const bool isFictitious = fictitiousGenerator();
    return isFictitious;
}

// READ/WRITE/COPY.

// Writes the object to a binary stream.
void Process::Serialize(std::ostream &out) const
{
    if (out.good()) {
        // Output the version ID (=0 at the moment).
        const unsigned int version = 0;
        out.write((char*)&version, sizeof(version));

        // Write the length of the name to the stream.
        unsigned int n = m_name.length();
        out.write((char*)&n, sizeof(n));

        // Write the name to the stream.
        out.write(m_name.c_str(), n);

        // Write reactant count.
        n = (unsigned int)m_reac.size();
        out.write((char*)&n, sizeof(n));

        // Write reactant stoichiometry.
        int m = 0;
        for (Sprog::StoichMap::const_iterator i=m_reac.begin(); i!=m_reac.end(); ++i) {
            // Write species ID.
            n = (unsigned int)i->first;
            out.write((char*)&n, sizeof(n));

            // Write stoichiometry.
            m = (int)i->second;
            out.write((char*)&m, sizeof(m));
        }

        // Write product count.
        n = (unsigned int)m_prod.size();
        out.write((char*)&n, sizeof(n));

        // Write product stoichiometry.
        for (Sprog::StoichMap::const_iterator i=m_prod.begin(); i!=m_prod.end(); ++i) {
            // Write species ID.
            n = (unsigned int)i->first;
            out.write((char*)&n, sizeof(n));

            // Write stoichiometry.
            m = (int)i->second;
            out.write((char*)&m, sizeof(m));
        }

        // Write scaling factor
        out.write(reinterpret_cast<const char*>(&m_a), sizeof(m_a));
    } else {
        throw invalid_argument("Output stream not ready "
                               "(Sweep, Process::Serialize).");
    }
}

// Reads the object from a binary stream.
void Process::Deserialize(std::istream &in, const Sweep::Mechanism &mech)
{
    m_mech = &mech;
    m_prod.clear();
    m_reac.clear();

    if (in.good()) {
        // Read the output version.  Currently there is only one
        // output version, so we don't do anything with this variable.
        // Still needs to be read though.
        unsigned int version = 0;
        in.read(reinterpret_cast<char*>(&version), sizeof(version));

        int m = 0;
        unsigned int n = 0, id = 0;
        char *name = NULL;

        switch (version) {
            case 0:
                // Read the length of the process name.
                in.read(reinterpret_cast<char*>(&n), sizeof(n));

                // Read the process name.
                name = new char[n];
                in.read(name, n);
                m_name.assign(name, n);
                delete [] name;

                // Read reactant count.
                in.read(reinterpret_cast<char*>(&n), sizeof(n));

                // Read reactant stoichiometry.
                for (unsigned int i=0; i!=n; ++i) {
                    // Read species ID.
                    in.read(reinterpret_cast<char*>(&id), sizeof(id));
                    // Read stoichiometry.
                    in.read(reinterpret_cast<char*>(&m), sizeof(m));
                    // Create reactant.
                    m_reac[id] = m;
                }

                // Read product count.
                in.read(reinterpret_cast<char*>(&n), sizeof(n));

                // Read product stoichiometry.
                for (unsigned int i=0; i!=n; ++i) {
                    // Read species ID.
                    in.read(reinterpret_cast<char*>(&id), sizeof(id));
                    // Read stoichiometry.
                    in.read(reinterpret_cast<char*>(&m), sizeof(m));
                    // Create product.
                    m_prod[id] = m;
                }

                // Read scaling factor
                double a;
                in.read(reinterpret_cast<char*>(&a), sizeof(a));
                SetA(a);

                break;
            default:
                throw runtime_error("Serialized version number is invalid "
                                    "(Sweep, Process::Deserialize).");
        }
    } else {
        throw invalid_argument("Input stream not ready "
                               "(Sweep, Process::Deserialize).");
    }
}

// PROTECTED HELPER FUNCTIONS.

/*!
 *@param[in]    gas     Gas phase mixture
 *
 *@return       Gas phase part of reaction rate
 */
double Process::chemRatePart(const EnvironmentInterface &gas) const
{
    double rate = 1.0;

    Sprog::StoichMap::const_iterator i;
    for (i=m_reac.begin(); i!=m_reac.end(); ++i) {
        double conc = gas.SpeciesConcentration(i->first) ;
        for (int j=0; j!=i->second; ++j) {
            rate *= conc;
        }
    }

    return rate;
}

/*!
 * Adjusts the gas-phase composition using the reactants and
 * products defined for this process.
 *
 * Think carefully about whether calling this function is a
 * good idea:  The use of this method was found to be problematic
 * in "A predictor-corrector algorithm for the coupling of stiff
 * ODEs to a particle population balance", Celnik et al, Preprint 58.
 * The requirement that the gas phase mixture be implemented via a
 * SprogIdealGasWrapper will also restrict future applications of the
 * code.  In situations where updating the gas phase during stochastic
 * particle reactions is definitely required, this is the method
 * to use.
 *
 * @param[in,out]   sys     System in which the gas phase is changing
 * @param[in]       wt      Statistical weight of reaction
 * @param[in]       n       Repeat count of reaction
 *
 * @pre      The gas phase in sys must be of type SprogIdealGasWrapper
 *
 * @exception   std::runtime_error      Could not cast gas phase to SprogIdealGasWrapper
 */
void Process::adjustGas(Cell &sys, double wt, unsigned int n) const
{
    if(!sys.FixedChem()) {
        // This method requires write access to the gas phase, which is not
        // standard in sweep.  This means it cannot use the generic gas
        // phase interface
        SprogIdealGasWrapper *gasWrapper = dynamic_cast<SprogIdealGasWrapper*>(&sys.GasPhase());
        if(gasWrapper == NULL)
            throw std::runtime_error("Could not cast gas phase to SprogIdealGasWrapper in Process::adjustGas");

        // If excecution reaches here, the cast must have been successful
        Sprog::Thermo::IdealGas *gas = gasWrapper->Implementation();

        // Get the existing concentrations
        fvector newConcs;
        gas->GetConcs(newConcs);

        // Now adjust the concentrations
        Sprog::StoichMap::const_iterator i;
        double n_NAvol = wt * (double)n / (NA * sys.SampleVolume());
        for (i=m_reac.begin(); i!=m_reac.end(); ++i)
            newConcs[i->first] -= (double)(i->second) * n_NAvol;
        for (i=m_prod.begin(); i!=m_prod.end(); ++i)
            newConcs[i->first] +=(double)(i->second) * n_NAvol;

        // Set the new data
        gas->SetConcs(newConcs);
    }
}

// Adjusts the gas-phase composition and temperature
// using the change in composition of the particle
// Currently only implemented for titania!
/*
* Same warning as in adjustGas above applies
* @param[in, out]   sys        System in which the gas phase is changing
* @param[in]        wt         Statistical weight of reaction
* @param[in]        n          Repeat count of reaction
* @param[in]        dcomp      Composition change in particle
* @param[in]        processID  Identify process as reaction (1, 2) or flow (3, 4)
*
* @pre      The gas phase in sys must be of type SprogIdealGasWrapper
*
* @exception   std::runtime_error      Could not cast gas phase to SprogIdealGasWrapper
*/
void Process::adjustParticleTemperature(Cell &sys, double wt, unsigned int n, double dcomp, int processID) const
{
	if (!sys.FixedChem())
	{
		// This method requires write access to the gas phase, which is not
		// standard in sweep.  This means it cannot use the generic gas
		// phase interface
		SprogIdealGasWrapper *gasWrapper = dynamic_cast<SprogIdealGasWrapper*>(&sys.GasPhase());

		if (gasWrapper == NULL)
			throw std::runtime_error("Could not cast gas phase to SprogIdealGasWrapper in Process::adjustGas");

		// If excecution reaches here, the cast must have been successful
		Sprog::Thermo::IdealGas *gas = gasWrapper->Implementation();

		// Function will update the temperature oldTg to temperature newTg
		double oldTg = sys.GasPhase().Temperature();
		double newTg = oldTg;

		// Thermal parameters
		// Cg, Cp, Cp * rhop and Hs/Us are stored on sys object for
		// faster computation. They are updated less frequently.
		fvector Hs, Cs;
		sys.getEnthalpies(Hs);
		double Cg = sys.getBulkHeatCapacity();
		double Cp = sys.getParticleHeatCapacity();
		bool constv = sys.IsConstV();
		int pindex = m_mech->GetParticleSpeciesIndex();
		// Set enthalpy, heat capacity and density for both phases
		// Uncomment to update properties each time this function is called.
		/*if (constv)
		{
			gas->Us(Hs);
			gas->CalcCvs(oldTg, Cs);
			Cg = gas->BulkCv();
			Cp = Cs[pindex];
		}
		else
		{
			gas->Hs(Hs);
			gas->CalcCps(oldTg, Cs);
			Cg = gas->BulkCp();
			Cp = Cs[pindex];
		}*/
		double mw = sys.ParticleModel()->Components()[0]->MolWt();
		double rhop = (sys.Particles().GetSum(iM) + sys.Particles().GetTotalMass()) / (mw * sys.SampleVolume());
		double rhog = gas->Density();
		double Cprhop = Cp * rhop;
		double Cgrhog = Cg * rhog;
		double rhog_mass = gas->MassDensity();
		double P_R = gas->Pressure() / R;
		double Hp = Hs[pindex];

		// Get the existing concentrations
		fvector newConcs;
		gas->GetConcs(newConcs);

		// Concentration change in system due to new particle(s)
		double n_NAvol = wt * (double)n / (NA * sys.SampleVolume());

		// Compute the change in concentrations and enthalpy
		double deltaHr = 0.0;
		double gdot = 0.0;
		double change = 0.0;
		if (processID == 1 || processID == 2) {                // Inception or surface growth
			Sprog::StoichMap::const_iterator i;
			for (i = m_reac.begin(); i != m_reac.end(); ++i)
			{
				change = (double)(i->second) * n_NAvol;
				newConcs[i->first] -= change;
				deltaHr -= change * Hs[i->first];
				gdot -= change;
			}
			for (i = m_prod.begin(); i != m_prod.end(); ++i)
			{
				change = (double)(i->second) * n_NAvol;
				newConcs[i->first] += change;
				deltaHr += change * Hs[i->first];
				gdot += change;
			}
			deltaHr += (dcomp * n_NAvol * Hp);                 // Contribution of particle formation
		} else if (processID == 4) {                           // Inflow - need to get (f/tau and?) Hp_in
			double Hp_in = Hp;
			deltaHr += n_NAvol * (Hp_in - Hp);
		}

		// Start to adjust the temperature by subtracting deltaHr/(rho*C)
		deltaHr *= 1.0 / (Cgrhog + Cprhop);
		newTg -= deltaHr;

		if (constv)
		{
			// a) dCk/dt = gdot_k
			// b) drho/dt = dC/dt (constant volume)
			// c) dT/dt * rho * Cv = - deltaHr_total + deltaHflow - (drho/dt * R * T)

			// Adjust the concentrations as in a) and b) above
			gas->SetConcs(newConcs);

			// Add denominator to gdot
			gdot *= 1.0 / (Cgrhog + Cprhop);

			// Finish adjusting the temperature as in c) above
			// newTg *= 1.0 / (1.0 + R * gdot);                // This way assumes T at tf
			newTg -= (R * oldTg * gdot);                       // This way assumes T at t0
			gas->SetTemperature(newTg);
		}
		else
		{
			// a) dT/dt * rho * Cv = - deltaHr_total + deltaHflow
			// b) gamma = (drho/dt / rho) + (dT/dt / T)
			// c) dCk/dt = gdot_k - gamma * Ck
			// d) rho = P / (R * T)

			// Adjust the temperature as in a) above
			// Taking rho at tf:
			//double a = Cprhop;
			//double b = (Cg * P_R) - (oldTg * Cprhop) + (deltaHr);
			//double c = -oldTg * Cg * P_R;
			//newTg = (-b + sqrt((b * b) - (4 * a * c))) / (2 * a);
			// Taking rho at t0, temperature update is already complete.
			gas->SetTemperature(newTg);

			// Compute gas-phase expansion as in b) above
			double gamma = (gdot / rhog) + ((newTg - oldTg) / oldTg);

			// Adjust the concentrations as in c) above
			fvector::const_iterator j;
			for (int j = 0; j != newConcs.size(); ++j)
				newConcs[j] -= (gamma * newConcs[j]);
			gas->SetConcs(newConcs);

			// Account for gas phase expansion (via gamma)
			sys.AdjustSampleVolume(rhog_mass / gas->MassDensity());
		}
	}
}