/*-------------------------------------------------------------------------------------*/
/*  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 "Cache_File_Point.hpp"
#include "Eval_Point.hpp"
using namespace std;

/*-----------------------------------*/
/*   static members initialization   */
/*-----------------------------------*/
int Eval_Point::_current_tag = 0;

/*---------------------------------------------------------------------*/
/*                            constructor 1                            */
/*---------------------------------------------------------------------*/
Eval_Point::Eval_Point ( void ) :
  _tag              (Eval_Point::_current_tag++  ) ,
  _signature        (NULL                        ) ,
  _in_cache         (false                       ) ,
  _current_run      (false                       ) ,
  _surrogate        (false                       ) ,
  _direction        (NULL                        ) ,
  _mesh_index       (NULL                        ) ,
  _poll_center_type (_UNDEFINED_POLL_CENTER_TYPE_) ,
  _eval_ok          (false                       ) ,
  _EB_ok            (true                        )   {}

/*---------------------------------------------------------------------*/
/*                            constructor 2                            */
/*---------------------------------------------------------------------*/
Eval_Point::Eval_Point ( int n , int m ) :
  Point             (n                           ) ,
  _tag              (Eval_Point::_current_tag++  ) ,
  _signature        (NULL                        ) ,
  _in_cache         (false                       ) ,
  _current_run      (false                       ) ,
  _surrogate        (false                       ) ,
  _direction        (NULL                        ) ,
  _mesh_index       (NULL                        ) ,
  _poll_center_type (_UNDEFINED_POLL_CENTER_TYPE_) ,
  _eval_ok          (false                       ) ,
  _EB_ok            (true                        ) ,
  _bb_outputs       (m                           )   {}

/*---------------------------------------------------------------------*/
/*                  constructor 3 ( used in Cache::load() )            */
/*---------------------------------------------------------------------*/
Eval_Point::Eval_Point ( const Cache_File_Point & x ) :
  Point             (x.get_n()                   ) ,
  _tag              (Eval_Point::_current_tag++  ) ,
  _signature        (NULL                        ) ,
  _in_cache         (false                       ) ,
  _current_run      (false                       ) ,
  _surrogate        (false                       ) ,
  _direction        (NULL                        ) ,
  _mesh_index       (NULL                        ) ,
  _poll_center_type (_UNDEFINED_POLL_CENTER_TYPE_) ,
  _eval_ok          (x.get_eval_ok()==1          ) ,
  _EB_ok            (true                        ) ,
  _bb_outputs       (x.get_bbo()                 )   {
  int n = size();
  for ( int i = 0 ; i < n ; ++i )
    (*this)[i] = x.get_coord(i);
}

/*---------------------------------------------------------------------*/
/*                               destructor                            */
/*---------------------------------------------------------------------*/
Eval_Point::~Eval_Point ( void ) {
  delete _mesh_index;
  delete _direction;
}

/*-------------------------------------------------------*/
/*  SET methods used to complete a default construction  */
/*-------------------------------------------------------*/
void Eval_Point::set ( int n , int m ) {
  reset(n);
  _bb_outputs.reset(m);
}

void Eval_Point::set ( const Point & x , int m ) {
  Point::operator = (x);
  _bb_outputs.reset (m);
}

/*-------------------------------------------------------*/
/*                     set_signature                     */
/*-------------------------------------------------------*/
void Eval_Point::set_signature ( const Signature * s ) {
  if (!s) {
    _signature = NULL;
    return;
  }

  if ( !s->is_compatible(*this) )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
			     "x.Eval_Point::set_signature(s): x and s are incompatible" );

  _signature = s;
}

/*-------------------------------------------------------*/
/*                          sizeof                       */
/*-------------------------------------------------------*/
int Eval_Point::size_of ( void ) const {
  return Point::size_of        () +
    _bb_outputs.size_of        () +
    _f.size_of                 () +
    _h.size_of                 () +
    _user_eval_priority.size_of() +
    _rand_eval_priority.size_of() +
    sizeof (_tag                ) +
    sizeof (Signature*          ) +
    sizeof (_current_run        ) +
    sizeof (_in_cache           ) +
    sizeof (_surrogate          ) +
    sizeof (_eval_ok            ) +
    sizeof (_EB_ok              ) +
    (( _mesh_index ) ? sizeof(*_mesh_index)  : sizeof(_mesh_index)) +
    ((_direction   ) ? _direction->size_of() : sizeof(_direction));
}

/*-------------------------------------------------------*/
/*                     snap to bounds                    */
/*       returns true if the point has been modified     */
/*-------------------------------------------------------*/
bool Eval_Point::snap_to_bounds ( void ) {
  if (!_signature)
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
			     "x.Eval_Point::snap_to_bounds(): x has no signature" );

  return _signature->snap_to_bounds ( *this , _direction );
}

/*--------------------------------------------------*/
/*                 Eval_Point::check                */
/*--------------------------------------------------*/
bool Eval_Point::check ( int m , check_failed_type & cf ) const {

  if ( size() <= 0 || !_signature || m != _bb_outputs.size() ) {
    string err = "Eval_Point::check() could not procede";
    if ( !_signature )
      err += " (no signature)";
    else if ( m != _bb_outputs.size() )
      err += " (wrong number of black-box outputs)";
    else
      err += " (point size <= 0 !)";
    throw NOMAD::Exception ( __FILE__ , __LINE__ , err );
  }

  cf = _OK_;

  const Point                 & lb          = _signature->get_lb();
  const Point                 & ub          = _signature->get_ub();
  const Point                 & fv          = _signature->get_fixed_variables();
  const vector<bb_input_type> & input_types = _signature->get_input_types();
  bb_input_type                 iti;
  int                           n           = size();

  for ( int i = 0 ; i < n ; ++i ) {

    const NOMAD::Double xi = (*this)[i];

    // undefined coordinates ?
    if ( !xi.is_defined() )
      throw NOMAD::Exception ( __FILE__ , __LINE__ ,
	    "Eval_Point::check() could not procede (undefined coordinates)" );

    // check the bounds:
    const NOMAD::Double & lbi = lb[i];
    if ( lbi.is_defined() && xi < lbi ) {
      cf = _LB_FAIL_;
      return false;
    }
    const NOMAD::Double & ubi = ub[i];
    if ( ubi.is_defined() && xi > ubi ) {
      cf = _UB_FAIL_;
      return false;
    }

    // check the integer/caterogical/binary variables:
    iti = input_types[i];
    if ( iti==_BINARY_ && !xi.is_binary() ) {
      cf = _BIN_FAIL_;
      return false;
    }
    if ( ( iti == _INTEGER_ || iti == _CATEGORICAL_ )
	 && !xi.is_integer() ) {
      cf = (iti==_INTEGER_) ? _INT_FAIL_ : _CAT_FAIL_;
      return false;
    }

    // check the fixed-variables:
    const NOMAD::Double & fvi = fv[i];
    if ( fvi.is_defined() && fvi != xi ) {
      cf = _FIX_VAR_FAIL_;
      return false;
    }
  }

  return true;
}

/*--------------------------------------------------*/
/*                      display                     */
/*--------------------------------------------------*/
void Eval_Point::display ( ostream & out ) const {
  out << "#"
      << setw ( (Eval_Point::_current_tag > 0) ?
		(1+int(log(static_cast<double>(Eval_Point::_current_tag))/LOG10)) : 1 )
      << _tag << " X=( ";
  Point::display ( out , " " , 2  , Point::get_display_limit() );
  out << " ) F(X)=[ ";
  _bb_outputs.display ( out , " " , 2 , Point::get_display_limit() );
  out << " ]";
  if ( _h.is_defined() )
    out << " h=" << _h;
  if ( _f.is_defined() )
    out << " f=" << _f;
}

/*---------------------------------------------------------------------*/
/*  Comparison operator '<': it is used to find and store the points   */
/*                           in the filter                             */
/*---------------------------------------------------------------------*/
bool Eval_Point::operator < ( const Eval_Point & x ) const {

  if ( this == &x || !_eval_ok || !_EB_ok )
    return false;

  double h  = _h.value();
  double f  = _f.value();
  double hx = x._h.value();
  double fx = x._f.value();

  if ( h < hx )
    return ( f <= fx );

  if ( h == hx )
    return ( f < fx );
  
  return false;
}

/*---------------------------------------------------------------------*/
/*              SET methods for _direction and _mesh_index             */
/*---------------------------------------------------------------------*/
void Eval_Point::set_mesh_index ( const int * l ) {
  delete _mesh_index;
  _mesh_index = NULL;
  if (l) {
    _mesh_index  = new int;
    *_mesh_index = *l;
  }
}

void Eval_Point::set_direction ( const Direction * dir ) {
  delete _direction;
  _direction = (dir) ? new Direction(*dir) : NULL;
}
