/***************************************************************************
**
**  This file is part of QGpCompatibility.
**
**  QGpCompatibility is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  QGpCompatibility 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 General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2004-04-28
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>
#include <QGpCoreWave.h>
#include "CompatInversionReport.h"
#include "CompatEllipticityData.h"
#include "CompatDispersionData.h"

namespace QGpCompatibility {

#define FILE_TAG "NA-OS,inversion_report,Marc Wathelet,2002\0"
#define TAG_LENGTH 42 // including terminating NULL character
#define CURRENT_VERSION 5
// VERSION 3 includes the weight for goal data
// VERSION 4 NA generated random values are stored together with the misfit
// VERSION 5 ellipticity with f0 and devf0
#define MAX_HEADER_BLOCKS_COUNT 5

//-----------------------------------------------------------------------------------------------------------------------------
//
//  1st part: general
//
//-----------------------------------------------------------------------------------------------------------------------------

CompatInversionReport::CompatInversionReport(bool isWrite, QString reportFile, QString naInFile,int nModels)
{
  TRACE;
  // in case of I/O error these members should be correctly initialized
  _maxDispOmegaCount=0;
  _maxEllOmegaCount=0;
  _headerBlocksCount=4;
  _paramAddresses=0;
  _currentModel=0;
  _currentNAModel=0;
  _currentDispersion=0;
  _currentEllipticity=0;
  _ptrStart=0;
  _offsetTable=0;
  _trueN=0; 
  QDir d;
  _fileName=d.absoluteFilePath(reportFile);
  QFile *f=new QFile(reportFile);
  if(isWrite) {
    if(!f->open(QIODevice::ReadWrite | QIODevice::Truncate)) {
      Message::critical(MSG_ID,"Inversion report", QString("file not found %1, or error writing to file").
                arg(reportFile),Message::cancel());
      _valid=false;
      return;
    }
    _s.setDevice(f);
    _nd=0;
    _currentID=0;
    _version=CURRENT_VERSION ;
    _blockParam=1;
    _blockOmegas=2;
    _blockGoal=3;
    _blockEll=4;
    // Add one more model in case of ellipticity
    if(!naInFile.isNull()) setNFromNaIn(naInFile); else _n=MAX_HEADER_BLOCKS_COUNT;
    _n+=nModels;
    if(_n<=0) {
      printf("No max number of models specified, setting it by default to 1000\n");
      _n=1000;
    }
    _offsetTable=new qint64 [_n];
    writeHeader();
    _currentIndex=-1;
  }
  else {
    if(!f->open(QIODevice::ReadWrite)) {
      Message::critical(MSG_ID,"Inversion report", QString("file not found %1, or error writing to file").
                arg(reportFile),Message::cancel());
      _valid=false;
      return;
    }
    _s.setDevice(f);
    if(!isRightFormat()) {
      Message::critical(MSG_ID,"Inversion report", QString("Bad file tag for %1, should be %2").
              arg(reportFile).arg(FILE_TAG),Message::cancel());
      _valid=false;
      return;
    }
    _s.device()->seek(TAG_LENGTH);
    _s >> _n;
    _offsetTable=new qint64 [_n];
    _trueN=countModels()+_headerBlocksCount;
    // Read the version, if block does not exist, it is version 0
    if(startReadingBlock(0,Information)) {
      _s >> _version;
      if(_version>CURRENT_VERSION) {
        Message::critical(MSG_ID,"Inversion report", QString("Bad file version (current=%1): %2, %3").
                arg(CURRENT_VERSION).arg(reportFile).arg(_version),Message::cancel());
        _valid=false;
        return;        
      }
      _blockParam=1;
      _blockOmegas=2;
      _blockGoal=3;
      _blockEll=4;
    }
    else {
      _version=0;
      _blockParam=0;
      _blockOmegas=1;
      _blockGoal=2;
      _blockEll=3;
    }
    if(!loadParamAddresses()) {
      _valid=false;
      return;
    }
    if(currentEllipticity()!=0) _headerBlocksCount++;
    _currentIndex=-1;
  }
  _valid=true;
}

CompatInversionReport::~CompatInversionReport()
{
  TRACE;
  delete _s.device();
  delete [] _paramAddresses;
  delete _currentModel;
  delete [] _currentNAModel;
  delete _currentDispersion;
  delete _currentEllipticity;
  delete [] _offsetTable;
}

bool CompatInversionReport::isA(const char * className)
{
  TRACE;
  return strcmp(className,"CompatInversionReport")==0;
}

CompatInversionReport::BlockType CompatInversionReport::reportType()
{
  _s.device()->seek(TAG_LENGTH+sizeof(int)*(_blockGoal+1));
  qint32 tmp;
  _s >> tmp;
  _ptrStart=tmp;
  if(_ptrStart) {
    _s.device()->seek(_ptrStart);
    int blockType=0;
    _s >> blockType;
    _ptrStart=0;
    return (BlockType)blockType;
  }
  else return Unknown;
}

bool CompatInversionReport::initCurrentModel()
{
  TRACE;
  // Get number of layers per model
  // Get Ptr to the first model
  if(startReadingBlock(_headerBlocksCount,ModelDispersion)) {
    int nLayers;
    _s >> nLayers; // ID unused here
    _s >> nLayers;
    _currentModel=new Seismic1DModel(nLayers);
    return true;
  }
  else {
    Message::critical(MSG_ID,"Inversion report", "Bad block for first ModelDispersion",Message::cancel());
    return false;
  }
}

bool CompatInversionReport::initCurrentEllipticity()
{
  TRACE;
  ASSERT(!_currentEllipticity);
  _currentEllipticity=new CompatEllipticity();
  if(startReadingBlock(_blockOmegas,Omegas)) {
    _currentEllipticity->reportToOmega(_s);
    return true;
  }
  else {
    Message::critical(MSG_ID,"Inversion report", "Bad block: omegas at index 1",Message::cancel());
    return false;
  }    
}

bool CompatInversionReport::isReady(QString reportFile)
{
  TRACE;
  QFile f(reportFile);
  if(!f.open(QIODevice::ReadOnly)) return false;
  QDataStream s(&f);
  
  char buf [TAG_LENGTH];
  s.readRawData(buf,TAG_LENGTH);
  if(strcmp(buf,FILE_TAG)) return false;
  
  int ptrStart;
  f.seek(TAG_LENGTH+sizeof(int)*(MAX_HEADER_BLOCKS_COUNT+1));
  s >> ptrStart;
  if(ptrStart) {
    int blockType=0;
    f.seek(ptrStart);
    s >> blockType;
    return (blockType==ModelDispersion);
  }
  else return false;
}

bool CompatInversionReport::isRightFormat(QString reportFile)
{
  TRACE;
  QFile f(reportFile);
  if(!f.open(QIODevice::ReadOnly)) return false;
  QDataStream s(&f);
  
  char buf [TAG_LENGTH];
  s.readRawData(buf,TAG_LENGTH);
  if(strcmp(buf,FILE_TAG)) return false;
  return true;
}

bool CompatInversionReport::isRightFormat()
{
  TRACE;
  char buf [TAG_LENGTH];
  _s.readRawData(buf,TAG_LENGTH);
  return !strcmp(buf,FILE_TAG);
}

QString CompatInversionReport::reportName()
{
  TRACE;
  QFileInfo f(_fileName);
  return f.baseName();
}

QString CompatInversionReport::reportFileName()
{
  TRACE;
  return _fileName;
}

void CompatInversionReport::flush()
{
  TRACE;
  if(!_flushed)
  {
    static_cast<QFile *>(_s.device())->flush();
    _flushed=true;
  }
}
/////////////////////////////////////////////////////////////////
// Write functions to construct the report file

bool CompatInversionReport::startWritingBlock(int index,BlockType wantedType)
{
  TRACE;
  if(_ptrStart) 
  {
    printf("You must call endWritingBlock before calling startWritingBlock\n");
    return false;
  }
  _s.device()->seek(TAG_LENGTH+sizeof(int)*(index+1));
  qint32 tmp;
  _s >> tmp;
  _ptrStart=tmp;
  if(_ptrStart) {
    int blockType;
    _s.device()->seek(_ptrStart);
    _s >> blockType;
    _ptrStart=-index-1;
#ifdef Q_OS_WIN
    // Under Windows it seems that there some difference between read and write
    // pointers, the next line update the write pointer 
    _s.device()->seek(_s.device()->pos());
#endif
    return (blockType==wantedType);
  }
  return false;
}

bool CompatInversionReport::startWritingBlock(BlockType wantedType)
{
  TRACE;
  ASSERT(!_ptrStart);
  if(_trueN<_n) {
    _s.device()->seek(_s.device()->size());
    _ptrStart=_s.device()->pos();
    _s << wantedType;
  }
  else return false;
  return true;
}

void CompatInversionReport::endWritingBlock()
{
  TRACE;
  // effectively writes the block to disk (we are sure that the block is really
  // written on the disk before the pointer in the table.
  static_cast<QFile *>(_s.device())->flush();
  if(_ptrStart>0) {
    _s.device()->seek((_trueN+1)*sizeof(int)+TAG_LENGTH);
    _s << (qint32)_ptrStart;
    _offsetTable[_trueN]=_ptrStart;
    _trueN++;
  }
  else if(_ptrStart<0)
  {
    // Tests if size of block has changed, if it is the last block ok, else error
    int index=-_ptrStart-1;
    if(index!=_n-1)
    {
      qint32 tmp;
      qint64 ptrEnd;
      qint64 lastPtr=_s.device()->pos();
      _s.device()->seek(TAG_LENGTH+sizeof(int)*(index+2));
      _s >> tmp;
      ptrEnd=tmp;
      if(ptrEnd && ptrEnd<lastPtr)
        printf("You write over existing data, the file may be corrupted\n");
    }
  }
  else 
  {
    printf("startWritingBlock did not execute correctly, you should call it before endWritingBlock\n");
  }
  _ptrStart=0;
}

void CompatInversionReport::setNFromNaIn(QString naInfile)
{
  TRACE;
  // Read na.in to get the total number of models that will be generated
  FILE * f;
  f=fopen(naInfile.toLatin1().data(),"r");
  int itmax=10,nsamplei=10,nsample=10;
  if(f) {
    int buflen=256;
    char * buf=new char [buflen];
    File::readLine(buf, buflen, f); // comment line
    File::readLine(buf, buflen, f); // comment line
    File::readLine(buf, buflen, f); // comment line
    File::readLine(buf, buflen, f); // Perform NA or MC ?
    File::readLine(buf, buflen, f); // maximum number of iterations
    sscanf(buf,"%i", &itmax);
    File::readLine(buf, buflen, f); // sample size for initial population
    sscanf(buf,"%i", &nsamplei);
    File::readLine(buf, buflen, f); // sample size for remaining populations
    sscanf(buf,"%i", &nsample);
    fclose(f);
    delete [] buf;
  }
  // Calculate the number of models
  // Add one more model in case of ellipticity
  _n=nsamplei+itmax*nsample+MAX_HEADER_BLOCKS_COUNT;
}

void CompatInversionReport::writeHeader()
{
  TRACE;
  // Reset file ptr to the beginning of file
  _s.device()->seek(0);
  // Write tag (TAG_LENGTH-1 bytes)
  _s.writeRawData(FILE_TAG,TAG_LENGTH);
  // max number of binary blocks (1 for parameters+
  //                              1 for omegas+
  //                              1 for goal dispersion+
  //                              n models)
  // may be less if process stopped
  _s << _n;   
  // initialize table to null
  int a=0;
  for(int i=0;i<_n;i++) _s << a;
  static_cast<QFile *>(_s.device())->flush();
  // initialize information block (any information can be added, changing the version)
  startWritingBlock(Information);
  _s << (int) _version; // Version
  _s << (int) 0; // space for the max number of omegas in all dispersion blocks
  _s << (int) 0; // space for the max number of omegas in all ellipticities blocks
  endWritingBlock();  
  // initialize parameter table with nd (parameters count) to null
  startWritingBlock(Parameters);  
  _s << a;
  endWritingBlock();
}

void CompatInversionReport::addParameter(paramID which,int layer)
{
  TRACE;
  // Description of parameters: 
  // - number of parameters (=nd) (initialized to 0 by writeHeader())
  // for each parameter:
  // - (0=H, 1=Vs, 2=Vp, 3=Rho) [enum paramID]
  // - layer number
  // (Must be set in file "init_param.cpp")
  
  _nd++;
  if(startWritingBlock(_blockParam,Parameters)) {
    // update nd
    _s << _nd;
    // move to correct parameter
    _s.device()->seek((_nd-1)*2*sizeof(int)+_s.device()->pos());
    // add informations on this parameter
    _s << which;
    _s << layer;
  }
  else printf("Error in writing the list of parameters to the inversion report\n");
  endWritingBlock();
}


void CompatInversionReport::addEllipticityGoal(CompatEllipticityData * ell)
{
  TRACE;
  // tests if omegas are entered
  if(!startReadingBlock(_blockOmegas,Omegas))  {
    printf("You must call addOmegas before calling addDispersionGoal\n");
    return;
  }
  
  startWritingBlock(EllipticityGoal);  
  if(ell) {
    ell->dataToReport(_s);
    ell->f0ToReport(_s);
    _headerBlocksCount++;
  }
  endWritingBlock();
}

void CompatInversionReport::modifyEllipticityGoal(CompatEllipticityData * ell)
{
  TRACE;
  startWritingBlock(_blockEll,EllipticityGoal);  
  if(ell) {
    ell->dataToReport(_s);
    ell->f0ToReport(_s);
  }
  endWritingBlock();
}

void CompatInversionReport::setCostValue(int imodel,double costValue)
{
  TRACE;
  if(startWritingBlock(imodel+_headerBlocksCount,ModelDispersion))
  {
    _s >> _currentID;
    int n; // Skip the layered model
    _s >> n;
    _s.device()->seek((4*n-1)*sizeof(double)+_s.device()->pos());
    _s << costValue;
  } else fprintf(stderr,"Bad model index, when rewriting a block\n");
  endWritingBlock(); 
}

void CompatInversionReport::setMaxOmegasCount(int dispCount, int ellCount)
{
  TRACE;
  if(dispCount>_maxDispOmegaCount) {
    _maxDispOmegaCount=dispCount;
    if(ellCount>_maxEllOmegaCount) {
      _maxEllOmegaCount=ellCount;
      startWritingBlock(0,Information);  
      _s.device()->seek(_s.device()->pos()+sizeof(int));
      _s << _maxDispOmegaCount;
      _s << _maxEllOmegaCount;
      endWritingBlock();
    }
    else {
      startWritingBlock(0,Information);  
      _s.device()->seek(_s.device()->pos()+sizeof(int));
      _s << _maxDispOmegaCount;
      endWritingBlock();     
    }
  }
  else {
    if(ellCount>_maxEllOmegaCount) {
      _maxEllOmegaCount=ellCount;
      startWritingBlock(0,Information);  
      _s.device()->seek(_s.device()->pos()+sizeof(int)*2);
      _s << _maxEllOmegaCount;
      endWritingBlock();
    }
  }
}

void CompatInversionReport::maxOmegasCount(int& dispCount, int& ellCount)
{
  TRACE;
  dispCount=omegasCount();
  ellCount=dispCount;
  if(_version>=1) {
    if(startReadingBlock(0,Information)) {
      _s.device()->seek(_s.device()->pos()+sizeof(int));
      _s >> _maxDispOmegaCount;
      _s >> _maxEllOmegaCount;
      dispCount+=_maxDispOmegaCount;
      ellCount+=_maxEllOmegaCount;
    }
    else fprintf(stderr," ### ERROR ### : cannot read information block");
  }
}

/////////////////////////////////////////////////////////////////
// Function to read the report file

int CompatInversionReport::countModels()
{
  TRACE;
  if(_trueN<_n) {
    ASSERT (_offsetTable);
    int tmp;
    qint64 * it=_offsetTable+_trueN;
    _s.device()->seek(TAG_LENGTH+sizeof(int)*(_trueN+1));
    for(int i=_trueN;i<_n;i++,it++) {
      _s >> tmp;
      *it=tmp;
      if(!*it) {
        _trueN=i;
        return _trueN-_headerBlocksCount;
      }
    }
    _trueN=_n;
  }
  return _trueN-_headerBlocksCount;
}

bool CompatInversionReport::loadParamAddresses()
{
  TRACE;
  if(startReadingBlock(_blockParam,Parameters)) {
    _s >> _nd;
    _paramAddresses=new paramAddress [_nd];
    for(int i=0;i<_nd;i++) {
      paramAddress& a=_paramAddresses[i];
      int tmp;
      _s >> tmp;
      a.which=(paramID)tmp;
      _s >> a.layer;
    }
    return true;
  }
  else {
    Message::critical(MSG_ID,"Inversion report", "Bad block: Parameters at index 0",Message::cancel());
    return false;
  }    
}

bool CompatInversionReport::isSameParameter(const CompatInversionReport * otherModels) const
{
  TRACE;
  if(_nd!=otherModels->_nd) return false;
  for(int i=0;i<_nd;i++) {
    paramAddress& a=_paramAddresses[i];
    paramAddress& b=otherModels->_paramAddresses[i];
    if(a.which!=b.which || a.layer!=b.layer) return false;
  }
  return true;
}

QString CompatInversionReport::parameterName(int paramIndex)
{
  TRACE;
  paramAddress& a=_paramAddresses[paramIndex];
  QString str;
  switch (a.which) {
  case H:
    str="H%1";
    break;
  case Vs:
    str="Vs%1";
    break;
  case Vp:
    str="Vp%1";
    break;
  case Rho:
    str="Rho%1";
    break;
  case Depth:
    str="Depth%1";
    break;
  case ExpGrad:
    str="Power law exp %1";
    break;
  case ModeStdDev:
    str="Std dev %1";
    break;
  case NAParam:
    str="NA %1";
    break;
  }
  return str.arg(a.layer);
}

double CompatInversionReport::getParamValue(int paramIndex)
{
  TRACE;
  paramAddress& a=_paramAddresses[paramIndex];
  if(a.which<NAParam && a.layer>=_currentModel->layerCount()) {
    a.layer=_currentModel->layerCount()-1;
    printf("Layer number for parameter greater than layer number in model\n");
  }
  else if(a.which>=NAParam && a.layer>=_nd) {
    a.layer=_nd-1;
    printf("Number for parameter greater than dimension number in model\n");    
  }
  switch (a.which) {
  case H:
    return _currentModel->h(a.layer);
  case Vs:
    return 1.0/_currentModel->slowS(a.layer);
  case Vp:
    return 1.0/_currentModel->slowP(a.layer);
  case Rho:
    return _currentModel->rho(a.layer);
  case Depth:
    return _currentModel->depth(a.layer);
  case ExpGrad:
    return _currentModel->expGrad(a.layer);
  case ModeStdDev:
    return _currentModel->slowS(a.layer);
  case NAParam:
    return _currentNAModel[a.layer];
  }
  return 0;
}

double CompatInversionReport::loadCostValue(int modelIndex)
{
  TRACE;
  if(!_currentModel && !initCurrentModel()) return 0;
  if(startReadingBlock(modelIndex+_headerBlocksCount,ModelDispersion))
  {
    _s >> _currentID;
    int n; // Skip the layered model
    _s >> n;
    _s.device()->seek((4*n-1)*sizeof(double)+_s.device()->pos());
    _s >> _currentCost;
  }
  else _currentCost=-1;
  return _currentCost;
}

Seismic1DModel *  CompatInversionReport::currentModel()
{
  TRACE;
  if(!_currentModel) loadModel(0);  
  return _currentModel;
}

float *  CompatInversionReport::currentNAModel()
{
  TRACE;
  if(!_currentNAModel) loadModel(0);  
  return _currentNAModel;
}

bool CompatInversionReport::loadModel(int modelIndex)
{
  TRACE;
  if(!_currentModel && !initCurrentModel()) return false;
  if(!_currentNAModel) _currentNAModel=new float [_nd];
  if(startReadingBlock(modelIndex+_headerBlocksCount,ModelDispersion)) {
    _s >> _currentID;
    _currentModel->fromStream(_s);
    _s >> _currentCost;
    if(_version>=4) _s.readRawData((char *)_currentNAModel,_nd*sizeof(float));
  }
  else
    _currentCost=-1;
  return true;
}

bool CompatInversionReport::loadNextModel()
{
  TRACE;
  if(!_currentModel && !initCurrentModel()) return false;
  if(!_currentNAModel) _currentNAModel=new float [_nd];
  if(startReadingBlock(_currentIndex+1+_headerBlocksCount,ModelDispersion)) {
    _s >> _currentID;
    _currentModel->fromStream(_s);
    _s >> _currentCost;
    if(_version>=4) _s.readRawData((char *)_currentNAModel,_nd*sizeof(float));
    _currentIndex++;
  }
  else
    _currentCost=-1;
  return true;
}

CompatEllipticityData * CompatInversionReport::loadEllipticityGoal()
{
  TRACE;
  if(!_currentEllipticity) return 0;
  CompatEllipticityData * ell=new CompatEllipticityData(_currentEllipticity);
  if(startReadingBlock(_blockEll,EllipticityGoal)) {
    ell->reportToData(_s);
    ell->reportToDataWeight(_s,_version>=3);
    if(_version>=5) ell->reportToF0(_s);
  }
  else printf("unable to read block EllipticityGoal in current file\n");
  return ell;
}

int CompatInversionReport::getBestFit()
{
  TRACE;
  int minCostIndex=-1, i=0, modelID, n;
  double cost,minCost=std::numeric_limits<double>::infinity();
  while(i+_headerBlocksCount<_trueN) {
    if(!startReadingBlock(i+_headerBlocksCount,ModelDispersion)) break;
    _s >> modelID;
    // Skip the layered model
    _s >> n;
    _s.device()->seek((4*n-1)*sizeof(double)+_s.device()->pos());
    _s >> cost;
    if(cost<minCost) {
      minCost=cost;
      minCostIndex=i;
    }
    i++;
  }
  return minCostIndex;
}

QVector<Point> * CompatInversionReport::getBestFits(QVector<Point>& curve,
                                                         int& startIndex,
                                                         double& minCostValue,
                                                         int& generatedModels)
{
  int minCostIndex=-1;
  int interCount=2000;
  while(startIndex+_headerBlocksCount<_trueN) {
    if(!startReadingBlock(startIndex+_headerBlocksCount,ModelDispersion)) break;
    _s >> _currentID;
    int n; // Skip the layered model
    _s >> n;
    _s.device()->seek((4*n-1)*sizeof(double)+_s.device()->pos());
    _s >> _currentCost;
    if(_currentCost<minCostValue) {
      if(curve.count()<2000) {
        minCostValue=_currentCost;
        if(_currentID==-1) minCostIndex=startIndex; else minCostIndex=_currentID;
        curve.push_back(Point(minCostIndex,minCostValue,0));
      }
      else {
        if(interCount>0) interCount--;
        else {
          minCostValue=_currentCost;
          if(_currentID==-1) minCostIndex=startIndex; else minCostIndex=_currentID;
          curve.push_back(Point(minCostIndex,minCostValue,0));
          interCount=2000;
        }
      }
    }
    startIndex++;
  }
  // Check again last record (needed if no more blocks were added since last call)
  startReadingBlock(startIndex+_headerBlocksCount-1,ModelDispersion);
  _s >> _currentID;
  generatedModels=_currentID;
  return &curve;
}

void CompatInversionReport::getNAParam(int& itmax, int& nsi, int& ns, int& nr)
{
  TRACE;
  QFileInfo fi(_fileName);
  QDir d(fi.absolutePath());
  d.cd(QString("wd_%1").arg(fi.baseName()));
  if(d.exists("na.in")) {
    // Read na.in to get the total number of models that will be generated
    FILE * f;
    f=fopen(d.absoluteFilePath("na.in").toLatin1().data(),"r");
    if(f) {
      int buflen=256;
      char * buf=new char [buflen];
      File::readLine(buf, buflen, f); // comment line
      File::readLine(buf, buflen, f); // comment line
      File::readLine(buf, buflen, f); // comment line
      File::readLine(buf, buflen, f); // Perform NA or MC ?
      File::readLine(buf, buflen, f); // maximum number of iterations
      sscanf(buf,"%i", &itmax);
      File::readLine(buf, buflen, f); // sample size for initial population
      sscanf(buf,"%i", &nsi);
      File::readLine(buf, buflen, f); // sample size for remaining populations
      sscanf(buf,"%i", &ns);
      File::readLine(buf, buflen, f); // number of cell to resample
      sscanf(buf,"%i", &nr);
      fclose(f);
      delete [] buf;
    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------
//
//  2nd part: specific to dispersion curve
//
//-----------------------------------------------------------------------------------------------------------------------------

bool CompatInversionReport::initCurrentDispersion()
{
  TRACE;
  ASSERT(!_currentDispersion);
  _currentDispersion=(CompatMultiModalCurves *) new CompatDispersion();
  if(startReadingBlock(_blockOmegas,Omegas))
    _currentDispersion->reportToOmega(_s);
  else {
    Message::critical(MSG_ID,"Inversion report", "Bad block: Omegas at index 1",Message::cancel());
    return false;
  }
  if(_trueN>_blockEll && startReadingBlock(_blockEll,EllipticityGoal))
    if(!initCurrentEllipticity()) return false;
  if(_version<2) 
    _currentDispersion->setRayleighModesCount(_currentDispersion->modesCount());
  return true;
}

void CompatInversionReport::addOmegas(CompatMultiModalFrequency * omegas)
{
  TRACE;
  // tests if parameters are entered (at least one)
  ASSERT(_nd);
  startWritingBlock(Omegas);  
  omegas->omegaToReport(_s);
  endWritingBlock();
}

void CompatInversionReport::addDispersionGoal(CompatDispersionData * rms)
{
  TRACE;
  // tests if omegas are entered
  if(!startReadingBlock(_blockOmegas,Omegas)) {
    printf("You must call addOmegas before calling addDispersionGoal\n");
    return;
  }    
  startWritingBlock(DispersionGoal);
  if(rms) {
    _s << rms->rayleighModesCount();
    rms->dataToReport(_s);
  }
  endWritingBlock();
}

void CompatInversionReport::modifyDispersionGoal(CompatDispersionData * rms)
{
  TRACE;
  startWritingBlock(_blockGoal,DispersionGoal);
  if(rms) {
    _s << rms->rayleighModesCount();
    rms->dataToReport(_s);
  }
  endWritingBlock();
}

void CompatInversionReport::addModel(const Seismic1DModel * model,
                                  float * naModel,
                                  const CompatDispersion * disp,
                                  double cost, int modelID)
{
  TRACE;
  // Write new model at the end of file
  if(startWritingBlock(ModelDispersion)) {
    if(modelID<0) {
      _s << _currentID;
      _currentID++;
    }
    else {
      if(modelID>=_currentID) _currentID=modelID+1;
      _s << modelID;   
    }
    model->toStream(_s);
    _s << cost;
    writeNAModel(naModel);
    disp->valuesToReport(_s);
    disp->refinesToReport(_s);
  }
  else printf("Maximum number of models is reached, model not added\n");
  endWritingBlock();
  setMaxOmegasCount(disp->refinesCount(),0);
}

void CompatInversionReport::addModel(const Seismic1DModel * model,
                                  float * naModel,
                                  const CompatDispersion * disp,
                                  const CompatEllipticity * ell,
                                  double cost, int modelID)
{
  TRACE;
  // Write new model at the end of file
  if(startWritingBlock(ModelDispersion)) {
    if(modelID<0) {
      _s << _currentID;
      _currentID++;
    }
    else {
      if(modelID>=_currentID) _currentID=modelID+1;
      _s << modelID;   
    }
    model->toStream(_s);
    _s << cost;
    writeNAModel(naModel);
    disp->valuesToReport(_s);
    disp->refinesToReport(_s);
    ell->valuesToReport(_s);
    ell->refinesToReport(_s);
  }
  else printf("Maximum number of models is reached, model not added\n");
  endWritingBlock();
  setMaxOmegasCount(disp->refinesCount(),ell->refinesCount());
}

bool CompatInversionReport::isSameGoalDispersion(CompatInversionReport * otherModels)
{
  TRACE;
  int nModes=_currentDispersion->modesCount();
  int n=_currentDispersion->omegasCount();
  if(nModes!=otherModels->_currentDispersion->modesCount() || 
      n!=otherModels->_currentDispersion->omegasCount()) return false;
  
  CompatDispersionData * rms1, * rms2;
  rms1=loadDispersionGoal();
  rms2=otherModels->loadDispersionGoal();
  
  return rms1->isSameData(rms2);
}

bool CompatInversionReport::loadDispersion(int modelIndex)
{
  TRACE;
  if(!_currentModel && !initCurrentModel()) return false;
  if(!_currentNAModel) _currentNAModel=new float [_nd];
  if(!_currentDispersion) initCurrentDispersion();
  if(startReadingBlock(modelIndex+_headerBlocksCount,ModelDispersion)) {
    _s >> _currentID;
    _currentModel->fromStream(_s);
    _s >> _currentCost;
    if(_version>=4) _s.readRawData((char *)_currentNAModel,_nd*sizeof(float));
    _currentDispersion->reportToValues(_s);
    if(_version>=1) _currentDispersion->reportToRefines(_s);
    if(_currentEllipticity) {
      _currentEllipticity->reportToValues(_s);
      if(_version>=1) _currentEllipticity->reportToRefines(_s);
    }
  }
  else _currentCost=-1;
  return true;
}

bool CompatInversionReport::loadDispersion(CompatDispersion * disp,CompatEllipticity * ell,int modelIndex)
{
  TRACE;
  if(!_currentModel && !initCurrentModel()) return true;
  if(!_currentNAModel) _currentNAModel=new float [_nd];
  if(!_currentDispersion) initCurrentDispersion();
  if(startReadingBlock(_blockOmegas,Omegas)) {
    disp->reportToOmega(_s);
    if(ell) {
      startReadingBlock(_blockOmegas,Omegas);
      ell->reportToOmega(_s);
    }
    if(startReadingBlock(modelIndex+_headerBlocksCount,ModelDispersion)) {
      _s >> _currentID;
      _currentModel->fromStream(_s);
      _s >> _currentCost;
      if(_version>=4) _s.readRawData((char *)_currentNAModel,_nd*sizeof(float));
      disp->reportToValues(_s);
      if(_version>=1) disp->reportToRefines(_s);
      if(_currentEllipticity && ell) {
        ell->reportToValues(_s);
        if(_version>=1) ell->reportToRefines(_s);
      }
    }
    else _currentCost=-1;
  }
  else _currentCost=-1;
  return false;
}

CompatDispersionData * CompatInversionReport::loadDispersionGoal()
{
  TRACE;
  if(!_currentDispersion) initCurrentDispersion();
  CompatDispersionData * rms=new CompatDispersionData(_currentDispersion);
  if(startReadingBlock(_blockGoal,DispersionGoal)) {
    if(_version>=2) {
      uint rayleighModesCount;
      _s >> rayleighModesCount;
      rms->setRayleighModesCount(rayleighModesCount);
      _currentDispersion->setRayleighModesCount(rayleighModesCount);
    }
    rms->reportToData(_s);
    rms->reportToDataWeight(_s,_version>=3);
  }
  else printf("unable to read block DispersionGoal in current file\n");
  if(_version==0) rms->convertStddev();
  return rms;
}

void CompatInversionReport::statDispersion(int ipoint,int imode,int start_model,
                                        int end_model,double& mean, double& stddev)
{
  TRACE;
  mean=0;
  stddev=0;
  for(int i=start_model;i<=end_model;i++) {
    loadDispersion(i);
    double xi=1/_currentDispersion->value(ipoint,imode);
    mean+=xi;
    stddev+=xi*xi;
  }
  int count=end_model-start_model+1;
  mean/=(double)count;
  stddev=sqrt((stddev/(double)count)-mean*mean);
}

} // namespace QGpCompatibility
