#ifndef _FW_LMO_IMPL_H_
#define _FW_LMO_IMPL_H_

#include <iostream>
#include <limits>
#include <unordered_map>
#include <iomanip>

#include "lmo.h"
#include "lmo_type.h"

// forward definition of oracles

double inner_call_oracle_enumeration_cpu(
    const int n,
    const vec_t<SCIP_Longint> weights,
    const SCIP_Longint capacity,
    const vec_t<double> direction,
    vec_t<double>& vertex
);


// forward definition of oracle
void call_knapsack_oracle(
   SCIP*                      scip,
   const int                  dim,
   const vec_t<SCIP_Longint>& weights,
   const SCIP_Longint         capacity,
   const vec_t<double>&       direction,
   double&                    objval,
   vec_t<double>&             vertex,
   const int                  threshold,
   oracle_t                   use_oracle = oracle_t::REF_ORACLE
   );

template<typename ix_t, typename val_t>
KnapsackOracle<ix_t, val_t>::KnapsackOracle(
   SCIP*                        scip,       // scip instance
   const int                    dim,        // dimension of knapsack
   const vec_t<SCIP_Longint>&   weights,    // integer weights of knapsack
   const SCIP_Longint           capacity    // capacity of knapsack
   )
   : m_scip(scip), m_dim(dim), m_weights(weights), m_capacity(capacity)
{
}

template<typename ix_t, typename val_t>
KnapsackOracle<ix_t, val_t>::~KnapsackOracle()
{
   m_scip = 0;
}

//! linear maximization in given direction with resulting vertex
template<typename ix_t, typename val_t>
val_t
KnapsackOracle<ix_t, val_t>::operator()(
   const vec_t<val_t>& direction,
   vec_t<val_t>&       vertex
   )
{
   val_t objval;

   assert( direction.size() == m_dim );
   assert( vertex.size() == m_dim );
   
#ifdef GPU_ORACLE
   oracle_t oracle = oracle_t::CUDA_ORACLE;
#elif defined HYBRID_ORACLE
   oracle_t oracle = oracle_t::CPU_HYBRID;
#else
   oracle_t oracle = oracle_t::REF_ORACLE;
#endif

#ifdef ORACLE_THRESHOLD
   #define THRESHOLD ORACLE_THRESHOLD
#else
   #define THRESHOLD 8
#endif

   call_knapsack_oracle(m_scip, m_dim, m_weights, m_capacity, direction, objval, vertex, THRESHOLD, oracle);

   return objval;
}

#endif // _FW_LMO_IMPL_H_
