////////////////////////////////////////////////////////////////////////
/// \class RAT::Optimisers::Powell
///
/// \brief  Optimisation using Powell's methods in multi-dimensions
///
/// \author Ian Coulter <icoulter@hep.upenn.edu> -- contact person
///
/// REVISION HISTORY:\n
///     01/09/2011 : I Coulter - new file \n
///     24/03/2014 : M Mottram - fixed to work for n dimensions \n
///
/// \details Optimisation using Powell's method (from Numerical Recipes)
///
////////////////////////////////////////////////////////////////////////

#ifndef __RAT_Optimiser_Powell_
#define __RAT_Optimiser_Powell_

#include <string>
#include <vector>
#include <math.h>

#include <RAT/Optimiser.hh>

namespace RAT
{

namespace Optimisers
{

class Powell : public Optimiser
{
public:
  virtual std::string GetName() const { return Powell::Name(); }

  static std::string Name() { return std::string( "powell" ); }

  /// Initialise the Minuit Optimiser
  void Initialise( const std::string& param );

  void BeginOfRun( DS::Run& ) { }

  void EndOfRun( DS::Run& ) { }

  virtual double Minimise();

  virtual double Maximise();

protected:
  /// Invoke the Minuit optimiser
  double
  PowellOptimise();

  void ReportErrors(std::vector<double> &p, const std::vector<double> &initP, const double initY, int &iter, double &fret, std::vector<double> &posErrors, std::vector<double> &negErrors, int& errorsValid);
  // Copied from Numerical Recipes (Chapter 10?)
  void powell(std::vector<double> &p, std::vector<double>& xi, double ftol, int &iter, double &fret, double& initY);
  double f1dim(double x);
  void linmin(std::vector<double> &p,std::vector<double> &xi, double &fret);
  void mnbrak_linmin(double &ax, double &bx, double &cx, double &fa, double &fb, double &fc);
  float brent_linmin(double ax, double xx, double bx, double TOL, double &xmin);
  void shift(double &a, double &b, double &c, double d);

  std::vector<double> fPcom, fXicom;
  int fNcom;
  int fIterMax;

  double fUp; ///< Increase in minimisation parameter value for error reporting
  double fMinFactor; ///< Always minimises, thus to maximise set this to -1
};

} //::Optimiser

} //::RAT

#endif