#ifndef _SCALAR_FW_FW_H_
#define _SCALAR_FW_FW_H_

#include <iostream>
#include <vector>
#include <fstream>

#include "types.h"
#include "active_set.h"
#include "../lc_clock.h"
#include "fwexitcodes.h"

template<typename ix_t, typename val_t, class lmo_t>
class AwayStepFrankWolfe
{
public:

   //! store result data
   struct solution_info_t
   {
      val_t        primal;         // primal value
      val_t        dual_gap;       // dual gap
      val_t        norm_viol;      // normalized violation of cut
      exitcode_t   exitcode;       // exit code
      int          noraclecalls;   // number of oracle calls
      int          niters;         // number of FW iterations
   };

   AwayStepFrankWolfe(
      const ix_t              dim,
      fun_t<ix_t, val_t>&     obj_fun,
      ip_fun_t<ix_t, val_t>&  grad_fun,
      lmo_t&                  lmo,
      bool                    verbose = false,
      val_t                   epsilon = 1e-9
      );

   ~AwayStepFrankWolfe();

   bool operator()(
      const vec_t<val_t>& xsep,                //! (fractional) point to be separated
      const vec_t<val_t>& xint,                //! relative interior point
      LC_CLOCK*           fwclock,             //! clock for measuring total FW time
      LC_CLOCK*           oracleclock,         //! clock for measuring oracle time
      solution_info_t&    result,              //! result data
      const ix_t          max_iters = 10000,   //! maximal number of iterations
      const val_t         primallimit = 1e-6,  //! stop FW if primal value falls below this limit
      const ix_t          print_ln = 100       //! output frequency
      );

   ix_t lmo_calls();

   const vec_t<val_t>& get_vertex();
   const vec_t<val_t>& get_solution();

 protected:
   void lazy_afw_step(
      const vec_t<val_t>& x,                // current iterate
      const vec_t<val_t>& x_sep,            // point to be separated
      const vec_t<val_t>& gradient,         // current gradient
      LC_CLOCK*           oracleclock,      //! clock for measuring oracle time
      vec_t<val_t>&       v,                // current vertex for minimizing gradient
      val_t&              phi,              // phi value
      val_t&              norm_viol,        // violation of point
      vec_t<val_t>&       d,                // new direction
      val_t&              gamma_max,        // steplength
      bool&               away_step_taken,  // whether an away step was taken
      bool&               fw_step_taken,    // whether a FW step was taken
      bool&               lazy_step_taken,  // whether a lazy step was taken
      bool&               dual_step_taken,  // whether a dual step was taken
      const val_t         K
      );

   val_t calc_norm_viol(
      const vec_t<val_t>& gradient,        //! gradient, i.e., normal vector of cut
      const vec_t<val_t>& v,               //! optimal vertex
      const vec_t<val_t>& xsep             //! point to be separated
      );

   const ix_t m_dim;   //! dimension

   fun_t<ix_t, val_t>& m_obj_fun;      //! objective function
   ip_fun_t<ix_t, val_t>& m_grad_fun;  //! gradient function

   lmo_t& m_lmo;         //! linear optmization oracle
   ix_t m_lmo_calls;     //! number of LMO calls

   vec_t<val_t> m_v;                   //! current vertex
   ActiveSet<ix_t, val_t> m_as;        //! active set

   bool m_verbose;       //! Output verbose messages?
   val_t m_epsilon;      //! for numerical comparisons
};

#endif // _SCALAR_FW_FW_H_
