/**
 * @file store_best_coordinates.hpp
 * @author Marcus Edel
 *
 * Implementation of the store best coordinates callback function.
 *
 * ensmallen is free software; you may redistribute it and/or modify it under
 * the terms of the 3-clause BSD license.  You should have received a copy of
 * the 3-clause BSD license along with ensmallen.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef ENSMALLEN_CALLBACKS_STORE_BEST_COORDINATES_HPP
#define ENSMALLEN_CALLBACKS_STORE_BEST_COORDINATES_HPP

namespace ens {

/**
 * Store best coordinates function, based on the Evaluate callback function.
 *
 * @tparam MatType Type of the model coordinates (arma::colvec, arma::mat,
 *     arma::sp_mat or arma::cube).
 */
template<typename ModelMatType = arma::mat>
class StoreBestCoordinates
{
 public:
  /**
   * Set up the store best model class, which keeps the best-performing
   * coordinates and objective.
   */
  StoreBestCoordinates() : bestObjective(std::numeric_limits<double>::max())
  { /* Nothing to do here. */ }

  /**
   * Callback function called after any call to Evaluate().
   *
   * @param optimizer The optimizer used to update the function.
   * @param function Function to optimize.
   * @param coordinates Starting point.
   * @param objective Objective value of the current point.
   */
  template<typename OptimizerType, typename FunctionType, typename MatType>
  void Evaluate(OptimizerType& /* optimizer */,
                FunctionType& /* function */,
                const MatType& coordinates,
                const double objective)
  {
    if (objective < bestObjective)
    {
      bestObjective = objective;
      bestCoordinates = coordinates;
    }
  }

  //! Get the best coordinates.
  ModelMatType const& BestCoordinates() const { return bestCoordinates; }
  //! Modify the best coordinates.
  ModelMatType& BestCoordinates() { return bestCoordinates; }

  //! Get the best objective.
  double const& BestObjective() const { return bestObjective; }
  //! Modify the best objective.
  double& BestObjective() { return bestObjective; }

 private:
  //! Locally-stored best objective.
  double bestObjective;

  //! Locally-stored best model coordinates.
  ModelMatType bestCoordinates;
};

} // namespace ens

#endif
