/*-------------------------------------------------------------------------------------*/
/*  NOMAD - Nonsmooth Optimization by Mesh Adaptive Direct search - version 3.0.0      */
/*                                                                                     */
/*  Copyright (C) 2001-2008  Mark Abramson        - the Boeing Company, Seattle        */
/*                           Charles Audet        - Ecole Polytechnique, Montreal      */
/*                           Gilles Couture       - Ecole Polytechnique, Montreal      */
/*                           John Dennis          - Rice University, Houston           */
/*                           Sebastien Le Digabel - Ecole Polytechnique, Montreal      */
/*                                                                                     */
/*  funded in part by AFOSR and Exxon Mobil                                            */
/*                                                                                     */
/*  Author: Sebastien Le Digabel                                                       */
/*                                                                                     */
/*  Contact information:                                                               */
/*    Ecole Polytechnique de Montreal - GERAD                                          */
/*    C.P. 6079, Succ. Centre-ville, Montreal (Quebec) H3C 3A7 Canada                  */
/*    e-mail: Sebastien.Le.Digabel@gerad.ca                                            */
/*    phone : 1-514-340-6053 #6673                                                     */
/*    fax   : 1-514-340-5665                                                           */
/*                                                                                     */
/*  This program is free software: you can redistribute it and/or modify it under the  */
/*  terms of the GNU General Public License as published by the Free Software          */
/*  Foundation, either version 3 of the License, or (at your option) any later         */
/*  version.                                                                           */
/*                                                                                     */
/*  This program is distributed in the hope that it will be useful, but WITHOUT ANY    */
/*  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A    */
/*  PARTICULAR PURPOSE.  See the GNU General Public License for more details.          */
/*                                                                                     */
/*  You should have received a copy of the GNU General Public License along with this  */
/*  program. If not, see <http://www.gnu.org/licenses/>.                               */
/*                                                                                     */
/*  You can find information on the NOMAD software at www.gerad.ca/nomad               */
/*-------------------------------------------------------------------------------------*/
#include "Evaluator_Control.hpp"
using namespace std;

/*-----------------------------------*/
/*   static members initialization   */
/*-----------------------------------*/
bool Evaluator_Control::_force_quit = false;

/*---------------------------------------------------------*/
/*                       constructor                       */
/*---------------------------------------------------------*/
Evaluator_Control::Evaluator_Control ( const Parameters & p             ,
				       Stats            & stats         ,
				       Evaluator        * ev            ,
				       ostream          & out             )
  : _p          ( p                                    ) ,
    _ev         ( ev                                   ) ,
    _delete_ev  ( false                                ) ,
    _cache      ( out , p.get_display_degree() , false ) ,
    _sgte_cache ( out , p.get_display_degree() , true  ) ,
    _stats      ( stats                                ) ,
    _out        ( out                                  )   {

  // Evaluator init:
  if ( !_ev ) {
    _ev = ( _p.get_index_obj().size() > 1 ) ? new Multi_Obj_Evaluator ( p , out ):
                                              new Evaluator           ( p , out );
    _delete_ev = true;
  }

  // caches init (we only load cache file points with m black-box outputs):
  int    m = p.get_bb_nb_outputs();
  string file_name;

  if ( !_p.get_cache_file().empty() ) {
    file_name = _p.get_problem_dir() + _p.get_cache_file();
    if ( !_cache.load ( file_name , &m )  && _p.get_display_degree() > 1    )
      out << "\nWarning (" << __FILE__ << ", " << __LINE__
	  << "): could not load (or create) the cache file " << file_name << "\n\n";
  }

  if ( !_p.get_sgte_cache_file().empty() ) {
    file_name = _p.get_problem_dir() + _p.get_sgte_cache_file();
    if ( !_sgte_cache.load ( file_name , &m ) && _p.get_display_degree() > 1        )
      out << "\nWarning (" << __FILE__ << ", " << __LINE__
	  << "): could not load (or create) the surrogate cache file "
	  << file_name << "\n\n";
  }
}

/*---------------------------------------------------------*/
/*                        destructor                       */
/*---------------------------------------------------------*/
Evaluator_Control::~Evaluator_Control ( void ) {
  if ( _delete_ev )
    delete _ev;
}

/*---------------------------------------------------------*/
/*                     save the caches                     */
/*---------------------------------------------------------*/
bool Evaluator_Control::save_caches ( bool overwrite ) {
  bool b1 = _cache.save      (overwrite);
  bool b2 = _sgte_cache.save (overwrite);
  if ( !b1 && _p.get_display_degree() > 1 )
    _out << "\nWarning (" << __FILE__ << ", " << __LINE__
	 << "): could not save the cache file "
	 << _p.get_problem_dir() << _p.get_cache_file() << "\n\n";
  if ( !b2 && _p.get_display_degree() > 1 )
    _out << "\nWarning (" << __FILE__ << ", " << __LINE__
	 << "): could not save the surrogate cache file "
	 << _p.get_problem_dir() << _p.get_sgte_cache_file() << "\n\n"; 
  return b1 && b2;
}

/*---------------------------------------------------------*/
/*         process an already evaluated Eval_Point         */
/*---------------------------------------------------------*/
success_type Evaluator_Control::process_eval_point ( const Eval_Point & x       ,
						     Barrier          & barrier   ) const {
  // initial checks:
  if ( !x.is_eval_ok() || !x.is_EB_ok() )
    return _UNSUCCESSFUL_;
  
  // insertion of the Eval_Point in the barrier:
  success_type success = barrier.insert(x);

  // call virtual method Evaluator::update_success():
  if ( success == _FULL_SUCCESS_ )
    _ev->update_success();

  return success;
}

/*---------------------------------------------------------*/
/*      count the output stats (STAT_SUM and STAT_AVG)     */
/*---------------------------------------------------------*/
void Evaluator_Control::count_output_stats ( const Eval_Point & x ) {

  const Point & bbo   = x.get_bb_outputs();
  int           i_sum = _p.get_index_stat_sum();
  int           i_avg = _p.get_index_stat_avg();

  // STAT_SUM:
  if ( i_sum >= 0 )
    _stats.update_stat_sum ( bbo[i_sum] );

  // STAT_AVG:
  if ( i_avg >= 0 )
    _stats.update_stat_avg ( bbo[i_avg] );
}

/*-------------------------------------------------------------------*/
/*                file displays for parameter STATS_FILE             */
/*-------------------------------------------------------------------*/
void Evaluator_Control::stats_file ( const string        & file_name ,
				     const NOMAD::Double & f         ,
				     const NOMAD::Double & h         ,
				     const Point         * sol       ,
				     const Point         * bbo         ) const {

  string   fn = _p.get_problem_dir() + file_name;
  ofstream out ( fn.c_str() , ios::app );

  if ( !out.fail() )
    display_stats ( false , out , _p.get_stats_file() , f , h , sol , bbo );
  else if ( _p.get_display_degree() > 1 )
    _out << "\nWarning (" << __FILE__ << ", " << __LINE__
	 << "): could not save information in stats file \""
	 << file_name << "\"\n\n";

  out.close();
}

/*-------------------------------------------------------------------*/
/*  display stats during Mads::run() for minimal and normal display  */
/*-------------------------------------------------------------------*/
void Evaluator_Control::display_stats ( bool                  header ,
					ostream             & out    ,
					const list<string>  & stats  ,
					const NOMAD::Double & f      ,
					const NOMAD::Double & h      ,
					const Point         * sol    ,
					const Point         * bbo      ) const {
  if ( stats.empty() ) {
    out << endl;
    return;
  }

  if ( header )
    out << "\tstats:\n";
  out << "\t";

  list<string>::const_iterator it , end = stats.end();
  for ( it = stats.begin() ; it != end ; ++it ) {
    if ( it->empty() )
      out << "\t";
    else {

      if ( header )
	out << *it;

      else {
	display_stats_type dst = get_display_stats_type (*it);
	switch (dst) {
	case _DS_UNDEFINED_:
	  out << *it;
	  break;
	case _DS_OBJ_:
	  out << f;
	  break;
	case _DS_SIM_BBE_:
	  out << _stats.get_sim_bb_eval();
	  break;
	case _DS_BBE_:
	  out << _stats.get_bb_eval();
	  break;
	case _DS_EVAL_:
	  out << _stats.get_eval();
	  break;
	case _DS_TIME_:
	  out << _stats.get_real_time();
	  break;
	case _DS_STAT_SUM_:
	  out << _stats.get_stat_sum();
	  break;
	case _DS_STAT_AVG_:
	  out << _stats.get_stat_avg();
	  break;
	case _DS_BBO_:
	  display_stats_point ( out , stats , it , bbo );
	  break;
	case _DS_SOL_:
	  display_stats_point ( out , stats , it , sol );
	  break;
	}
      }
    }
  }
  out << endl;
}

/*---------------------------------------------------------*/
/*    display a point with DISPLAY_STATS (or STATS_FILE)   */
/*---------------------------------------------------------*/
void Evaluator_Control::display_stats_point ( ostream                      & out           ,
					      const list<string>           & display_stats ,
					      list<string>::const_iterator & it            ,
					      const Point                  * x      ) const {
  if (x) {
    int n = x->size();
    string s1;
    if ( it != display_stats.begin() ) {
      s1 = *(--it);
      ++it;
    }
    string s2;
    ++it;
    if ( it != display_stats.end() )
      s2 = *it;
    else if ( s2.empty() )
      --it;
    for ( int i = 0 ; i < n ; ++i ) {
      if ( !s1.empty() && i > 0 )
	out << s1;
      out << (*x)[i];
      if ( !s1.empty() )
	out << s1;
      if ( !s2.empty() && i < n-1 )
	out << " " << s2;
      out << " ";
    }
  }
}

/*---------------------------------------------------------*/
/*  save the solution file (SOLUTION_FILE) or update the   */
/*  history file (HISTORY_FILE)                            */
/*---------------------------------------------------------*/
void Evaluator_Control::write_sol_or_his_file ( const string     & file_name      ,
						const Eval_Point & x              ,
						int                display_degree ,
						bool               is_sol           ) const {

  // if is_sol == true: save the solution file
  //              else: update the history file 

  bool     failed = false;
  ofstream out;

  if ( is_sol )
    out.open ( file_name.c_str() );
  else
    out.open ( file_name.c_str() , ios::app );

  if ( !out.fail() ) {

    out.setf(ios::fixed);
    out.precision(DISPLAY_PRECISION_BB);

    // solution display:
    if ( is_sol ) {
      if ( _p.get_bb_input_include_seed() )
	out << _p.get_seed() << endl;
      if ( _p.get_bb_input_include_tag() )
	out << x.get_tag() << endl;
      x.Point::display ( out , "\n" , -1 , -1 );
      out << endl;
    }

    // history display:
    else {
      x.Point::display ( out , " " , -1 , -1 );
      out << " ";
      x.get_bb_outputs().Point::display ( out , " " , -1 , -1 );
      out << endl;
    }

    if ( out.fail() )
      failed = true;
  }
  else
    failed = true;

  out.close();

  if ( failed && display_degree > 1 )
    _out << "\nWarning (" << __FILE__ << ", " << __LINE__
	 << "): could not "
	 << ( is_sol ? "save the current solution" : "update the history")
	 << " in \""
	 << file_name << "\"\n\n";
}

/*---------------------------------------------------------*/
/*                   display evaluation result             */
/*---------------------------------------------------------*/
void Evaluator_Control::display_eval_result ( const Eval_Point & x                ,
					      int                display_degree   ,
					      search_type        search           ,
					      success_type       one_eval_success ,
					      success_type       success            ) const {
  // surrogate evaluation:
  if ( x.is_surrogate() ) {
    if ( display_degree > 2 ) {
      _out << "\n\tpoint #" << x.get_tag() << " surrogate evaluation: ";
      if ( x.is_eval_ok() ) {
	_out << "h=";
	if ( x.get_h().is_defined() )
	  _out << x.get_h();
	else
	  _out << "inf (extr. barrier)";
	_out << " f=" << x.get_f();
      }
      else
	_out << "failed";
      _out << endl;
    }
    return;
  }

  // from this point, only 'true' evaluations are considered.

  // update the history file:
  const string & his_file = _p.get_history_file();
  if ( !his_file.empty() )
    write_sol_or_his_file ( _p.get_problem_dir() + his_file ,
			    x                               ,
			    display_degree                  ,
			    false                             );

  // success displays:
  if ( one_eval_success != _UNSUCCESSFUL_ && one_eval_success >= success ) {

    // save the solution file:
    const string & sol_file = _p.get_solution_file();
    if ( !sol_file.empty() && x.is_feasible(_p.get_h_min()) )
      write_sol_or_his_file ( _p.get_problem_dir() + sol_file ,
			      x                               ,
			      display_degree                  ,
			      true                              );

    bool ds_ok = one_eval_success==_FULL_SUCCESS_ && x.is_feasible(_p.get_h_min());
    
    // normal display:
    if ( display_degree == 1 || display_degree == 2 ) {
      if ( ds_ok )
	display_stats ( false                  ,
			_out                   ,
			_p.get_display_stats() ,
			x.get_f()              ,
			x.get_h()              ,
			&x                     ,
			&x.get_bb_outputs()      );
    }

    // detailed display:
    else if ( display_degree > 3 ||
	      (display_degree > 2 && one_eval_success==_FULL_SUCCESS_) )
      _out << "\n\t" << search << " " << one_eval_success
	   << " point: " << x << endl;

    // file stats:
    if ( ds_ok ) {
      const string & stats_file_name = _p.get_stats_file_name();
      if ( !stats_file_name.empty() )
	stats_file ( stats_file_name , x.get_f() , x.get_h() , &x , &x.get_bb_outputs() );
    }
    
  }
  else if ( display_degree > 3 )
    _out << "\n\t" << search << " " << one_eval_success
	 << " point #" << x.get_tag() << endl;
}

/*----------------------------------------------------------------*/
/*             eval_list_of_points (parallel version)             */
/*----------------------------------------------------------------*/
#ifdef USE_MPI // USE_MPI is defined (or not) by the makefile
void Evaluator_Control::eval_list_of_points
( search_type                search         ,     // IN     : search type
  set<Priority_Eval_Point> & points         ,     // IN/OUT : the list of points
  Barrier                  * barrier        ,     // IN/OUT : the barrier (can be NULL)
  bool                     & stop           ,     // IN/OUT : stopping criteria
  stop_type                & stop_reason    ,     // OUT    : stopping reason
  const Eval_Point        *& new_feas_inc   ,     // OUT    : new feasible incumbent
  const Eval_Point        *& new_infeas_inc ,     // OUT    : new infeasible incumbent
  success_type             & success        ,     // OUT    : type of success
  list<const Eval_Point *> & evaluated_pts    )   // OUT    : list of processed points
{}

/*----------------------------------------------------------------*/
/*              eval_list_of_points (scalar version)              */
/*----------------------------------------------------------------*/
#else
void Evaluator_Control::eval_list_of_points
( search_type                search         ,      // IN     : search type
  set<Priority_Eval_Point> & points         ,      // IN/OUT : the list of points
  Barrier                  * barrier        ,      // IN/OUT : the barrier (can be NULL)
  bool                     & stop           ,      // IN/OUT : stopping criteria
  stop_type                & stop_reason    ,      // OUT    : stopping reason
  const Eval_Point        *& new_feas_inc   ,      // OUT    : new feasible incumbent
  const Eval_Point        *& new_infeas_inc ,      // OUT    : new infeasible incumbent
  success_type             & success        ,      // OUT    : type of success
  list<const Eval_Point *> & evaluated_pts    ) {  // OUT    : list of processed points

  evaluated_pts.clear();

  if ( stop || points.empty() ) {
    stop_reason = _UNKNOWN_;
    return;
  }

  int display_degree = (search==_POLL_) ?
    _p.get_poll_display_degree() : _p.get_search_display_degree();
  if ( display_degree > 2 )
    _out << endl << "begin of Evaluator_Control::eval_list_of_points ("
	 << search << ")" << endl;

  // opportunistic eval. parameters (disabled if search undefined or x0 evaluations):
  bool opportunistic = _p.get_opportunistic_eval();
  if ( search == _X0_ || search == _UNDEFINED_SEARCH_ )
    opportunistic = false;

  const Eval_Point * old_feasible_incumbent   = NULL;
  const Eval_Point * old_infeasible_incumbent = NULL;
  if (barrier) {
    old_feasible_incumbent   = barrier->get_best_feasible();
    old_infeasible_incumbent = barrier->get_best_infeasible();
  }

  NOMAD::Double f0;
  if ( _p.get_opportunistic_min_f_imprvmt().is_defined() && old_feasible_incumbent )
    f0 = old_feasible_incumbent->get_f();

  new_feas_inc   = NULL;
  new_infeas_inc = NULL;
  success        = _UNSUCCESSFUL_;
  stop_reason    = _NO_STOP_;
  
  const Eval_Point                 * cache_x;
  const Eval_Point                 * x;
  check_failed_type                  check_failed_reason;
  bool                               count_eval;
  bool                               one_for_luck     = false;
  success_type                       one_eval_success = _UNSUCCESSFUL_;
  int                                init_nb_eval     = _stats.get_eval();
  int                                nb_success       = 0;
  int                                m                = _p.get_bb_nb_outputs();
  int                                k                = 0;
  int                                nb_points        = static_cast<int>(points.size());

  // main loop (on the list of points):
  // ----------------------------------
  set<Priority_Eval_Point>::iterator it = points.begin() , end = points.end();
  while ( it != end ) {

    x = it->get_point();
    
    // displays:
    if ( display_degree > 2 ) {
      _out << endl << "\t";
      if ( x->is_surrogate() )
	_out << "surrogate ";
      _out << "evaluation "
	   << k+1 << "/" << nb_points << ": point #" << x->get_tag() << " ( ";
      x->Point::display ( _out , " " , 2 , Point::get_display_limit() );
      _out << " )" << endl;
      if ( x->get_direction() )
	_out << "\tdirection  : " << *x->get_direction() << endl;
      if ( x->get_mesh_index() )
	_out << "\tmesh index : "   << *x->get_mesh_index() << endl;
      _out << endl;
    }

    // current point check (# of bb outputs, bounds, integer values, fixed-variables):
    if ( x->check ( m , check_failed_reason ) ) {

      // cache check:
      if ( x->is_in_cache() )
	cache_x = x;

      else {
	    
	// 1. search in 'true' cache:
	cache_x = _cache.find ( x );

	// 2. search in surrogate cache:
	if ( !cache_x && x->is_surrogate() )
	  cache_x = _sgte_cache.find (x);

	// cache hit: we transfer the signature, the direction,
	//   and the mesh index, from x to cache_x:
	if ( cache_x ) {

	  if ( cache_x->is_eval_ok() ) {

	    Eval_Point * modifiable_cache_x = &Cache::get_modifiable_point ( *cache_x );

	    // if wrong number of outputs, we reset cache_x._bb_outputs:
	    if ( cache_x->get_bb_outputs().size() != m )
	      modifiable_cache_x->set_bb_output ( Point(m) );

	    modifiable_cache_x->set_signature        ( x->get_signature       () );
	    modifiable_cache_x->set_direction        ( x->get_direction       () );
	    modifiable_cache_x->set_mesh_index       ( x->get_mesh_index      () );
	    modifiable_cache_x->set_poll_center_type ( x->get_poll_center_type() );

	    // set_f, set_h, and set_EB_ok:
	    _ev->compute_f ( *modifiable_cache_x );
	    _ev->compute_h ( *modifiable_cache_x );
	  }
	}
      }

      // point in cache but evaluation is to be made again:
      if ( cache_x && cache_x->is_eval_ok() && 
   	   ( !cache_x->get_f().is_defined() ||
   	     ( cache_x->is_EB_ok()                      &&
   	       !cache_x->get_bb_outputs().is_complete() &&
   	       cache_x->get_h().is_defined()            &&
	       barrier                                  &&
   	       cache_x->get_h() < barrier->get_h_max()      ) ) ) {
	x       = cache_x; // previous x will be deleted by ~Priority_Eval_Point()
	cache_x = NULL;
      }

      // point in cache:
      // ---------------
      if ( cache_x ) {

	_stats.add_cache_hit();

	// displays:
	if ( display_degree > 2 ) {
	  _out << "\t";
	  if ( cache_x->is_surrogate() )
	    _out << "surrogate ";
	  _out << "cache hit: #" << x->get_tag()
	       << " --> #" << cache_x->get_tag() << endl;
	}

	// we process the Eval_Point taken in cache:
	one_eval_success = ( barrier ) ?
	  process_eval_point ( *cache_x , *barrier ) :
	  _UNSUCCESSFUL_;

	// (particular case: the cache point is not a surrogate
	// and the original point was: this point will be treated in
	// the next eval_lop call)

	x = cache_x; // previous x will be deleted by ~Priority_Eval_Point()
      }

      // point not in cache:
      // -------------------
      else {

	// black-box or surrogate evaluations are allowed:
	if ( (!x->is_surrogate() && _p.get_max_bb_eval  () != 0) ||
	     ( x->is_surrogate() && _p.get_max_sgte_eval() != 0)    ) {

	  Eval_Point * eval_x = &Cache::get_modifiable_point ( *x );
	  count_eval          = true;

	  // evaluation of the point:
	  // ------------------------
	  if ( _ev->eval_x ( *eval_x                                            ,
			     (barrier) ? barrier->get_h_max() : NOMAD::Double() ,
			     count_eval                                           ) ) {
	      
	    eval_x->set_eval_ok ( true );

	    // set_f, set_h and set_EB_ok:
	    _ev->compute_f ( *eval_x );
	    _ev->compute_h ( *eval_x );

	    // we process the freshly evaluated point:
	    one_eval_success = ( barrier ) ?
	      process_eval_point ( *eval_x , *barrier ) :
	      _UNSUCCESSFUL_;
	  }
	  else {
	    eval_x->set_eval_ok(false);
	    _stats.add_failed_eval();
	  }

	  // insertion in cache even if is_eval_ok == false
	  // ( x will be deleted by ~Cache() ):
	  if ( !x->is_in_cache() )
	    (x->is_surrogate() ? _sgte_cache : _cache).insert (x);

	  // we count the bb evaluation:
	  if ( count_eval ) {
	    if ( x->is_surrogate() )
	      _stats.add_sgte_eval();
	    else
	      _stats.add_bb_eval();
	  }

	  // we count the output stats (STAT_SUM and STAT_AVG):
	  if ( _p.check_stat_sum() || _p.check_stat_avg() ) {

	    count_output_stats (*x);

	    // check STAT_SUM_TARGET:
	    NOMAD::Double sum_target = _p.get_stat_sum_target();
	    if ( sum_target.is_defined() ) {
	      NOMAD::Double sum = _stats.get_stat_sum();
	      if ( !stop && sum.is_defined() && sum >= sum_target ) {
		stop        = true;
		stop_reason = _STAT_SUM_TARGET_REACHED_;
	      }
	    }
	  }
	}

	// check the number of black-box evaluations:
	if ( !stop                     &&
	     _p.get_max_bb_eval() >= 0 &&
	     _stats.get_bb_eval() >= _p.get_max_bb_eval() ) {
	  stop        = true;
	  stop_reason = _MAX_BB_EVAL_REACHED_;
	}
	if ( !stop                       &&
	     _p.get_max_sgte_eval() >= 0 &&
	     _stats.get_sgte_eval() >= _p.get_max_sgte_eval() ) {
	  stop        = true;
	  stop_reason = _MAX_SGTE_EVAL_REACHED_;
	}
      }

      // list of processed points:
      if ( x->is_in_cache() )
	evaluated_pts.push_back (x);

      // check the time:
      if ( !stop                 &&
	   _p.get_max_time() > 0 &&
	   _stats.get_real_time() >= _p.get_max_time() ) {
	stop        = true;
	stop_reason = _MAX_TIME_REACHED_;
      }

      // count an evaluation or a simulated black-box evaluation:
      if ( !x->is_surrogate() ) {
	_stats.add_eval();
	if ( !x->get_current_run() )
	  _stats.add_sim_bb_eval();
      }
      x->set_current_run(true);

      // check the stopping condition MAX_EVAL:
      if ( !stop                 &&
	   _p.get_max_eval() > 0 &&
	   _stats.get_eval() >= _p.get_max_eval() ) {
	stop        = true;
	stop_reason = _MAX_EVAL_REACHED_;
      }

      // check the stopping condition MAX_SIM_BB_EVAL:
      if ( !stop                         &&
	   _p.get_max_sim_bb_eval() >  0 &&
	   _stats.get_sim_bb_eval() >= _p.get_max_sim_bb_eval() ) {
	stop        = true;
	stop_reason = _MAX_SIM_BB_EVAL_REACHED_;
      }

      // check the stopping condition F_TARGET:
      if ( !stop                             &&
	   _p.get_f_target().is_defined()    &&
	   !x->is_surrogate()                &&
	   x->is_eval_ok()                   &&
	   x->is_feasible ( _p.get_h_min() ) &&
	   x->get_f() <= _p.get_f_target()      ) {
	stop        = true;
	stop_reason = _F_TARGET_REACHED_;
      }

      // succes for all the evaluations:
      if ( one_eval_success > success )
	success = one_eval_success;

      // displays:
      display_eval_result ( *x , display_degree , search , one_eval_success , success );

      // stop the evaluations (opportunistic strategy) ?
      if ( k < nb_points - 1 && !x->is_surrogate() ) {
	
	if ( stop ) {
	  _stats.add_interrupted_eval();
	  break;
	}

	if ( opportunistic ) {

	  if ( one_for_luck && one_eval_success != _FULL_SUCCESS_ ) {
	    if ( display_degree > 2 )
	      _out << "\n\topportunistic termination of evaluations (lucky eval)" << endl;
	    _stats.add_interrupted_eval();
	    break;
	  }

	  if ( success == _FULL_SUCCESS_ ) {
	    if ( check_opportunistic_criteria ( display_degree   ,
						one_eval_success ,
						init_nb_eval     ,
						f0               ,
						barrier          ,
						nb_success       ,
						one_for_luck       ) ) {
	      _stats.add_interrupted_eval();
	      break;
	    }
	  }
	}
      }
    }
    // points[k]->check() failed: the point will be deleted by ~Priority_Eval_Point
    else if ( display_degree > 2 )
      _out << "\tcheck failed (" << check_failed_reason << ")" << endl;

    ++it;
    ++k;

    // force quit (by pressing ctrl-c):
    if ( !stop && Evaluator_Control::_force_quit ) {
      stop        = true;
      stop_reason = _CTRL_C_;
    }

  } // end of main loop
    // ----------------

  // barrier update:
  if ( !stop && barrier && !barrier->update ( success ) ) {
    stop        = true;
    stop_reason = _ERROR_;
  }

  if ( display_degree > 2 )
    _out << endl << "end of Evaluator_Control::eval_list_of_points ("
	 << search << ")" << endl;

  // incumbents update:
  if (barrier) {
    const Eval_Point * bf = barrier->get_best_feasible  ();
    const Eval_Point * bi = barrier->get_best_infeasible();
    if ( bf && bf != old_feasible_incumbent )
      new_feas_inc = bf;
    if ( bi && bi != old_infeasible_incumbent )
      new_infeas_inc = bi;
  }

  // the list of eval. points is deleted (only points in cache are kept):
  points.clear();
}

/*----------------------------------------------------------------*/
/*       check the opportunistic strategy stopping criteria       */
/*            return true to stop the evaluations                 */
/*            return false to continue the evaluations            */
/*----------------------------------------------------------------*/
bool Evaluator_Control::check_opportunistic_criteria
( int                   display_degree   ,
  success_type          one_eval_success ,
  int                   init_nb_eval     ,
  const NOMAD::Double & f0               ,
  const Barrier       * barrier          ,
  int                 & nb_success       ,
  bool                & one_for_luck       ) {

  int           min_nb_success = _p.get_opportunistic_min_nb_success();
  int           min_eval       = _p.get_opportunistic_min_eval();
  NOMAD::Double min_f_imprvmt  = _p.get_opportunistic_min_f_imprvmt();
  bool          lucky_eval     = _p.get_opportunistic_lucky_eval();

  // min_nb_success:
  if ( min_nb_success > 0 ) {

    if ( one_eval_success == _FULL_SUCCESS_ )
      ++nb_success;

    if ( nb_success < min_nb_success ) {

      if ( display_degree > 3 )
	_out << "\n\topportunistic strategy (nb_success=" << nb_success
	     << " < min_nb_success=" << min_nb_success << "): continue evaluations" << endl;
      
      return false;
    }
  }

  // min_eval:
  if ( min_eval > 0 ) {
    
    int eval = _stats.get_eval() - init_nb_eval;

    if ( eval < min_eval ) {

      if ( display_degree > 3 )
	_out << "\n\topportunistic strategy (eval=" << eval
	     << " < min_eval=" << min_eval << "): continue evaluations" << endl;

      return false;
    }
  }

  // min_f_imprvmt:
  if ( barrier && min_f_imprvmt.is_defined() ) {

    if ( f0.is_defined() && barrier->get_best_feasible() ) {

      NOMAD::Double f = barrier->get_best_feasible()->get_f();

      if ( f.is_defined() ) {
		  
	NOMAD::Double f_imprvmt = ( f != 0.0 ) ? 100.0 * (f0 - f) / f.abs() : 100.0;
	
	if ( f_imprvmt < min_f_imprvmt ) {

	  if ( display_degree > 3 )
	    _out << "\n\topportunistic strategy (f_improvement="
		 << f_imprvmt << " < min_f_imprvmt=" << min_f_imprvmt
		 << "): continue evaluations" << endl;

	  return false;
	}
      }
    }
  }

  // lucky_eval:
  if ( lucky_eval && one_eval_success == _FULL_SUCCESS_ ) {
    one_for_luck = true;

    if ( display_degree > 3 )
      _out << "\n\topportunistic strategy: one more evaluation for luck" << endl;

    return false;
  }

  if ( display_degree > 3 )
    _out << "\n\topportunistic strategy: terminate evaluations" << endl;

  return true;
}

#endif
