/***************************************************************************
**
**  This file is part of DinverCore.
**
**  DinverCore 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.
**
**  DinverCore 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: 2007-04-11
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "PdfCurve.h"
#include "Parameter.h"

namespace DinverCore {

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

  Full description of class still missing
*/

double PdfPoint::_degreesOfFreedom=1.0;

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

  Full description of class still missing
*/

void PdfCurve::toStream(QTextStream& s, const ModelSet& models)
{
  TRACE;
  for(const_iterator it=begin(); it!=end(); ++it) {
    s << it->x() << " " <<  it->probability(models) << " " << it->cell() << endl;
  }
}

void PdfCurve::printCumulative(const ModelSet& models, const Parameter& p)
{
  TRACE;
  int n=count();
  double * cumProb=new double [ n ];
  cumProb[0]=0.0;
  const PdfPoint * p1, *p2;
  p2=&first();
  for(int i=1; i<n; i++) {
    p1=p2;
    p2=&at(i);
    cumProb[i]=cumProb[i-1] + (p2->x() - p1->x()) * p1->probability(models);
  }
  for(int i=1; i<n; i++) {
    printf("%lf %lg\n", p.realValue(at(i).x()), cumProb[i]/cumProb[n-1]);
  }
  delete [] cumProb;
}

/*!
  Generate a random value that follow the probability dentsity function given by \a pdf.
  \a pdf is not necessarely normalized. It is defined by constant steps:
  \code 
  pdf[x]       pdf[y]
  25           23          23 if 25 &lt; x &lt; 30
  30           35          35 if 30 &lt; x &lt; 35
  35           45          45 if 35 &lt; x &lt; 37
  37           50          50 if 37 &lt; x &lt; 41
  41           20          20 if 41 &lt; x &lt; 50
  50           10          10 if 50 &lt; x &lt; 70
  70           10          last value not used, only the x is used for max of last class
  \endcode
*/
double PdfCurve::randomValue(Random& generator, const ModelSet& models, int& cellIndex)
{
  TRACE;
  int n=count();
  double * cumProb=new double [ n ];
  cumProb[0]=0.0;
  const PdfPoint * p1, *p2;
  p2=&first();
  for(int i=1; i<n; i++) {
    p1=p2;
    p2=&at(i);
    cumProb[i]=cumProb[i-1] + (p2->x() - p1->x()) * p1->probability(models);
  }
  double val=generator.uniform()*cumProb[n-1];
  n--;
  int index;
  if(n<12) {
    for(index=1; index<n; index++ ) {
      if(val<cumProb[index]) break;
    }
  } else {
    int n2=1;
    while(n2 < n) n2=n2 << 1; // multiply by 2
    index=n2;
    int step2=index >> 1;
    while(step2 > 0) {
      if(index > n) index -= step2;
      else if(val <= cumProb[index] ) {
        if(val > cumProb[index-1] ) break;
        index -= step2;
      } else
        index += step2;
      step2=step2 >> 1;
    }
  }
  cellIndex=at(index-1).cell();
  return at(index-1).x()+ (val-cumProb[index-1])
        /(cumProb[index] - cumProb[index-1] )
         * (at(index).x() - at(index-1).x());
}

} // namespace DinverCore
