/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library 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.1 of the License, or (at your option) any later version.
**
**  This file 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 library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-07-15
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>

#include "IrregularGrid2DData.h"
#include "GaussDistribution.h"
#include "GridSearch.h"

namespace QGpCoreMath {

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
IrregularGrid2DData::IrregularGrid2DData()
  : QSharedData()
{
  TRACE;
  _x=nullptr;
  _y=nullptr;
  _nx=_ny=0;
  _n2x=_n2y=0;
  _values=nullptr;
}

IrregularGrid2DData::IrregularGrid2DData(int nx, int ny)
  : QSharedData()
{
  TRACE;
  _nx=nx;
  _ny=ny;
  ASSERT(_nx>0 && _ny>0);
  initLookup();
  _x=new double[nx];
  _y=new double[ny];
  _values=new double[_nx*_ny];
}

IrregularGrid2DData::IrregularGrid2DData(const IrregularGrid2DData& o)
  : QSharedData(o)
{
  TRACE;
  _nx=o._nx;
  _ny=o._ny;
  _n2x=o._n2x;
  _n2y=o._n2y;
  _x=new double[_nx];
  _y=new double[_ny];
  int n=_nx*_ny;
  _values=new double[n];
  memcpy(_x, o._x, sizeof(double)*_nx);
  memcpy(_y, o._y, sizeof(double)*_ny);
  memcpy(_values, o._values, sizeof(double)*n);
}

/*!
  Description of destructor still missing
*/
IrregularGrid2DData::~IrregularGrid2DData()
{
  TRACE;
  clear();
}

void IrregularGrid2DData::operator=(const IrregularGrid2DData& o)
{
  TRACE;
  clear();
  _nx=o._nx;
  _ny=o._ny;
  _n2x=o._n2x;
  _n2y=o._n2y;
  _x=new double[_nx];
  _y=new double[_ny];
  int n=_nx*_ny;
  _values=new double[n];
  memcpy(_x, o._x, sizeof(double)*_nx);
  memcpy(_y, o._y, sizeof(double)*_ny);
  memcpy(_values, o._values, sizeof(double)*n);
}

void IrregularGrid2DData::copy(const GridSearch& o, AxisType normalTo, double at)
{
  TRACE;
  clear();
  switch(normalTo) {
  case XAxis:
    _nx=o.ny();
    _ny=o.nz();
    break;
  case YAxis:
    _nx=o.nx();
    _ny=o.nz();
    break;
  case ZAxis:
    _nx=o.nx();
    _ny=o.ny();
    break;
  }

  initLookup();
  _x=new double[_nx];
  _y=new double[_ny];
  int n=_nx*_ny;
  _values=new double[n];
  int i;
  switch(normalTo) {
  case XAxis:
    for(i=0; i<_nx; i++) {
      _x[i]=o.y(i);
    }
    break;
  case YAxis:
  case ZAxis:
    for(i=0; i<_nx; i++) {
      _x[i]=o.x(i);
    }
    break;
  }
  switch(normalTo) {
  case XAxis:
  case YAxis:
    for(i=0; i<_ny; i++) {
      _y[i]=o.z(i);
    }
    break;
  case ZAxis:
    for(i=0; i<_ny; i++) {
      _y[i]=o.y(i);
    }
    break;
  }
  switch(normalTo) {
  case XAxis:
  case YAxis:
    for(i=0; i<_ny; i++) {
      _y[i]=o.z(i);
    }
    break;
  case ZAxis:
    for(i=0; i<_ny; i++) {
      _y[i]=o.y(i);
    }
    break;
  }
  for(int k=_nx*_ny-1; k>=0; k--) {
    _values[k]=o.value(k);
  }

  // TODO: it looks like unfinished developments, to be checked
  switch(normalTo) {
  case XAxis:
  case YAxis:
    for(i=0; i<_ny; i++) {
      _y[i]=o.z(i);
    }
    break;
  case ZAxis:
    for(int k=_nx*_ny-1; k>=0; k--) {
      _values[k]=o.value(k);
    }
    break;
  }
}

void IrregularGrid2DData::operator+=(const IrregularGrid2DData& o)
{
  TRACE;
  if(_nx!=o._nx || _ny!=o._ny) return ;
  int n=_nx*_ny;
  for(int i=0; i<n; i++) _values[i]+=o._values[i];
}

void IrregularGrid2DData::clear()
{
  TRACE;
  delete [] _x;
  delete [] _y;
  delete [] _values;
  _x=nullptr;
  _y=nullptr;
  _values=nullptr;
  _nx=_ny=0;
  _n2x=_n2y=0;
}

QVector<double> IrregularGrid2DData::x() const
{
  QVector<double> v;
  for(int i=0; i<_nx; i++) v << _x[i];
  return v;
}

QVector<double> IrregularGrid2DData::y() const
{
  QVector<double> v;
  for(int i=0; i<_ny; i++) v << _y[i];
  return v;
}

void IrregularGrid2DData::initLookup()
{
  TRACE;
  _n2x=1;
  while(_n2x<_nx) _n2x=_n2x << 1;
  _n2y=1;
  while(_n2y<_ny) _n2y=_n2y << 1;
}

void IrregularGrid2DData::init(int nx, int ny)
{
  TRACE;
  if(nx!=_nx) {
    _nx=nx;
    delete [] _x;
    _x=new double[nx];
  } else if(ny==_ny) return;
  if(ny!=_ny) {
    _ny=ny;
    delete [] _y;
    _y=new double[ny];
  }
  initLookup();
  delete [] _values;
  _values=new double[_nx*_ny];
}

void IrregularGrid2DData::init(double value)
{
  TRACE;
  int n=_nx * _ny;
  for(int i=0; i<n; i++) _values[i]=value;
}

bool IrregularGrid2DData::isLike(IrregularGrid2DData& o) const
{
  TRACE;
  if(_nx!=o._nx || _ny!=o._ny) return false;
  int i;
  for(i=0; i<_nx; i++) {
    if(fabs(_x[i]-o._x[i])>1e-10) return false;
  }
  for(i=0; i<_ny; i++) {
    if(fabs(_y[i]-o._y[i])>1e-10) return false;
  }
  return true;
}

/*!
  Return the vector pointer and the number of cells ( \a n) in direction \a axis
*/
double * IrregularGrid2DData::getAxis(AxisType axis, int& n) const
{
  TRACE;
  double * val;
  if(axis==XAxis) {
    n=_nx;
    val=_x;
  } else {
    n=_ny;
    val=_y;
  }
  return val;
}

void IrregularGrid2DData::setLinear(AxisType axis, double min, double max)
{
  TRACE;
  ASSERT(max>min);
  int n;
  double * val=getAxis(axis, n);
  int n1=n-1;
  double factor=(max-min)/static_cast<double>(n1);
  val[0]=min;
  for(int i=1;i<n1; i++) val[i]=min+factor*i;
  val[n1]=max;
}

void IrregularGrid2DData::setLog(AxisType axis, double min, double max)
{
  TRACE;
  ASSERT(max>min && min>0);
  double factor=max/min;
  int n;
  double * val=getAxis(axis, n);
  int n1=n-1;
  val[0]=min;
  for(int i=1; i<n1; i++) {
    val[i]=min*pow(factor, static_cast<double>(i)/static_cast<double>(n1));
  }
  val[n1]=max;
}

void IrregularGrid2DData::inverse(AxisType axis)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  // As val must always increase, we must re-order the _values
  double * newValues=new double[_nx*_ny];
  if(axis==XAxis) {
    double * newAxis=new double[_nx];
    for(int ix=0; ix<_nx; ix++) {
      int index=ix;
      int new_index=_nx-ix-1;
      newAxis[new_index]=1.0/_x[index];
      for(int iy=_nx*(_ny-1); iy>=0; iy-=_nx) {
        newValues[iy+new_index]=_values[iy+index];
      }
    }
    delete [] _x;
    _x=newAxis;
  } else {
    double * newAxis=new double[_ny];
    for(int iy=0; iy<_ny; iy++) {
      int index=iy;
      int new_index=_ny-iy-1;
      newAxis[new_index]=1.0/_y[index];
      index*=_nx;
      new_index*=_nx;
      for(int ix=_nx-1; ix>=0; ix--) {
        newValues[ix+new_index]=_values[ix+index];
      }
    }
    delete [] _y;
    _y=newAxis;
  }
  delete [] _values;
  _values=newValues;
}

void IrregularGrid2DData::log10(AxisType axis)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  if(axis==XAxis) {
    for(int ix=0; ix<_nx; ix++) _x[ix]=::log10(_x[ix]);
  } else {
    for(int iy=0; iy<_ny; iy++) _y[iy]=::log10(_y[iy]);
  }
}

void IrregularGrid2DData::log10()
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  int n=_nx*_ny;
  for(int i=0; i<n; i++) _values[i]=::log10(_values[i]);
}

void IrregularGrid2DData::pow10(AxisType axis)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  if(axis==XAxis) {
    for(int ix=0; ix<_nx; ix++) _x[ix]=pow(10.0, _x[ix]);
  } else {
    for(int iy=0; iy<_ny; iy++) _y[iy]=pow(10.0, _y[iy]);
  }
}

void IrregularGrid2DData::pow10()
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  int n=_nx*_ny;
  for(int i=0; i<n; i++) _values[i]=pow(10.0, _values[i]);
}

void IrregularGrid2DData::multiplyCoordinates(AxisType axis, double fac)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  if(axis==XAxis) {
    for(int ix=0; ix<_nx; ix++) _x[ix]*=fac;
  } else {
    for(int iy=0; iy<_ny; iy++) _y[iy]*=fac;
  }
}

void IrregularGrid2DData::multiplyValues(AxisType along, int index, double fac)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  getAxis(along, na);
  ASSERT(na*di==n);
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) val[i]*=fac;
}

void IrregularGrid2DData::multiplyValues(double fac)
{
  TRACE;
  ASSERT(_nx>0 && _ny>0);
  int n=_nx*_ny;
  for(int i=0; i<n; i++) _values[i]*=fac;
}

/*!
  Return the minimum value of the grid
*/
double IrregularGrid2DData::minimumValue() const
{
  TRACE;
  double val=std::numeric_limits<double>::infinity();
  int n=_nx*_ny;
  for(int i=0; i<n; i++) {
    if(_values[i]<val) val=_values[i];
  }
  return val;
}

/*!
  Return the minimum value of the grid and also the coordinates of the minimum
*/
double IrregularGrid2DData::minimumValue(int& ix, int& iy) const
{
  TRACE;
  double val=-std::numeric_limits<double>::infinity();
  int n=_nx*_ny;
  int iMin=0;
  for(int i=0; i<n; i++) {
    if(_values[i]<val) {
      iMin=i;
      val=_values[i];
    }
  }
  iy=iMin/_nx;
  ix=iMin-iy*_nx;
  return val;
}

/*!
  Return the maximum value of the grid
*/
double IrregularGrid2DData::maximumValue() const
{
  TRACE;
  double val=-std::numeric_limits<double>::infinity();
  int n=_nx*_ny;
  for(int i=0; i<n; i++) {
    if(_values[i]>val) val=_values[i];
  }
  return val;
}

/*!
  Return the maximum value of the grid and also the coordinates of the minimum
*/
double IrregularGrid2DData::maximumValue(int& ix, int& iy) const
{
  TRACE;
  double val=-std::numeric_limits<double>::infinity();
  int n=_nx*_ny;
  int iMax=0;
  for(int i=0; i<n; i++) {
    if(_values[i]>val) {
      iMax=i;
      val=_values[i];
    }
  }
  iy=iMax/_nx;
  ix=iMax-iy*_nx;
  return val;
}

/*!
  Return the minimum coordinate along \a axis (coordinate vectors are always sorted and increasing).
*/
double IrregularGrid2DData::minimumAxis(AxisType axis) const
{
  TRACE;
  // _x or _y is always strictly increasing
  int n;
  double * val=getAxis(axis, n);
  return val[0];
}

/*!
  Return the maximum coordinate along \a axis (coordinate vectors are always sorted and increasing).
*/
double IrregularGrid2DData::maximumAxis(AxisType axis) const
{
  TRACE;
  // _x or _y is always strictly increasing
  int n;
  double * val=getAxis(axis, n);
  return val[n-1];
}

/*!
  Return the sum of values along axis \a along at \a index counted on the other axis
*/
double IrregularGrid2DData::sum(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  getAxis(along, na);
  ASSERT(na * di==n);
  double sum=0;
  for(int i=0; i<n; i+=di) {
    sum+=val[i];
  }
  return sum;
}

/*!
  Return the maximum of values along axis \a along at \a index counted on the other axis
*/
double IrregularGrid2DData::maximumValue(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  double v=-std::numeric_limits<double>::infinity();
  for(int i=0; i<n; i+=di) {
    if(val[i]>v) {
      v=val[i];
    }
  }
  return v;
}

/*!
  Return the maximum of values along axis \a along at \a index counted on the other axis
*/
double IrregularGrid2DData::minimumValue(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  double v=std::numeric_limits<double>::infinity();
  for(int i=0; i<n; i+=di) {
    if(val[i]<v) {
      v=val[i];
    }
  }
  return v;
}

/*!
  Return the mean of values along axis \a along at \a index counted on the other axis.
  Return NAN if the histogram is null.
*/
double IrregularGrid2DData::mean(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na * di==n);
  // Calculate the total area below the curve
  double p, sumP=0.0, sumX=0.0;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    p=val[i]*widthOrHeight(vala, na, ia);
    sumP+=p;
    sumX+=p*vala[ia];
  }
  if(sumP!=0.0) {
    return sumX/sumP;
  } else {
    return std::nan("");
  }
}

/*!
  Return the median of values along axis \a along at \a index counted on the other axis.
  Return NAN if the histogram is null.
*/
double IrregularGrid2DData::median(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  // Calculate the total area below the curve
  double p, sumP=0.0;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    p=val[i]*widthOrHeight(vala, na, ia);
    sumP+=p;
    if(sumP>0.5) {
      // interpolation to find the exact median
      // dy/dy0*dx0=(0.5-(sumP-P))/p*dx0
      if(ia==0) {
        return vala[ia];
      } else {
        return vala[ia-1]+(1.0-(sumP-0.5)/p)*(vala[ia]-vala[ia-1]);
      }
    }
  }
  return std::numeric_limits<double>::quiet_NaN();
}

/*!
  Return the mode of values along axis \a along at \a index count on the other axis.
  Return NAN if the histogram is null.
*/
double IrregularGrid2DData::mode(AxisType along, int index) const
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na * di==n);
  // Calculate the total area below the curve
  double p, pmax=0.0, x=std::numeric_limits<double>::quiet_NaN();
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    p=val[i]*widthOrHeight(vala, na, ia);
    if(p>pmax) {
      pmax=p;
      x=vala[ia];
    }
  }
  return x;
}

/*!
  Return the variance of values along axis \a along at \a index count on the other axis.
  Return NAN if the histogram is null.
*/
double IrregularGrid2DData::variance(AxisType along, int index) const
{
  TRACE;
  // Grid is assumed to be correctly normalized, using function normalize(...)
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  int hitCount=na;
  double p, a, cellWidth=1.0, sumP=0.0, sumX=0.0, sumX2=0.0;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    if(val[i]>0) {
      cellWidth=widthOrHeight(vala, na, ia);
      p=val[i]*cellWidth;
      sumP+=p;
      a=p*vala[ia];
      sumX+=a;
      sumX2+=a*vala[ia];
    } else {
      hitCount--;
    }
  }
  if(hitCount>1) {
    double invSumP=1.0/sumP;
    double average=sumX*invSumP;
    return sumX2*invSumP-average*average;
  } else if(hitCount==1) {
    // cellWidth contains the only one cellWidth, assumed to be 4*sigma (95% on a gaussian);
    return cellWidth*cellWidth/16;
  }
  return std::numeric_limits<double>::quiet_NaN();
}

/*!
  Return the variance of values along axis \a along at \a index count on the other axis.
  The variance is computed relative to \a average that can be any of mean, median or mode. If \a average is
  the median, the median deviation is returned.
  Return NAN if the histogram is null.
*/
double IrregularGrid2DData::variance(AxisType along, int index, double average) const
{
  TRACE;
  // Grid is assumed to be correctly normalized, using function normalize(...)
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  int hitCount=na;
  double p, a, cellWidth=1.0, sumP=0.0, sumX=0.0, sumX2=0.0;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    if(val[i]>0) {
      cellWidth=widthOrHeight(vala, na, ia);
      p=val[i]*cellWidth; // probability attached to vala[ia]
      sumP+=p;
      a=p*vala[ia];
      sumX+=a;
      sumX2+=a*vala[ia];
    } else {
      hitCount--;
    }
  }
  if(hitCount>1) {
    double invSumP=1.0/sumP;
    return sumX2*invSumP+(average-2.0*sumX*invSumP)*average;
  } else if(hitCount==1) {
    // cellWidth contains the only one cellWidth, assumed to be 4*sigma (95% on a gaussian);
    return cellWidth*cellWidth/16.0;
  }
  return std::numeric_limits<double>::quiet_NaN();
}

double IrregularGrid2DData::varianceFit(AxisType along, int index, double average) const
{
  TRACE;
  // Grid is assumed to be correctly normalized, using function normalize(...)
  double minMisfit=std::numeric_limits<double>::infinity();
  double minErr=0.0;
#define MAX_ERROR 0.5
#define ERROR_STEP 0.001
  for(double err=ERROR_STEP; err<MAX_ERROR; err+=ERROR_STEP) {
    GaussDistribution gd(average, average*err);
    double m=misfit(along, index, gd);
    if(m<0.0) {
      break;
    }
    if(m<minMisfit) {
      minMisfit=m;
      minErr=err;
    }
  }
  if(minErr<MAX_ERROR-ERROR_STEP) {
    return average*minErr;
  } else {
    return std::numeric_limits<double>::quiet_NaN();
  }
}

/*!
  Compute misfit between theoretical normal distribution and a cross-section at \a index.
  Cumulative function is compared and not the density function.
*/
double IrregularGrid2DData::misfit(AxisType along, int index, const GaussDistribution& gd) const
{
  TRACE;
  // Grid is assumed to be correctly normalized, using function normalize(...)
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  double m=0.0, sum=0.0, ygd;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    sum+=val[i]*widthOrHeight(vala, na, ia);
    ygd=gd.part(vala[ia]);
    double dy=sum-ygd;
    m+=dy*dy;
  }
  m/=na;
  return ::sqrt(m);
}

/*!
  Normalize area below curve along an axis at index, returns the normalizing factor
*/
double IrregularGrid2DData::normalizeArea(AxisType along, int index)
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  // Calculate the total area below the curve
  double sum=0.0;
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    sum+=val[i]*widthOrHeight(vala, na, ia);
  }
  // Divide all value by this sum
  if(sum>0.0) {
    double f=1.0/sum;
    for(i=0; i<n; i+=di) {
      val[i]*=f;
    }
    return sum;
  } else {
    // All values are zero or all width/height are null
    /*sum=0.0;
    if(na>1) {
      sum+=rightOrTop(vala, na, na);
      sum-=leftOrBottom(vala, na, 0);
    }
    if(sum>0.0) {
      // In the first case, we set a uniform distribution over the grid range
      for(i=0; i<n; i+=di) {
        val[i]=1.0;
      }
      // Normalize it again
      return normalizeArea(along, index);
    } else {
      return 1.0;
    }*/
    return sum;
  }
}

/*!
  Normalize area below curve along an axis for all indexes
*/
void IrregularGrid2DData::normalizeArea(AxisType along)
{
  TRACE;
  if(along==XAxis) {
    for(int i=0; i<_ny; i++) {
      normalizeArea(along, i);
    }
  } else {
    for(int i=0; i<_nx; i++) {
      normalizeArea(along, i);
    }
  }
}

/*!
  Normalize amplitude along an axis at index, returns the normalizing factor
*/
double IrregularGrid2DData::normalizeAmplitude(AxisType along, int index)
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Calculate the maximum value of the curve
  double v=-std::numeric_limits<double>::infinity();
  for(int i=0; i<n; i+=di) {
    if(val[i]>v) {
      v=val[i];
    }
  }
  // Divide all value by this maximum
  if(v!=0.0) {
    double f=1.0/v;
    for(int i=0; i<n; i+=di) {
      val[i]*=f;
    }
  }
  return v;
}

/*!
  Normalize amplitude along an axis for all indexes
*/
void IrregularGrid2DData::normalizeAmplitude(AxisType along)
{
  TRACE;
  if(along==XAxis) {
    for(int i=0; i<_ny; i++) {
      normalizeAmplitude(along, i);
    }
  } else {
    for(int i=0; i<_nx; i++) {
      normalizeAmplitude(along, i);
    }
  }
}

/*!
  Returns the integral of the grid at index along axis as a curve
  For correctness, normalize() must have been called before
  */
Curve<Point2D> IrregularGrid2DData::integralCrossSection(AxisType along, int index) const
{
  Curve<Point2D> f;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  double sum=0, dsum;
  bool continuous=true;
  Point2D p;
  for(int ia=0, i=0; ia<na; i+=di, ia++) {
    dsum=val[i]*widthOrHeight(vala, na, ia);
    if(dsum>1e-15) {
      if(!continuous) {
        p.setX(sum+1e-15);
        p.setY(rightOrTop(vala, na, ia-1));
        f.append(p);
      }
      sum+=dsum;
      p.setX(sum);
      p.setY(rightOrTop(vala, na, ia));
      f.append(p);
      continuous=true;
    } else continuous=false;
  }
  f.sort();
  return f;
}


/*!
  Returns a cross section along \a axis at index \a ixy
*/
Curve<Point2D> IrregularGrid2DData::crossSection(AxisType axis, int ixy) const
{
  int n;
  double * val=getAxis(axis, n);
  Curve<Point2D> f (n);
  for(int i=0; i<n; i++) f[i].setX(val[i]);
  if(axis ==XAxis) {
    for(int i=0; i<n; i++) f[i].setY(value(i, ixy));
  } else {
    for(int i=0; i<n; i++) f[i].setY(value(ixy, i));
  }
  f.sort();
  return f;
}

void IrregularGrid2DData::smooth(AxisType along)
{
  TRACE;
  if(along==XAxis) {
    for(int i=0; i<_ny; i++) {
      smooth(along, i);
    }
  } else {
    for(int i=0; i<_nx; i++) {
      smooth(along, i);
    }
  }
}

void IrregularGrid2DData::smooth(AxisType along, int index)
{
  TRACE;
  int n;
  if(along==XAxis) {
     n=_nx-1;
    double * newValues=new double[n];
    for(int ix=1; ix<n; ix++) {
      newValues[ix]=(1.0/3.0)*(value(ix-1, index)+value(ix, index)+value(ix+1, index));
    }
    for(int ix=1; ix<n; ix++) {
      *valuePointer(ix, index)=newValues[ix];
    }
    if(_nx>1) {
      *valuePointer(0, index)=0.5*(value(0, index)+value(1, index));
      *valuePointer(n, index)=0.5*(value(n, index)+value(n-1, index));
    }
    delete [] newValues;
  } else {
    n=_ny-1;
    double * newValues=new double[n];
    for(int iy=1; iy<n; iy++) {
      newValues[iy]=value(index, iy-1)+value(index, iy)+value(index, iy+1);
    }
    for(int iy=1; iy<n; iy++) {
      *valuePointer(index, iy)=newValues[iy];
    }
    if(_ny>1) {
      *valuePointer(index, 0)=0.5*(value(index, 0)+value(index, 1));
      *valuePointer(index, n)=0.5*(value(index, n)+value(index, n-1));
    }
    delete [] newValues;
  }
}

double IrregularGrid2DData::smoothInvalidCells(AxisType along, int index,
    double invalidValue,
    double sigma2, double RVal,
    bool modify)
{
  TRACE;
#define RRatio 0.01
  double logE=::log10(::exp(1.0));
  double proba=0;
  double cellWidth;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  // Estimate the standard deviation of the current curve
  double logR=::log10(RVal);
  // Recalculate a pseudo value for invalid cells
  int i=0;
  int iis, iisa;
  for(int ia=0; ia<na; i+=di, ia++) {
    if(val[i]==invalidValue) {
      iis=i;
      iisa=ia;
      while(ia<na && val[i]==invalidValue) {
        i+=di;
        ia++;
      }
      ASSERT (iisa>0 || ia<na);
      if(iisa==0) {
        // Invalid values at the beginning of the histogram
        int j=iis;
        double x1=vala[ia-1], d;
        for(int ja=iisa; ja<ia; j+=di, ja++) {
          d=vala[ja]-x1;
          if(modify) val[j]=d*d/sigma2*logE-logR;
          else {
            if(ja>0) cellWidth=vala[ja]-vala[ja-1];
            else cellWidth=vala[1]-vala[0];
            double t=-d*d/sigma2;
            if(t>-50) proba+=cellWidth*RVal*::exp(t);
          }
        }
      } else if(ia==na) {
        // Invalid values at the end of the histogram
        int j=iis;
        double x1=vala[iisa], d;
        for(int ja=iisa; ja<ia; j+=di, ja++) {
          d=vala[ja]-x1;
          if(modify) val[j]=d*d/sigma2*logE-logR;
          else {
            cellWidth=vala[ja]-vala[ja-1];
            double t=-d*d/sigma2;
            if(t>-50) proba+=cellWidth*RVal*::exp(t);
          }
        }

      } else {
        // Invalid value inside the histogram range
        if(iisa==ia-1) {
          if(modify) val[iis]=-::log10(RVal);
          else {
            cellWidth=vala[iisa]-vala[iisa-1];
            proba+=cellWidth*RVal;
          }
        } else if(iisa==ia-2) {
          if(modify) {
            val[iis]=-::log10(RVal);
            val[iis+di]=val[iis];
          } else {
            cellWidth=vala[iisa]-vala[iisa-1];
            proba+=cellWidth*RVal;
            cellWidth=vala[iisa+1]-vala[iisa];
            proba+=cellWidth*RVal;
          }
        } else {
          double x1=vala[iisa], x2=vala[ia-1];
          double s=x1+x2;
          double p=x1*x2;
          double alpha=0.25*(x1*x1+x2*x2+2*p);
          double c=(RVal*RRatio*p-alpha*RVal)/(p-alpha);
          double a=(c-RVal)/p;
          double b=-s*a;
          int j=iis;
          double x;
          for(int ja=iisa; ja<ia; j+=di, ja++) {
            x=vala[ja];
            if(modify) val[j]=-::log10(a*x*x+b*x+c);
            else {
              cellWidth=vala[ja]-vala[ja-1];
              proba+=cellWidth*(a*x*x+b*x+c);
            }
          }
        }
      }
    }
  }
  return proba;
}

double * IrregularGrid2DData::iterator(AxisType along, int index, int &di, int& n) const
{
  TRACE;
  double * val;
  if(along==XAxis) {
    n=_nx;
    di=1;
    val=_values+index*_nx;
  } else {
    n=_ny*_nx;
    di=_nx;
    val=_values+index;
  }
  return val;
}

/*!
  Returns the cell index of value \a val in vector \a axis which contains \a n cells.
  It returns -1 if \a val is outside range on the left (or bottom) and \a n if it is
  outside range on the right (or top).
*/
int IrregularGrid2DData::indexOf(double * axis, int n, int n2, double xory) const
{
  TRACE;
  xory*=2.0;
  if(xory<axis[0]+axis[1]) {
    return xory<2*leftOrBottom(axis, n, 0) ? -1 : 0;
  }
  int lasti=n-1;
  if(xory>axis[lasti-1]+axis[lasti]) {
    return xory>2.0*rightOrTop(axis, n, lasti) ? n : lasti;
  }
  int i=n2;
  int step2=i >> 1;
  while(step2>0) {
    if(i>=lasti) i-=step2;
    else if(xory<=axis[i]+axis[i+1]) {
      if(xory>axis[i]+axis[i-1]) break;
      i-=step2;
    } else
      i+=step2;
    step2=step2 >> 1;
  }
  return i;
}

bool IrregularGrid2DData::belongs(const Point2D& p, int ix, int iy) const
{
  TRACE;
  return left(ix)<p.x() && p.x()<right(ix) &&
         bottom(iy)<p.y() && p.y()<top(iy);
}

void IrregularGrid2DData::resample(AxisType axis, int n, double min, double max, SamplingOptions options)
{
  TRACE;
  if(_ny<2 || _nx<2) return ;
  double * values, *x, *y, *val;
  Curve<Point2D> valuesF;
  if(axis==XAxis) {
    values=new double[n*_ny];
    val=values;
    for(int iy=0; iy<_ny; iy++) {
      valuesF=crossSection(axis, iy);
      valuesF.resample(n, min, max, options);
      for(int ix=0; ix<n; ix++) val[ix]=valuesF[ix].y();
      val+=n;
    }
    x=new double[n];
    delete [] _x;
    _x=x;
    for(int ix=0; ix<n; ix++) x[ix]=valuesF[ix].x();
    _nx=n;
  } else {
    values=new double[_nx*n];
    val=values;
    for(int ix=0; ix<_nx; ix++) {
      valuesF=crossSection(axis, ix);
      valuesF.resample(n, min, max, options);
      int offY=0;
      for(int iy=0; iy<n; iy++, offY+=_nx) val[offY]=valuesF[iy].y();
      val++;
    }
    y=new double[n];
    delete [] _y;
    _y=y;
    for(int iy=0; iy<n; iy++) y[iy]=valuesF[iy].x();
    _ny=n;
  }
  delete [] _values;
  _values=values;
}

void IrregularGrid2DData::resample(AxisType axis, const QVector<double>& xModel)
{
  TRACE;
  if(_ny<2 || _nx<2) return ;
  double * values, *x, *y, *val;
  int n=xModel.count();
  Curve<Point2D> valuesF;
  if(axis==XAxis) {
    values=new double[n*_ny];
    val=values;
    for(int iy=0; iy<_ny; iy++) {
      valuesF=crossSection(axis, iy);
      // This is the only difference with precedding function
      valuesF.resample(xModel);
      for(int ix=0; ix<n; ix++) val[ix]=valuesF[ix].y();
      val+=n;
    }
    x=new double[n];
    delete [] _x;
    _x=x;
    for(int ix=0; ix<n; ix++) x[ix]=valuesF[ix].x();
    _nx=n;
  } else {
    values=new double[_nx*n];
    val=values;
    for(int ix=0; ix<_nx; ix++) {
      valuesF=crossSection(axis, ix);
      // This is the only difference with precedding function
      valuesF.resample(xModel);
      int offY=0;
      for(int iy=0; iy<n; iy++, offY+=_nx) val[offY]=valuesF[iy].y();
      val++;
    }
    y=new double[n];
    delete [] _y;
    _y=y;
    for(int iy=0; iy<n; iy++) y[iy]=valuesF[iy].x();
    _ny=n;
  }
  delete [] _values;
  _values=values;
}

void IrregularGrid2DData::cut(AxisType axis, double min, double max, SamplingOptions options)
{
  TRACE;
  if(_ny<2 || _nx<2) return ;
  double * values=nullptr, *x, *y, *val=nullptr;
  int n=0;
  Curve<Point2D> valuesF;
  if(axis ==XAxis) {
    for(int iy=0; iy<_ny; iy++) {
      valuesF=crossSection(axis, iy);
      valuesF.cut(min, max, options | Interpole);
      if(iy==0) {
        n=valuesF.count();
        values=new double[n*_ny];
        val=values;
      }
      for(int ix=0; ix<n; ix++) val[ix]=valuesF[ix].y();
      val+=n;
    }
    x=new double[n];
    delete [] _x;
    _x=x;
    for(int ix=0; ix<n; ix++) x[ix]=valuesF[ix].x();
    _nx=n;
  } else {
    for(int ix=0; ix<_nx; ix++) {
      valuesF=crossSection(axis, ix);
      valuesF.cut(min, max, options | Interpole);
      if(ix==0) {
        n=valuesF.count();
        values=new double[_nx*n];
        val=values;
      }
      int offY=0;
      for(int iy=0; iy<n; iy++, offY+=_nx) val[offY]=valuesF[iy].y();
      val++;
    }
    y=new double[n];
    delete [] _y;
    _y=y;
    for(int iy=0; iy<n; iy++) y[iy]=valuesF[iy].x();
    _ny=n;
  }
  delete [] _values;
  _values=values;
}

void IrregularGrid2DData::printValues(AxisType along, int index)
{
  TRACE;
  // Construct an iterator on grid values
  int di=0, n;
  double * val=iterator(along, index, di, n);
  // Construct an iterator on grid axis values to calculate the grid spacing
  int na;
  double * vala=getAxis(along, na);
  ASSERT(na*di==n);
  if(along==YAxis) {
    printf("x=%lf\n", _x[index]);
  } else {
    printf("y=%lf\n", _y[index]);
  }
  int i=0;
  for(int ia=0; ia<na; i+=di, ia++) {
    printf( "%lf %lf\n", vala[ia], val[i]);
  }
}


/*!
  Returns the delta coordinate for the cell ival along axis
*/
double IrregularGrid2DData::widthOrHeight(double * axis, int n, int ival) const
{
  TRACE;
  if(n==0) return 0.0;
  if(ival<=0) return axis[1]-axis[0];
  else if(ival>=n-1) return axis[n-1]-axis[n-2];
  return (axis[ival+1]-axis[ival-1])*0.5;
}

/*!
  Returns the maximum coordinate for the cell \a ival along \a axis.
*/
double IrregularGrid2DData::rightOrTop(double * axis, int n, int ival) const
{
  TRACE;
  if(n==0) return 0.0;
  if(ival<=0) return (axis[0]+axis[1])*0.5;
  else if(ival>=n-1) return (3.0*axis[n-1]-axis[n-2])*0.5;
  return (axis[ival]+axis[ival+1])*0.5;
}

/*!
  Returns the minimum coordinate for the cell \a ival along \a axis.
*/
double IrregularGrid2DData::leftOrBottom(double * axis, int n, int ival) const
{
  TRACE;
  if(ival<=0) return (3.0*axis[0]-axis[1])*0.5;
  else if(ival>=n-1) return (axis[n-1]+axis[n-2])*0.5;
  return (axis[ival-1]+axis[ival])*0.5;
}

/*!
  Returns a cross section through the grid along the curve \a path. \a deltaX is an optional offset on the X
  values of the produced curve.
*/
Curve<Point2D> IrregularGrid2DData::crossSection(const Curve<Point2D>& path, double deltaX) const
{
  Segment2D seg;
  int n=path.count();
  Curve<Point2D> curve;
  for(int i=1; i<n; i++) {
    seg.set(path.at(i-1), path.at(i));
    if(curve.count()==0) {
      curve.append(crossSection(seg, deltaX));
    } else {
      curve.append(crossSection(seg, -curve.last().x()));
    }
  }
  return curve;
}

/*!
  Returns a cross section through the grid along the segment \a seg. \a deltaX is an optional offset on the X
  values of the produced curve.
*/
Curve<Point2D> IrregularGrid2DData::crossSection(Segment2D seg, double deltaX) const
{
  Curve<Point2D> interX;
  Curve<Point2D> interY;
  Segment2D gseg;
  int i, firstX=-1, firstY=-1;
  double v, l1, l2;
  Point2D p;
  // Looking for X intersections
  l1=bottom(0);
  l2=top(_ny-1);
  v=left(0);
  gseg.set(v, l1, v, l2);
  if(seg.intersects(gseg, p)) {
    interX.append(p);
    firstX=0;
    //printf("first ix %i inter: %lg %lg\n",firstX,p.x,p.y);
  }
  for(i=0; i<_nx; i++) {
    v=right(i);
    gseg.set(v, l1, v, l2);
    if(seg.intersects(gseg, p)) {
      interX.append(p);
      if(firstX==-1) firstX=i+1;
      //printf("first ix %i inter: %lg %lg\n",firstX,p.x,p.y);
    }
  }
  // Looking for Y intersections
  l1=left(0);
  l2=right(_nx-1);
  v=bottom(0);
  gseg.set(l1, v, l2, v);
  if(seg.intersects(gseg, p)) {
    interY.append(p);
    firstY=0;
    //printf("first iy %i inter: %lg %lg\n",firstY,p.x,p.y);
  }
  for(i=0; i<_ny; i++) {
    v=top(i);
    gseg.set(l1, v, l2, v);
    if(seg.intersects(gseg, p)) {
      interY.append(p);
      if(firstY==-1) firstY=i+1;
      //printf("first iy %i inter: %lg %lg\n",firstY,p.x,p.y);
    }
  }
  Curve<Point2D> f;
  int iX, iY;
  int incX, incY;
  int endX, endY;
  if(seg.p1().x()<=seg.p2().x()) {
    endX=interX.count();
    iX=0;
    incX=1;
    if(firstX==-1) {
      firstX=indexOfX(seg.p1().x());
      if(firstX==_nx) firstX-=2;
      else if(firstX==-1) firstX+=2;
      else firstX++;
    }
  } else {
    endX=-1;
    iX=interX.count()-1;
    incX=-1;
    firstX += iX;
    if(firstX==-1) {
      firstX=indexOfX(seg.p1().x());
      if(firstX==_nx) firstX--;
      else if(firstX==-1) firstX++;
    }
    //printf("Reversing X\n");
  }
  if(seg.p1().y() <= seg.p2().y()) {
    endY=interY.count();
    iY=0;
    incY=1;
    if(firstY==-1) {
      firstY=indexOfY(seg.p1().y());
      if(firstY==_ny) firstY-=2;
      else if(firstY==-1) firstY+=2;
      else firstY++;
    }
  } else {
    endY=-1;
    iY=interY.count()-1;
    incY=-1;
    firstY+=iY;
    if(firstY==-1) {
      firstY=indexOfY(seg.p1().y());
      if(firstY==_ny) firstY--;
      else if(firstY==-1) firstY++;
   }
    //printf("Reversing Y\n");
  }
  double dX, dY;
  int index;
  //printf("P1 %lg %lf\n",seg.p1().x,seg.p1().y);
  if(iX!=endX)
    dX=seg.p1().distanceTo(interX.at(iX));
  else
    dX=std::numeric_limits<double>::infinity();
  if(iY!=endY)
    dY=seg.p1().distanceTo(interY.at(iY));
  else
    dY=std::numeric_limits<double>::infinity();
  while(iX!=endX || iY!=endY) {
    //printf("Next point on X %lg %lf\n",(*itX).x,(*itX).y);
    //printf("Next point on Y %lg %lf\n",(*itY).x,(*itY).y);
    //printf("dX=%lg dY=%lg\n",dX,dY);
    if(dX < dY) {
      if(incY<0)
        index=firstY<_ny ? firstY : _ny-1;
      else
        index=firstY>0 ? firstY-1 : 0;
      //printf("index=%i\n",index);
      if(firstX==0) {
        p.setX(dX-deltaX);
        p.setY(value(0, index));
        f.append(p);
        //printf("x appending firstX %i : %lg %lg\n",firstX,resX->back(),resY->back());
        firstX+=incX;
      } else if(firstX==_nx) {
        p.setX(dX-deltaX);
        p.setY(value(_nx-1, index));
        f.append(p);
        //printf("x appending firstX %i : %lg %lg\n",firstX,resX->back(),resY->back());
        firstX+=incX;
      } else {
        p.setX(dX-deltaX);
        if(incX<0)
          p.setY(value(firstX, index));
        else
          p.setY(value(firstX-1, index));
        f.append(p);
        //printf("x appending firstX %i : %lg %lg\n",firstX,resX->back(),resY->back());
        if(firstX<_nx) {
          p.setX(dX-deltaX);
          if(incX<0)
            p.setY(value(firstX-1, index));
          else
            p.setY(value(firstX, index));
          f.append(p);
          //printf("x appending firstX %i : %lg %lg\n",firstX,resX->back(),resY->back());
          firstX+=incX;
        }
      }
      iX+=incX;
      if(iX!=endX)
        dX=seg.p1().distanceTo(interX.at(iX));
      else
        dX=std::numeric_limits<double>::infinity();
    } else {
      if(incX<0)
        index=firstX<_ny ? firstX : _nx-1;
      else
        index=firstX>0 ? firstX-1 : 0;
      //printf("index=%i\n",index);
      if(firstY==0) {
        p.setX(dY-deltaX);
        p.setY(value(index, 0));
        f.append(p);
        //printf("y appending firstY %i : %lg %lg\n",firstY,resX->back(),resY->back());
        firstY+=incY;
      } else if(firstY==_ny) {
        p.setX(dY-deltaX);
        p.setY(value(index, _ny-1));
        f.append(p);
        //printf("y appending firstY %i : %lg %lg\n",firstY,resX->back(),resY->back());
        firstY+=incY;
      } else {
        p.setX(dY-deltaX);
        if(incY<0)
          p.setY(value(index, firstY));
        else
          p.setY(value(index, firstY-1));
        f.append(p);
        //printf("y appending firstY %i : %lg %lg\n",firstY,resX->back(),resY->back());
        if(firstY<_ny) {
          p.setX(dY-deltaX);
          if(incY<0)
            p.setY(value(index, firstY-1));
          else
            p.setY(value(index, firstY));
          f.append(p);
          //printf("y appending firstY %i : %lg %lg\n",firstY,resX->back(),resY->back());
          firstY+=incY;
        }
      }
      iY+=incY;
      if(iY!=endY)
        dY=seg.p1().distanceTo(interY.at(iY));
      else
        dY=std::numeric_limits<double>::infinity();
    }
  }
  return f;
}

} // namespace QGpCoreMath
