#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <set>

#include "mpi.h"

using namespace std;
#define Xyce_PARALLEL_MPI
 
#include "N_CIR_Xyce.h"

// #define TRACE

void resizeBuff (char * &, int, int &);
void readLine (ifstream &, string &, ofstream &);
int main(int iargs, char **cargs) {
  string inFile, outFile;
  N_CIR_Xyce *xycePtr;
  vector< string > dacNames;
  map<string, int> dacProcs;
  map<string, int> adcProcs;
  vector< string >::iterator d_i, d_end;
  map< string, vector< pair<double,double> >* > updates;
  map< string, vector< pair<double,double> > > adcVals;
  map< string, vector< pair<double,double> >* > dacValues;
  map<string,map<string,double> > ADCParamsMap;
  map< string, int > adcWidths;
  double time, step_time, actual_step_time;
  int i, j, nLine, istat, len;
  bool accept, stat;
  map< string, vector< pair<double,double> > >::iterator a_i, a_end;
  map<string,map<string,double> >::iterator am_i, am_end;
  string inputLine, lineType, arg;
  MPI_Comm xyceComm = MPI_COMM_WORLD;
  MPI_Comm myComm = MPI_COMM_WORLD;
  int myRank, numProc;
  char * buf;
  int bsize;
  int pos;
  int msize;
 
  MPI_Init( &iargs, &cargs );

  MPI_Comm_rank ( myComm, &myRank );
  MPI_Comm_size ( myComm, &numProc );

  bsize = numProc * sizeof(int);
  buf = new char[bsize];

  xycePtr = new N_CIR_Xyce(xyceComm);

  if (myRank == 0) {
    if (iargs < 3) {
      cerr << "Usage: Xyro <in_pipe> <out_pipe>" << endl;
      return 1;
    }

    inFile = cargs[1];
    outFile = cargs[2];

    if (!xycePtr) {
      cerr << "Failed to create Xyce instance" << endl;
      return 1;
    }

    ifstream inp (inFile.c_str());
    if (!inp) {
      cerr << "File '" << inFile << "' not found" << endl;
      return 1;
    }

    ofstream out (outFile.c_str());
    if (!out) {
      cerr << "File '" << outFile << "' not found" << endl;
      return 1;
    }

#ifdef TRACE
    ofstream trace ("trace.out");
#else
    ofstream trace ("/dev/null");
#endif

    out.precision(15);
    nLine = 0;
    while (1) {
      inputLine = "";
      ++nLine;
      readLine (inp, inputLine, trace);
#ifdef DEBUG
      cerr << "Line " << nLine << ": " << inputLine << endl;
#endif
      istringstream nextLine (inputLine);
      nextLine >> lineType;
      if (lineType != "Calling") {
        cerr << "Internal error at line " << nLine << " of file " << inFile <<
                ", expected: 'Calling', actual: '" << lineType << "'" << endl;
        return 1;
      }
      nextLine >> lineType;
#ifdef DEBUG
      cerr << "Calling " << lineType << " at line " << nLine << endl;
#endif
      pos = 0;
      len = lineType.size();
      resizeBuff (buf, len, bsize);
      MPI_Pack (const_cast<char *>(lineType.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
      MPI_Bcast(&len, 1, MPI_INT, 0, myComm);
      MPI_Bcast(buf, len, MPI_CHAR, 0, myComm);
      cerr << "Line Type = " << lineType << endl;
      if ( lineType == "initialize" ) {
        int ixargs;
        char **cxargs;
        ++nLine;
        readLine (inp, inputLine, trace);
        istringstream nextLine (inputLine);
        nextLine >> ixargs;
        MPI_Bcast(&ixargs, 1, MPI_INT, 0, myComm);
        cxargs = static_cast<char **>(malloc(ixargs*sizeof(char *)));
        msize = 0;
        for ( i=0 ; i<ixargs ; ++i ) {
          arg = "";
          nextLine >> arg;
          len = arg.size();
          cxargs[i] = static_cast<char *>(malloc(len+1));
          strcpy(cxargs[i],arg.c_str());
          msize += sizeof(int);
          msize += len;
        }
        MPI_Bcast(&msize, 1, MPI_INT, 0, myComm);
        resizeBuff (buf, msize, bsize);
        pos = 0;
        for ( i=0 ; i<ixargs ; ++i ) {
          len = strlen(cxargs[i]);
          MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
          MPI_Pack (cxargs[i], len, MPI_CHAR, buf, bsize, &pos, myComm);
        }
        MPI_Bcast(buf, msize, MPI_CHAR, 0, myComm);
        stat = xycePtr->initialize(ixargs, cxargs);
        for ( i=0 ; i<ixargs ; ++i ) {
          free(cxargs[i]);
        }
        free(cxargs);
        istat = stat?1:0;
        out << istat << endl;
        out << "Done" << endl;
        out.flush();
      }
      else if ( lineType == "getDACDeviceNames" ) {
        vector<int> procLens(numProc);
        vector<int> displs(numProc);
        int proc, posEnd, bs;
        dacNames.clear();
        stat = xycePtr->getDACDeviceNames(dacNames);
        for (i=0 ; i<dacNames.size() ; ++i) {
          dacProcs[dacNames[i]] = 0;
        }
        bs = 0;
        MPI_Gather(&bs, 1, MPI_INT, &procLens[0], 1, MPI_INT, 0, myComm);
        len = 0;
        for (i=0 ; i<numProc ; ++i) {
          displs[i] = len;
          len += procLens[i];
        }
        resizeBuff (buf, len, bsize);
        MPI_Gatherv(buf, 0, MPI_CHAR, buf, &procLens[0], &displs[0], MPI_CHAR, 0, myComm);
        pos = 0;
        proc = 1;
        bs = len;
        while (pos < bs) {
          if (procLens[proc] > 0) {
            posEnd = pos + procLens[proc];
            while (pos < posEnd) {
              MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
              string curr(buf+pos, len);
              pos += len;
              dacProcs[curr] = proc;
            }
          }
          else {
            ++proc;
          }
        }
        if (pos != bs) {
          cerr << "Unpacking length ERROR in updateTimeVoltagePairs" << endl;
        }
        len = dacProcs.size();
        istat = stat?1:0;
        out << len << "  " << istat << endl;
        map<string, int>::iterator dp_i, dp_end;
        dp_i = dacProcs.begin();
        dp_end = dacProcs.end();
        for ( ; dp_i != dp_end ; ++dp_i ) {
          out << (*dp_i).first << endl;
        }
        out << "Done" << endl;
        out.flush();
      }
      else if ( lineType == "getADCMap" ) {
        vector<int> procLens(numProc);
        vector<int> displs(numProc);
        int proc, posEnd, bs, numMap, numPar;
        map<string, map<string,double> >::iterator am_i, am_end; 
        map<string,double>::iterator pm_i, pm_end; 
        int numChanges, size;
        double pVal;

        ADCParamsMap.clear();
        stat = xycePtr->getADCMap(ADCParamsMap);

        bs = 0;
        MPI_Gather(&bs, 1, MPI_INT, &procLens[0], 1, MPI_INT, 0, myComm);
        len = 0;
        for (i=0 ; i<numProc ; ++i) {
          displs[i] = len;
          len += procLens[i];
        }
        resizeBuff (buf, len, bsize);
        MPI_Gatherv(buf, 0, MPI_CHAR, buf, &procLens[0], &displs[0], MPI_CHAR, 0, myComm);
        pos = 0;
        proc = 1;
        bs = len;
        while (pos < bs) {
          if (procLens[proc] > 0) {
            posEnd = pos + procLens[proc];
            while (pos < posEnd) {
              MPI_Unpack (buf, bsize, &pos, &numMap, 1, MPI_INT, myComm);
              for (i=0 ; i<numMap ; ++i) {
                MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
                string adcName(buf+pos, len);
                adcProcs[adcName] = proc;
                pos += len;
                MPI_Unpack (buf, bsize, &pos, &numPar, 1, MPI_INT, myComm);
                for (j=0 ; j<numPar ; ++j) {
                  MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
                  string parName(buf+pos, len);
                  pos += len;
                  MPI_Unpack (buf, bsize, &pos, &pVal, 1, MPI_DOUBLE, myComm);
                  ADCParamsMap[adcName][parName] = pVal;
                }
              }
            }
          }
          else {
            ++proc;
          }
        }
        if (pos != bs) {
          cerr << "Unpacking length ERROR in getADCMap" << endl;
        }

        len = ADCParamsMap.size();
        istat = stat?1:0;
        out << len << "  " << istat << endl;

        am_i = ADCParamsMap.begin();
        am_end = ADCParamsMap.end();
        for ( ; am_i != am_end ; ++am_i ) {
          size = (*am_i).second.size();
          out << (*am_i).first << "  " << size << endl;
          pm_i = (*am_i).second.begin();
          pm_end = (*am_i).second.end();
          for ( ; pm_i != pm_end ; ++pm_i ) {
            out << (*pm_i).first << "  " << (*pm_i).second << endl;
          }
        }
        out << "Done" << endl;
        out.flush();
      }
      else if ( lineType == "setADCWidths" ) {
        int numADC, iarg;
        int bs, width;
        vector<int> procLens(numProc);
        vector<int> displs(numProc);
        vector<map<string, int> > adcW(numProc);
        ++nLine;
        readLine (inp, inputLine, trace);
        istringstream nextLine (inputLine);
        nextLine >> numADC;
        for (i=0 ; i<numADC ; ++i) {
          ++nLine;
          readLine (inp, inputLine, trace);
          istringstream nextLine (inputLine);
          nextLine >> arg;
          nextLine >> iarg;
          adcW[adcProcs[arg]][arg] = iarg;
        }

        procLens[0] = 0;
        displs[0] = 0;
        map< string, int >::iterator a_i, a_end;
        for (i=1 ; i<numProc ; ++i) {
          bs = 0;
          a_i = adcW[i].begin();
          a_end = adcW[i].end();
          for ( ; a_i != a_end ; ++a_i ) {
            bs += sizeof(int);
            bs += (*a_i).first.size();
            bs += sizeof(int);
          }
          procLens[i] = bs;
          displs[i] = displs[i-1] + procLens[i-1];
        }
        MPI_Scatter(&procLens[0], 1, MPI_INT, &bs, 1, MPI_INT, 0, myComm);
        bs = displs[numProc-1] + procLens[numProc-1];
        resizeBuff (buf, bs, bsize);
        pos = 0;
        for (i=1 ; i<numProc ; ++i) {
          a_i = adcW[i].begin();
          a_end = adcW[i].end();
          for ( ; a_i != a_end ; ++a_i ) {
            len = (*a_i).first.size();
            MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
            MPI_Pack (const_cast<char *>((*a_i).first.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
            width = (*a_i).second;
            MPI_Pack (&width, 1, MPI_INT, buf, bsize, &pos, myComm);
          }
        }
        if (pos != bs) {
          cerr << "Packing length ERROR in setADCWidths" << endl;
        }
        MPI_Scatterv(buf, &procLens[0], &displs[0], MPI_CHAR, &bs, 1, MPI_CHAR, 0, myComm);

        if (adcW[0].size() > 0)
        xycePtr->setADCWidths (adcW[0]);
        out << "Done" << endl;
        out.flush();
      }
      else if ( lineType == "updateTimeVoltagePairs" ) {
        vector <map< string, vector< pair<double,double> >* > > dacV(numProc);
        int bs, iarg, proc;
        vector<int> procLens(numProc);
        vector<int> displs(numProc);
        double Time, Voltage;
        ++nLine;
        readLine (inp, inputLine, trace);
        istringstream nextLine (inputLine);
        arg = "";
        nextLine >> iarg;
        dacValues.clear();
        for ( i=0 ; i<iarg ; ++i ) {
          ++nLine;
          readLine (inp, inputLine, trace);
          istringstream nextLine (inputLine);
          nextLine >> arg;
          nextLine >> len;
          proc = dacProcs[arg];
          dacV[proc][arg] = new vector< pair<double,double> >;
          for  ( j=0 ; j<len ; ++j ) {
            ++nLine;
            readLine (inp, inputLine, trace);
            istringstream tvLine (inputLine);
            tvLine >> Time;
            tvLine >> Voltage;
            dacV[proc][arg]->push_back(pair<double,double>(Time, Voltage));
          }
        }

        procLens[0] = 0;
        displs[0] = 0;
        map< string, vector< pair<double,double> >* >::iterator dv_i, dv_end;
        for (i=1 ; i<numProc ; ++i) {
          bs = 0;
          dv_i = dacV[i].begin();
          dv_end = dacV[i].end();
          for ( ; dv_i != dv_end ; ++dv_i ) {
            bs += sizeof(int);
            bs += (*dv_i).first.size();
            bs += sizeof(int);
            bs += 2*sizeof(double)*(*dv_i).second->size();
          }
          procLens[i] = bs;
          displs[i] = displs[i-1] + procLens[i-1];
        }
        MPI_Scatter(&procLens[0], 1, MPI_INT, &bs, 1, MPI_INT, 0, myComm);
        bs = displs[numProc-1] + procLens[numProc-1];
        resizeBuff (buf, bs, bsize);
        pos = 0;
        for (i=1 ; i<numProc ; ++i) {
          dv_i = dacV[i].begin();
          dv_end = dacV[i].end();
          for ( ; dv_i != dv_end ; ++dv_i ) {
            len = (*dv_i).first.size();
            MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
            MPI_Pack (const_cast<char *>((*dv_i).first.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
            len = (*dv_i).second->size();
            MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
            for (j=0 ; j<len ; ++j) {
              Time = (*((*dv_i).second))[j].first;
              Voltage = (*((*dv_i).second))[j].second;
              MPI_Pack (&Time, 1, MPI_DOUBLE, buf, bsize, &pos, myComm);
              MPI_Pack (&Voltage, 1, MPI_DOUBLE, buf, bsize, &pos, myComm);
            }
          }
        }
        if (pos != bs) {
          cerr << "Packing length ERROR in updateTimeVoltagePairs" << endl;
        }
        MPI_Scatterv(buf, &procLens[0], &displs[0], MPI_CHAR, &bs, 1, MPI_CHAR, 0, myComm);

        xycePtr->updateTimeVoltagePairs (dacV[0]);
        out << "Done" << endl;
        out.flush();

        for (proc=0 ; proc < numProc ; ++proc) {
          dv_i = dacV[proc].begin();
          dv_end = dacV[proc].end();
          for ( ; dv_i != dv_end ; ++dv_i ) {
            delete (*dv_i).second;
          }
        }
      }
      else if ( lineType == "provisionalStep" ) {
        double maxDt, step, xStep;
        double darg;
        double Time, Voltage;
        int numADC, bs, proc, pos, posEnd;
        int iarg, istat;
        bool stat;
        map< string, vector< pair<double,double> > >::iterator av_i, av_end;
        vector<int> procLens(numProc);
        vector<int> displs(numProc);

        ++nLine;
        readLine (inp, inputLine, trace);
        istringstream nextLine (inputLine);
        nextLine >> maxDt;

        MPI_Bcast(&maxDt, 1, MPI_DOUBLE, 0, myComm);
        adcVals.clear();
        cerr << "Calling provisionalStep with maxDt = " << maxDt << endl;
        stat = xycePtr->provisionalStep ( maxDt, step, adcVals );

        bs = 0;
        MPI_Gather(&bs, 1, MPI_INT, &procLens[0], 1, MPI_INT, 0, myComm);
        len = 0;
        for (i=0 ; i<numProc ; ++i) {
          displs[i] = len;
          len += procLens[i];
        }
        resizeBuff (buf, len, bsize);
        MPI_Gatherv(buf, 0, MPI_CHAR, buf, &procLens[0], &displs[0], MPI_CHAR, 0, myComm);
        pos = 0;
        proc = 1;
        bs = len;
        while (pos < bs) {
          if (procLens[proc] > 0) {
            posEnd = pos + procLens[proc];
            while (pos < posEnd) {
              MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
              string adcName(buf+pos, len);
              pos += len;
              MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
              adcVals[adcName].resize(len);
              for (i=0 ; i<len ; ++i) {
                MPI_Unpack (buf, bsize, &pos, &Time, 1, MPI_DOUBLE, myComm);
                MPI_Unpack (buf, bsize, &pos, &Voltage, 1, MPI_DOUBLE, myComm);
                adcVals[adcName][i] = pair<double,double>(Time, Voltage);
              }
            }
          }
          else {
            ++proc;
          }
        }
        if (pos != bs) {
          cerr << "Unpacking length ERROR in provisionalStep" << endl;
        }

        istat = stat?1:0;
        iarg = adcVals.size();
        out << step << "  " << iarg << "  " << istat << endl;
        av_i = adcVals.begin();
        av_end = adcVals.end();
        for ( ; av_i != av_end ; ++av_i ) {
          iarg = (*av_i).second.size();
          out << (*av_i).first << "  " << iarg << endl;
          for ( i=0 ; i<iarg ; ++i ) {
            out << (*av_i).second[i].first << "  " << (*av_i).second[i].second << endl;
          }
        }

        out << "Done" << endl;
        out.flush();
      }
      else if ( lineType == "acceptProvisionalStep" ) {
        xycePtr->acceptProvisionalStep();
      }
      else if ( lineType == "rejectProvisionalStep" ) {
        xycePtr->rejectProvisionalStep();
      }
      else if ( lineType == "destructor" ) {
        delete xycePtr;
        inp.close();
        out << "Done" << endl;
        out.close();
        trace.close();
        break;
      }
      else if (lineType == "finalize") {
        MPI_Finalize();
        return 0;
      }
      else {
        cerr << "Internal error at line " << nLine << " of file " << inFile <<  
                ", unknown type: " << lineType << endl;
        return 1;
      }
    }
  }
  else {
    while (1) {
      MPI_Bcast(&len, 1, MPI_INT, 0, myComm);
      MPI_Bcast(buf, len, MPI_CHAR, 0, myComm);
      lineType = string(buf, len);
      if ( lineType == "initialize" ) {
        int ixargs;
        char **cxargs;
        MPI_Bcast(&ixargs, 1, MPI_INT, 0, myComm);
        cxargs = static_cast<char **>(malloc(ixargs*sizeof(char *)));
        MPI_Bcast(&msize, 1, MPI_INT, 0, myComm);
        resizeBuff (buf, msize, bsize);
        MPI_Bcast(buf, msize, MPI_CHAR, 0, myComm);
        pos = 0;
        for ( i=0 ; i<ixargs ; ++i ) {
          MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
          string curr(buf+pos, len);
          pos += len;
          cxargs[i] = static_cast<char *>(malloc(len+1));
          strcpy(cxargs[i],curr.c_str());
        }
        stat = xycePtr->initialize(ixargs, cxargs);
        for ( i=0 ; i<ixargs ; ++i ) {
          free(cxargs[i]);
        }
        free(cxargs);
      }
      else if ( lineType == "getDACDeviceNames" ) {
        dacNames.clear();
        stat = xycePtr->getDACDeviceNames(dacNames);
        int numDac = dacNames.size();
        int bs = 0;
        int nd;
        for (nd=0 ; nd<numDac ; ++nd) {
          bs += sizeof(int);
          bs += dacNames[nd].size();
        }
        MPI_Gather(&bs, 1, MPI_INT, buf, numProc, MPI_INT, 0, myComm);
        resizeBuff (buf, bs, bsize);
        pos = 0;
        for (nd=0 ; nd<numDac ; ++nd) {
          len = dacNames[nd].size();
          MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
          MPI_Pack (const_cast<char *>(dacNames[nd].c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
        }
        if (pos != bs) {
          cerr << "Packing length ERROR in getDACDeviceNames" << endl;
        }
        MPI_Gatherv(buf, bs, MPI_CHAR, buf, 0, 0, MPI_CHAR, 0, myComm);
      }
      else if ( lineType == "getADCMap" ) {
        ADCParamsMap.clear();
        stat = xycePtr->getADCMap(ADCParamsMap);
        int numAdc = ADCParamsMap.size();
        int bs = 0;
        int np;
        double pVal;
        map<string,map<string,double> >::iterator apm_i, apm_end;
        map<string,double>::iterator pm_i, pm_end;
        bs += sizeof(int);
        apm_i = ADCParamsMap.begin();
        apm_end = ADCParamsMap.end();
        for ( ; apm_i != apm_end ; ++apm_i ) {
          bs += sizeof(int);
          bs += (*apm_i).first.size();
          bs += sizeof(int);
          np = (*apm_i).second.size();
          pm_i = (*apm_i).second.begin();
          pm_end = (*apm_i).second.end();
          for ( ; pm_i != pm_end ; ++pm_i ) {
            bs += sizeof(int);
            bs += (*pm_i).first.size();
            bs += sizeof(double);
          }
        }
        MPI_Gather(&bs, 1, MPI_INT, buf, numProc, MPI_INT, 0, myComm);
        resizeBuff (buf, bs, bsize);
        pos = 0;
        len = ADCParamsMap.size();
        MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
        apm_i = ADCParamsMap.begin();
        apm_end = ADCParamsMap.end();
        for ( ; apm_i != apm_end ; ++apm_i ) {
          len = (*apm_i).first.size();
          MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
          MPI_Pack (const_cast<char *>((*apm_i).first.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
          np = (*apm_i).second.size();
          MPI_Pack (&np, 1, MPI_INT, buf, bsize, &pos, myComm);
          pm_i = (*apm_i).second.begin();
          pm_end = (*apm_i).second.end();
          for ( ; pm_i != pm_end ; ++pm_i ) {
            len = (*pm_i).first.size();
            MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
            MPI_Pack (const_cast<char *>((*pm_i).first.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
            pVal = (*pm_i).second;
            MPI_Pack (&pVal, 1, MPI_DOUBLE, buf, bsize, &pos, myComm);
          }
        }
        if (pos != bs) {
          cerr << "Packing length ERROR in getADCMap" << endl;
        }
        MPI_Gatherv(buf, bs, MPI_CHAR, buf, 0, 0, MPI_CHAR, 0, myComm);
      }
      else if ( lineType == "setADCWidths" ) {
        int bs, adcW;
        MPI_Scatter(buf, 1, MPI_INT, &bs, 1, MPI_INT, 0, myComm);
        resizeBuff (buf, bs, bsize);
        MPI_Scatterv(NULL, NULL, NULL, MPI_CHAR, buf, bs, MPI_CHAR, 0, myComm);
        if (bs > 0) {
          adcWidths.clear();
          pos = 0;
          while (pos < bs) {
            MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
            string curr(buf+pos, len);
            pos += len;
            MPI_Unpack (buf, bsize, &pos, &adcW, 1, MPI_INT, myComm);
            adcWidths[curr] = adcW;
          }
          if (pos != bs) {
            cerr << "Unpacking length ERROR in setADCWidths" << endl;
          }
          xycePtr->setADCWidths (adcWidths);
/*
          map<string, int>::iterator aw_i, aw_end;
          aw_i = adcWidths.begin();
          aw_end = adcWidths.end();
          for ( ; aw_i != aw_end ; ++aw_i) {
            cerr  << "Proc " << myRank << " ADC: " << (*aw_i).first << " has width " <<
                     (*aw_i).second << endl;
          }
*/
        }
      }
      else if ( lineType == "updateTimeVoltagePairs" ) {
        int bs;
        double Time, Voltage;
        MPI_Scatter(buf, 1, MPI_INT, &bs, 1, MPI_INT, 0, myComm);
        resizeBuff (buf, bs, bsize);
        MPI_Scatterv(NULL, NULL, NULL, MPI_CHAR, buf, bs, MPI_CHAR, 0, myComm);
        if (bs > 0) {
          pos = 0;
          dacValues.clear();
          while (pos < bs) {
            MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
            string adcName(buf+pos, len);
            pos += len;
            MPI_Unpack (buf, bsize, &pos, &len, 1, MPI_INT, myComm);
            dacValues[adcName] = new vector< pair<double,double> > (len);
            for (i=0 ; i<len ; ++i) {
              MPI_Unpack (buf, bsize, &pos, &Time, 1, MPI_DOUBLE, myComm);
              MPI_Unpack (buf, bsize, &pos, &Voltage, 1, MPI_DOUBLE, myComm);
              (*dacValues[adcName])[i] = pair<double,double>(Time, Voltage);
            }
          }
          if (pos != bs) {
            cerr << "Unpacking length ERROR in updateTimeVoltagePairs" << endl;
          }
          xycePtr->updateTimeVoltagePairs (dacValues);
          map< string, vector< pair<double,double> >* >::iterator dv_i, dv_end;
          dv_i = dacValues.begin();
          dv_end = dacValues.end();
          for ( ; dv_i != dv_end ; ++dv_i ) {
            cerr << "(" << (*dv_i).second->size() << ") T,V pairs for " << (*dv_i).first << endl;
            for (i=0 ; i<(*dv_i).second->size() ; ++i) {
              cerr << (*(*dv_i).second)[i].first << " :: " << (*(*dv_i).second)[i].second << endl;
            }
            delete (*dv_i).second;
          }
        }
      }
      else if ( lineType == "provisionalStep" ) {
        double maxDt, step;
        double Time, Voltage;
        int bs, pos, i;
        map< string, vector< pair<double,double> > >::iterator av_i, av_end;

        MPI_Bcast(&maxDt, 1, MPI_DOUBLE, 0, myComm);
        adcVals.clear();
        cerr << "Calling provisionalStep with maxDt = " << maxDt << endl;
        stat = xycePtr->provisionalStep ( maxDt, step, adcVals );

        bs = 0;
        av_i = adcVals.begin();
        av_end = adcVals.end();
        for ( ; av_i != av_end ; ++av_i ) {
          bs += sizeof(int);
          bs += (*av_i).first.size();
          bs += sizeof(int); 
          bs += 2*sizeof(double)*(*av_i).second.size();
        }
        MPI_Gather(&bs, 1, MPI_INT, buf, numProc, MPI_INT, 0, myComm);
        resizeBuff (buf, bs, bsize);

        pos = 0;
        av_i = adcVals.begin();
        av_end = adcVals.end();
        for ( ; av_i != av_end ; ++av_i ) {
          len = (*av_i).first.size();
          MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
          MPI_Pack (const_cast<char *>((*av_i).first.c_str()), len, MPI_CHAR, buf, bsize, &pos, myComm);
          len = (*av_i).second.size();
          MPI_Pack (&len, 1, MPI_INT, buf, bsize, &pos, myComm);
          for (j=0 ; j<len ; ++j) {
            Time = (*av_i).second[j].first;
            Voltage = (*av_i).second[j].second;
            MPI_Pack (&Time, 1, MPI_DOUBLE, buf, bsize, &pos, myComm);
            MPI_Pack (&Voltage, 1, MPI_DOUBLE, buf, bsize, &pos, myComm);
          }
        }
        if (pos != bs) {
          cerr << "Packing length ERROR in provisionalStep" << endl;
        }
        MPI_Gatherv(buf, bs, MPI_CHAR, buf, 0, 0, MPI_CHAR, 0, myComm);
      }
      else if ( lineType == "acceptProvisionalStep" ) {
        xycePtr->acceptProvisionalStep();
      }
      else if ( lineType == "rejectProvisionalStep" ) {
        xycePtr->rejectProvisionalStep();
      }
      else if ( lineType == "destructor" ) {
      }
      else if (lineType == "finalize") {
        MPI_Finalize();
        return 0;
      }
      else {
        cerr << "Internal error at line " << nLine << " of file " << inFile <<  
                ", unknown type: " << lineType << endl;
        return 1;
      }
    }
  }

  return 0;
}

void resizeBuff (char * & b, int ms, int & bs) {
  if (ms > bs) {
    delete b;
    b = new char[ms];
    bs = ms;
  }
}

void readLine (ifstream &inp, string &line, ofstream &trace) {
#ifdef TRACE
  string myLine;
  getline (inp, myLine);
  line = myLine;
  trace << myLine << endl;
#else
  getline (inp, line);
#endif
}
