// @(#)root/minuit2:$Id$
// Authors: M. Winkler, F. James, L. Moneta, A. Zsenei   2003-2005

/**********************************************************************
 *                                                                    *
 * Copyright (c) 2005 LCG ROOT Math team,  CERN/PH-SFT                *
 *                                                                    *
 **********************************************************************/

#ifndef ROOT_Minuit2_FumiliStandardChi2FCN
#define ROOT_Minuit2_FumiliStandardChi2FCN


#include "Minuit2/FumiliChi2FCN.h"
#include "Minuit2/ParametricFunction.h"
#include <assert.h>
#include <vector>
#include <cmath>

namespace ROOT {

   namespace Minuit2 {



/**

Class implementing the standard chi square function, which
is the sum of the squares of the figures-of-merit calculated for each measurement
point, the individual figures-of-merit being: (the Value predicted by the
model-measured Value)/standard deviation.

@author Andras Zsenei and Lorenzo Moneta, Creation date: 31 Aug 2004

@see FumiliChi2FCN

@ingroup Minuit

\todo nice formula for the documentation...

*/

class FumiliStandardChi2FCN : public FumiliChi2FCN {

public:


  /**

  Constructor which initializes chi square function for one-dimensional model function

  @param modelFCN the model function used for describing the data.

  @param meas vector containing the measured values.

  @param pos vector containing the x values corresponding to the
  measurements

  @param mvar vector containing the variances corresponding to each
  measurement (where the variance equals the standard deviation squared).
  If the variances are zero, a Value of 1 is used (as it is done in ROOT/PAW)

  */

   FumiliStandardChi2FCN(const ParametricFunction& modelFCN, const std::vector<double>& meas,
                         const std::vector<double>& pos,
                         const std::vector<double>& mvar)
   { //this->fModelFCN = &modelFunction;
      this->SetModelFunction(modelFCN);

      assert(meas.size() == pos.size());
      assert(meas.size() == mvar.size());
      fMeasurements = meas;
      std::vector<double> x(1);
      unsigned int n = mvar.size();
      fPositions.reserve( n);
      // correct for variance == 0
      fInvErrors.resize(n);
      for (unsigned int i = 0; i < n; ++i)
      {
         x[0] = pos[i];
         fPositions.push_back(x);
         // PAW/ROOT hack : use 1 for 0 entries bins
         if (mvar[i] == 0)
            fInvErrors[i] = 1;
         else
            fInvErrors[i] = 1.0/std::sqrt(mvar[i]);
      }

  }


  /**

  Constructor which initializes the multi-dimensional model function.

  @param modelFCN the model function used for describing the data.

  @param meas vector containing the measured values.

  @param pos vector containing the x values corresponding to the
  measurements

  @param mvar vector containing the variances corresponding to each
  measurement (where the variance equals the standard deviation squared).
  If the variances are zero, a Value of 1 is used (as it is done in ROOT/PAW)

  */

  FumiliStandardChi2FCN(const ParametricFunction& modelFCN, const std::vector<double>& meas,
                        const std::vector<std::vector<double> >& pos,
                        const std::vector<double>& mvar)
   { //this->fModelFCN = &modelFunction;
      this->SetModelFunction(modelFCN);

      assert(meas.size() == pos.size());
      assert(meas.size() == mvar.size());
      fMeasurements = meas;
      fPositions = pos;
      // correct for variance == 0
      unsigned int n = mvar.size();
      fInvErrors.resize(n);
      for (unsigned int i = 0; i < n; ++i)
      {
         // PAW/ROOT hack : use 1 for 0 entries bins
         if (mvar[i] == 0)
            fInvErrors[i] = 1;
         else
            fInvErrors[i] = 1.0/std::sqrt(mvar[i]);
      }

  }




  ~FumiliStandardChi2FCN() {}





  /**

  Evaluates the model function for the different measurement points and
  the Parameter values supplied, calculates a figure-of-merit for each
  measurement and returns a vector containing the result of this
  evaluation. The figure-of-merit is (Value predicted by the model
  function-measured Value)/standard deviation.

  @param par vector of Parameter values to feed to the model function.

  @return A vector containing the figures-of-merit for the model function evaluated
  for each set of measurements.

  \todo What to do when the variances are 0???!! (right now just pushes back 0...)

  */

  std::vector<double> Elements(const std::vector<double>& par) const;



  /**

  Accessor to the position of the measurement (x coordinate).

  @param Index Index of the measuerement the position of which to return.

  @return the position of the measurement.

  */

  virtual const std::vector<double> & GetMeasurement(int Index) const;


  /**

  Accessor to the number of measurements used for calculating
  the chi-square.

  @return the number of measurements.

  */

  virtual int GetNumberOfMeasurements() const;


  /**

  Evaluate function Value, Gradient and Hessian using Fumili approximation, for values of parameters p
  The resul is cached inside and is return from the FumiliFCNBase::Value ,  FumiliFCNBase::Gradient and
  FumiliFCNBase::Hessian methods

  @param par vector of parameters

  **/

  virtual  void EvaluateAll( const std::vector<double> & par );


 private:


  std::vector<double> fMeasurements;
  // support multi dim coordinates
  std::vector<std::vector<double> > fPositions;
  std::vector<double> fInvErrors;
};

  }  // namespace Minuit2

}  // namespace ROOT

#endif  // ROOT_Minuit2_FumiliStandardChi2FCN
