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

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

#include "Minuit2/MnConfig.h"
#include "Minuit2/FumiliMinimizer.h"
#include "Minuit2/MinimumSeedGenerator.h"
#include "Minuit2/FumiliGradientCalculator.h"
#include "Minuit2/Numerical2PGradientCalculator.h"
#include "Minuit2/AnalyticalGradientCalculator.h"
#include "Minuit2/MinimumBuilder.h"
#include "Minuit2/MinimumSeed.h"
#include "Minuit2/FunctionMinimum.h"
#include "Minuit2/MnUserParameterState.h"
#include "Minuit2/MnUserParameters.h"
#include "Minuit2/MnUserTransformation.h"
#include "Minuit2/MnUserFcn.h"
#include "Minuit2/FumiliFCNBase.h"
#include "Minuit2/FumiliFCNBase.h"
#include "Minuit2/FumiliGradientCalculator.h"
#include "Minuit2/FCNGradientBase.h"
#include "Minuit2/MnStrategy.h"
#include "Minuit2/MnPrint.h"

namespace ROOT {

   namespace Minuit2 {


// for Fumili implement Minimize here because need downcast


FunctionMinimum FumiliMinimizer::Minimize(const FCNBase& fcn, const MnUserParameterState& st, const MnStrategy& strategy, unsigned int maxfcn, double toler) const {
   // Minimize using Fumili. Create seed and Fumili gradient calculator.
   // The FCNBase passed must be a FumiliFCNBase type otherwise method will fail !

   MnUserFcn mfcn(fcn, st.Trafo());
   Numerical2PGradientCalculator gc(mfcn, st.Trafo(), strategy);

   unsigned int npar = st.VariableParameters();
   if(maxfcn == 0) maxfcn = 200 + 100*npar + 5*npar*npar;
   //FUMILI needs much less function calls
   maxfcn = int(0.1*maxfcn);

   MinimumSeed mnseeds = SeedGenerator()(mfcn, gc, st, strategy);

   // downcast fcn

   //std::cout << "FCN type " << typeid(&fcn).Name() << std::endl;

   FumiliFCNBase * fumiliFcn = dynamic_cast< FumiliFCNBase *>( const_cast<FCNBase *>(&fcn) );
   if ( !fumiliFcn ) {
      MN_ERROR_MSG("FumiliMinimizer: Error : wrong FCN type. Try to use default minimizer");
      return  FunctionMinimum(mnseeds, fcn.Up() );
   }


   FumiliGradientCalculator fgc(*fumiliFcn, st.Trafo(), npar);
#ifdef DEBUG
   std::cout << "Minuit::Minimize using FumiliMinimizer" << std::endl;
#endif
   return ModularFunctionMinimizer::Minimize(mfcn, fgc, mnseeds, strategy, maxfcn, toler);
}



FunctionMinimum FumiliMinimizer::Minimize(const FCNGradientBase& fcn, const MnUserParameterState& st, const MnStrategy& strategy, unsigned int maxfcn, double toler) const {
   // Minimize using Fumili. Case of interface is a FCNGradientBase.
   // Normally other method is used  - probably this could be removed (t.b.i.)

   // need MnUserFcn
   MnUserFcn mfcn(fcn, st.Trafo() );
   AnalyticalGradientCalculator gc(fcn, st.Trafo());

   unsigned int npar = st.VariableParameters();
   if(maxfcn == 0) maxfcn = 200 + 100*npar + 5*npar*npar;

   MinimumSeed mnseeds = SeedGenerator()(mfcn, gc, st, strategy);

   // downcast fcn

   FumiliFCNBase * fumiliFcn = dynamic_cast< FumiliFCNBase *>( const_cast<FCNGradientBase *>(&fcn) );
   if ( !fumiliFcn ) {
      MN_ERROR_MSG("FumiliMinimizer: Error : wrong FCN type. Try to use default minimizer");
      return  FunctionMinimum(mnseeds, fcn.Up() );
   }


   FumiliGradientCalculator fgc(*fumiliFcn, st.Trafo(), npar);
#ifdef DEBUG
   std::cout << "Minuit::Minimize using FumiliMinimizer" << std::endl;
#endif
   return ModularFunctionMinimizer::Minimize(mfcn, fgc, mnseeds, strategy, maxfcn, toler);

}

   }  // namespace Minuit2

}  // namespace ROOT
