/*-------------------------------------------------------------------------------------*/
/*  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 "Priority_Eval_Point.hpp"
using namespace std;

/*----------------------------------------------------------------*/
/*                            destructor                          */
/*----------------------------------------------------------------*/
Priority_Eval_Point::~Priority_Eval_Point ( void ) {
  if ( _delete_point && !get_point()->is_in_cache() )
    delete get_element();
}

/*----------------------------------------------------------------*/
/*  . specific Priority_Eval_Point elements of comparison         */
/*  . this method is defined virtual in Set_Element so that       */
/*    Priority_Eval_Point::operator < (Set_Element x) can invoke  */
/*    it on x (which is in fact a Priority_Eval_Point)            */
/*  . this avoids an expensive downcast in operator <             */
/*----------------------------------------------------------------*/
void Priority_Eval_Point::get_priority_criteria
( NOMAD::Double & f_sgte             ,
  NOMAD::Double & h_sgte             ,
  NOMAD::Double & angle_success_dir  ,
  NOMAD::Double & angle_simplex_grad   ) const {
  f_sgte             = _f_sgte;
  h_sgte             = _h_sgte;
  angle_success_dir  = _angle_success_dir;
  angle_simplex_grad = _angle_simplex_grad;
}

/*------------------------------------------------------*/
/*                  comparison operator                 */
/*  . x1 < x2 true if x1 should be evaluated before x2  */
/*  . x is a Priority_Eval_Point                        */
/*------------------------------------------------------*/
bool Priority_Eval_Point::operator < ( const Set_Element<Eval_Point> & x ) const {

  if ( this == &x )
    return false;

  const Eval_Point * x1 = get_element();
  const Eval_Point * x2 = x.get_element();
  
  // criterion 1: user criterion:
  // ------------
  const NOMAD::Double uep1 = x1->get_user_eval_priority();
  if ( uep1.is_defined() ) {
    const NOMAD::Double uep2 = x2->get_user_eval_priority();
    if ( uep2.is_defined() ) {
      if ( uep1 > uep2 )
	return true;
      if ( uep2 > uep1 )
	return false;
    }
  }

  // specific Priority_Eval_Point elements of comparison:
  NOMAD::Double x_f_sgte;             
  NOMAD::Double x_h_sgte;             
  NOMAD::Double x_angle_success_dir;
  NOMAD::Double x_angle_simplex_grad;

  x.get_priority_criteria ( x_f_sgte             ,
			    x_h_sgte             ,
			    x_angle_success_dir  ,
			    x_angle_simplex_grad   );

  // criterion 2: give priority to already evaluated cache points:
  // ------------
  if ( x1->is_in_cache() && !x2->is_in_cache() )
    return true;
  if ( x2->is_in_cache() && !x1->is_in_cache() )
    return false;

  // criterion 3: give priority to already evaluated points
  // ------------ that are eval_ok:
  if ( x1->is_eval_ok() && !x2->is_eval_ok() )
    return true;
  if ( x2->is_eval_ok() && !x1->is_eval_ok() )
    return false;

  // criterion 4: surrogate f and h values:
  // ------------
  if ( _f_sgte.is_defined() && x_f_sgte.is_defined() ) {

    if ( _h_sgte.is_defined() && x_h_sgte.is_defined() ) {

      // the current point is feasible:
      if ( _h_sgte <= _h_min ) {

	// both points are feasible:
	if ( x_h_sgte <= _h_min  ) {
	  if ( _f_sgte < x_f_sgte )
	    return true;
	  if ( x_f_sgte < _f_sgte )
	    return false;
	}
	
	// current point feasible and the other is infeasible:
	else
	  return true;
      }

      // the current point is infeasible:
      else {
	
	// the other point is feasible:
	if ( x_h_sgte <= _h_min  )
	  return false;

	// both points are infeasible:
	if ( (_h_sgte  < x_h_sgte && _f_sgte  < x_f_sgte) ||
	     (_h_sgte == x_h_sgte && _f_sgte  < x_f_sgte) ||
	     (_h_sgte  < x_h_sgte && _f_sgte == x_f_sgte)    )
	  return true;

	if ( (x_h_sgte  < _h_sgte && x_f_sgte  < _f_sgte) ||
	     (x_h_sgte == _h_sgte && x_f_sgte  < _f_sgte) ||
	     (x_h_sgte  < _h_sgte && x_f_sgte == _f_sgte)    )
	  return false; 
      }
    }

    // we only have f values:
    else {
      if ( _f_sgte < x_f_sgte )
	return true;
      if ( x_f_sgte < _f_sgte )
	return false;
    }
  }

  // criterion 5: check the angle with the last successful direction:
  // ------------
  if ( _angle_success_dir.is_defined() && x_angle_success_dir.is_defined() ) {

    if ( _angle_success_dir < x_angle_success_dir )
      return true;
  
    if ( x_angle_success_dir < _angle_success_dir )
      return false;
  }

  // criterion 6: check the angle with the simplex gradient:
  // ------------
  if ( _angle_simplex_grad.is_defined() && x_angle_simplex_grad.is_defined() ) {
    
    if ( _angle_simplex_grad < x_angle_simplex_grad )
      return true;

    if ( x_angle_simplex_grad < _angle_simplex_grad )
      return false;
  }

  // criterion 7: random criterion for randomly generated directions:
  // ------------
  const NOMAD::Double rep1 = x1->get_rand_eval_priority();
  if ( rep1.is_defined() ) {
    const NOMAD::Double rep2 = x2->get_rand_eval_priority();
    if ( rep2.is_defined() ) {
      if ( rep1 < rep2 )
	return true;
      if ( rep2 < rep1 )
	return false;
    }
  }

  // last criterion (8): compare the coordinates (so that no set of Priority_Eval_Point
  // ------------------- can contain 2 points with the same coordinates):
  
  return x1->Point::operator < (*x2);
}
