// @(#)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 <cassert>
#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() override {}

   /**

   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 override;

   /**

   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.

   */

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

   /**

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

   @return the number of measurements.

   */

   int GetNumberOfMeasurements() const override;

   /**

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

   @param par vector of parameters

   **/

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

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
