/***************************************************************************
**
**  This file is part of gpgridsearch.
**
**  gpgridsearch 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.
**
**  gpgridsearch 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: 2016-04-25
**  Copyright: 2016-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "GridGenerator.h"

/*!
  \class GridGenerator GridGenerator.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
GridGenerator::GridGenerator(int variableCount, const Curve<Point1D> * values)
{
  TRACE;
  _variableCount=variableCount;
  _values=new Curve<Point1D>[_variableCount];
  _currentIndex=new int[_variableCount];
  for(int i=0; i<_variableCount; i++) {
    _values[i]=values[i];
    _currentIndex[i]=0;
  }
  _current=new double[_variableCount];
  _modelCount=0;

  _subsetIndex=0;
  _subsetCount=1;
  _subsetIndexBase=0;
}
/*!
  Description of constructor still missing
*/
GridGenerator::GridGenerator(int variableCount, int subsetIndex, const Curve<Point1D> * values)
{
  TRACE;
  ASSERT(subsetIndex<=variableCount);
  _variableCount=variableCount;
  _values=new Curve<Point1D>[_variableCount];
  _currentIndex=new int[_variableCount];
  int i=0;
  for(; i<subsetIndex; i++) {
    _values[i]=Curve<Point1D>(1);
    _currentIndex[i]=0;
  }
  for(; i<_variableCount; i++) {
    _values[i]=values[i];
    _currentIndex[i]=0;
  }
  _current=new double[_variableCount];
  _modelCount=0;

  _subsetIndex=0;
  _subsetCount=1;
  _subsetIndexBase=0;
}

/*!
  Description of destructor still missing
*/
GridGenerator::~GridGenerator()
{
  TRACE;
  delete [] _currentIndex;
  delete [] _current;
  delete [] _values;
  delete [] _subsetIndexBase;
}

/*!
  Create a new generator as a subset of this one for parallelization.

  \sa setSubsetIndex
*/
GridGenerator * GridGenerator::subset(int index) const
{
  TRACE;
  GridGenerator * gg=new GridGenerator(_variableCount, _subsetIndex, _values);
  // Convert index into variable index
  int * subsetIndexes=new int[_subsetIndex];
  for(int i=0; i<_subsetIndex; i++) {
    subsetIndexes[i]=index/_subsetIndexBase[i];
    index-=subsetIndexes[i]*_subsetIndexBase[i];
  }
  // Set constant values for all variables less than _subsetIndex
  for(int i=0; i<_subsetIndex; i++) {
    gg->_values[i].first()=_values[i].at(subsetIndexes[i]);
  }
  delete [] subsetIndexes;
  return gg;
}

/*!
  Given minimum number of subsets, calculate the subsetIndex which is the variable
  index where to start subsets. Designed to separate the generator into various
  threads.
*/
void GridGenerator::setSubsetIndex(int minCount)
{
  TRACE;
  _subsetIndex=0;
  _subsetCount=1;
  while(_subsetCount<minCount) {
    _subsetIndex++;
    _subsetCount*=_values[_subsetIndex].count();
  }
  delete [] _subsetIndexBase;
  if(_subsetIndex>0) {
    _subsetIndexBase=new int[_subsetIndex];
    _subsetIndexBase[_subsetIndex-1]=1;
    for(int i=_subsetIndex-2; i>=0; i--) {
      _subsetIndexBase[i]=_subsetIndexBase[i+1]*_values[i+1].count();
    }
  } else {
    _subsetIndexBase=0;
  }

}

void GridGenerator::model(RealSpace& ps) const
{
  double v;

  for(int i=0; i<_variableCount; i++) {
    v=_values[i].at(_currentIndex[i]).x();
    ps.variableParameter(i)->setRealValue(v);
  }
}

bool GridGenerator::next()
{
  _modelCount++;
  _currentIndex[_variableCount-1]++;
  for(int i=_variableCount-1; i>0; i--) {
    //App::log(tr("_currentIndex[%1]=%2 < %3?\n").arg(i).arg(_currentIndex[i]).arg(_values[i].count()) );
    if(_currentIndex[i]>=_values[i].count()) {
      _currentIndex[i-1]++;
      _currentIndex[i]=0;
    } else {
      return true;
    }
  }
  //App::log(tr("_currentIndex[0]=%1 < %3?\n").arg(_currentIndex[0]).arg(_values[0].count()) );
  return _currentIndex[0]<_values[0].count();
}

qint64 GridGenerator::count() const
{
  TRACE;
  qint64 n=1;
  for(int i=0; i<_variableCount; i++) {
    n*=_values[i].count();
  }
  return n;
}
