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

/*--------------------------------------------------*/
/*                  constructor 1                   */
/*--------------------------------------------------*/
Signature::Signature ( const Parameters & p , ostream & out ) :
  _p          (p   ) ,
  _mesh       (NULL) ,
  _out        (out )   {
  init ( p.get_lb               () ,
	 p.get_ub               () ,
	 p.get_fixed_variables  () ,
	 p.get_bb_input_types   () ,
	 p.get_variable_groups  () ,
	 p.get_initial_mesh_size() ,
	 p.get_min_mesh_size    () ,
	 p.get_min_poll_size    ()   );
}

/*--------------------------------------------------*/
/*                  constructor 2                   */
/*--------------------------------------------------*/
Signature::Signature ( const Parameters                   & p                 ,
		       const Point                        & lb                ,
		       const Point                        & ub                ,
		       const Point                        & fixed_variables   ,
		       const vector<bb_input_type>        & input_types       ,
		       const set<Variable_Group*,VG_Comp> & var_groups        ,
		       const Point                        & initial_mesh_size ,
		       const Point                        & min_mesh_size     ,
		       const Point                        & min_poll_size     ,
		       ostream                            & out                 ) :
  _p          (p   ) ,
  _mesh       (NULL) ,
  _out        (out )   {
  init ( lb                ,
	 ub                ,
	 fixed_variables   ,
	 input_types       ,
	 var_groups        ,
	 initial_mesh_size ,
	 min_mesh_size     ,
	 min_poll_size       );
}

/*--------------------------------------------------*/
/*                     destructor                   */
/*--------------------------------------------------*/
Signature::~Signature ( void ) {
  delete _mesh;
  reset_var_groups();
}

/*--------------------------------------------------*/
/*                     initializations              */
/*--------------------------------------------------*/
void Signature::init ( const Point                        & lb                ,
		       const Point                        & ub                ,
		       const Point                        & fixed_variables   ,
		       const vector<bb_input_type>        & input_types       ,
		       const set<Variable_Group*,VG_Comp> & var_groups        ,
		       const Point                        & initial_mesh_size ,
		       const Point                        & min_mesh_size     ,
		       const Point                        & min_poll_size       ) {

  int n = lb.size();
  
  if ( ub.size()                            != n                 ||
       fixed_variables.size()               != n                 ||
       static_cast<int>(input_types.size()) != n                 ||
       initial_mesh_size.size()             != n                 ||
       (min_mesh_size.is_defined() && min_mesh_size.size() != n) ||
       (min_poll_size.is_defined() && min_poll_size.size() != n)    )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
		 "Signature::init() error: arguments with different sizes" );

  _lb              = lb;
  _ub              = ub;
  _fixed_variables = fixed_variables;
  _input_types     = input_types;

  // variable groups:
  set<Variable_Group *,VG_Comp>::const_iterator end = var_groups.end();
  for ( set<Variable_Group *,VG_Comp>::const_iterator it = var_groups.begin() ;
	it != end ; ++it ) {
    if ( !(*it)->check ( fixed_variables , input_types , NULL , NULL , NULL , NULL ) ) {
      reset_var_groups();
      throw NOMAD::Exception ( __FILE__ , __LINE__ ,
	    "Signature::init() error: incompatible variable group" );
    }
    _var_groups.push_back ( new Variable_Group (**it) );
  }

  // mesh:
  delete _mesh;
  _mesh = new Mesh ( initial_mesh_size , min_mesh_size , min_poll_size );
}

/*--------------------------------------------------*/
/*                       reset                      */
/*--------------------------------------------------*/
void Signature::reset ( const Parameters & p ) {

  reset_var_groups();

  init ( p.get_lb               () ,
	 p.get_ub               () ,
	 p.get_fixed_variables  () ,
	 p.get_bb_input_types   () ,
	 p.get_variable_groups  () ,
	 p.get_initial_mesh_size() ,
	 p.get_min_mesh_size    () ,
	 p.get_min_poll_size    ()   );
}

/*--------------------------------------------------*/
/*                  reset_var_groups                */
/*--------------------------------------------------*/
void Signature::reset_var_groups ( void ) {
  list<Variable_Group *>::const_iterator end = _var_groups.end();
  for ( list<Variable_Group *>::const_iterator it = _var_groups.begin() ;
	it != end ; ++it )
    delete *it;
  _var_groups.clear();
}

/*----------------------------------------------------------*/
/*  check the stopping conditions on the minimal poll size  */
/*  and on the minimal mesh size                            */
/*----------------------------------------------------------*/
void Signature::check_min_mesh_sizes ( const Parameters & p           ,
				       int                mesh_index  ,
				       bool             & stop        ,
				       stop_type        & stop_reason   ) {
  if (stop)
    return;

  // 1. mesh index tests:
  if ( abs(mesh_index) > L_LIMITS ) {
    stop        = true;
    stop_reason = _L_LIMITS_REACHED_;
  }
  if ( p.get_max_mesh_index() != UNDEFINED_L &&
       mesh_index > p.get_max_mesh_index() ) {
    stop        = true;
    stop_reason = _L_MAX_REACHED_;
  }

  // 2. delta_k^p (poll size) tests:
  if ( _mesh->check_min_poll_size_criterion ( mesh_index ) ) {
    stop        = true;
    stop_reason = _DELTA_P_MIN_REACHED_;
  }

  // 3. delta_k_m (mesh size) tests:
  if ( _mesh->check_min_mesh_size_criterion ( mesh_index ) ) {
    stop        = true;
    stop_reason = _DELTA_M_MIN_REACHED_;
  }
}

/*----------------------------------------------------------*/
/*           check the compatibility of a point             */
/*   . we only check the number of variables                */
/*   . other stuff (fixed variables, binary variables,...)  */
/*     will be checked by Eval_Point::check()               */
/*----------------------------------------------------------*/
bool Signature::is_compatible ( const Point & x ) const {
  if ( get_n() != x.size() || !_mesh || _var_groups.empty() )
    return false;
  return true;
}

/*-----------------------------------------------------*/
/*     compute the directions (they include delta_m)   */
/*-----------------------------------------------------*/
void Signature::get_directions ( list<Direction> & dirs       ,
				 poll_type         poll       ,
				 int               mesh_index   ) const {

  Direction                     * pd;
  int                             i;
  list<Direction>::const_iterator it_dir , end_dir;
  set<int>::const_iterator        it_vi  , end_vi;

  // get delta_m (mesh size parameter):
  int   n = get_n();
  Point delta_m (n);
  _mesh->get_delta_m ( delta_m , mesh_index );

  // loop on variable groups:
  list<Variable_Group*>::const_iterator end_vg = _var_groups.end();
  for ( list<Variable_Group*>::const_iterator it_vg = _var_groups.begin() ;
	it_vg != end_vg ; ++it_vg ) {

    const set<int> & var_indexes = (*it_vg)->get_var_indexes();

    // get the directions for the current group of variables:
    list<Direction> dirs_nc;
    (*it_vg)->get_directions ( dirs_nc , poll , mesh_index );

    // scale with delta_m and resize the directions to size n;
    // also round integer and binary variables:
    end_dir = dirs_nc.end();
    for ( it_dir = dirs_nc.begin() ; it_dir != end_dir ; ++it_dir ) {
      dirs.push_back ( Direction ( n , 0.0 , it_dir->get_type() ) );
      pd = &(*(--dirs.end()));

      end_vi = var_indexes.end();
      i      = 0;
      for ( it_vi = var_indexes.begin() ; it_vi != end_vi ; ++it_vi ) {

	(*pd) [ *it_vi ] = delta_m[ *it_vi ] * (*it_dir)[i++];

	// integer variables:
	if ( _input_types[*it_vi] == _INTEGER_ ) {
	  if ( (*pd) [ *it_vi ] >= 0.0 )
	    (*pd) [ *it_vi ] = ceil  ( (*pd) [ *it_vi ].value() );
	  else
	    (*pd) [ *it_vi ] = floor ( (*pd) [ *it_vi ].value() );
	}

	// binary variables:
	else if ( _input_types[*it_vi] == _BINARY_ ) {
	  if ( (*pd) [ *it_vi ] != 0.0 )
	    (*pd) [ *it_vi ] = 1.0;
	}

	// categorical variables: not yet coded: set direction=0:
	else if ( _input_types[*it_vi] == _CATEGORICAL_ )
	  (*pd) [ *it_vi ] = 0.0;
      }
    }
  }
}

/*--------------------------------------------------*/
/*                   snap to bounds                 */
/*         returns true if x has been modified      */
/*--------------------------------------------------*/
bool Signature::snap_to_bounds ( Point & x , Direction * direction ) const {
  int n = get_n();
  if ( n != x.size() )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
	  "Signature::snap_to_bounds(x) error: x.size() != signature.size()" );
  bool modified = false;

  for ( int i = 0 ; i < n ; ++i ) {
    const NOMAD::Double & ubi = _ub[i];
    NOMAD::Double       & xi  = x[i];
    if ( ubi.is_defined() && xi > ubi ) {
      if (direction)
	(*direction)[i] += ubi - xi;
      xi       = ubi;
      modified = true;
    }
    const NOMAD::Double & lbi = _lb[i];
    if ( lbi.is_defined() && xi < lbi ) {
      if (direction)
	(*direction)[i] += (lbi - xi);
      xi       = lbi;
      modified = true;
    }
  }
  return modified;
}

/*--------------------------------------------------*/
/*                      display                     */
/*--------------------------------------------------*/
void Signature::display ( ostream & out ) const {

  out << "\tn               : "   << get_n()                 << endl
      << "\tlb              : ( " << _lb              << ")" << endl
      << "\tub              : ( " << _ub              << ")" << endl
      << "\tfixed vars      : ( " << _fixed_variables << ")" << endl
      << "\tinput types     : ( " << _input_types     << ")" << endl

    // mesh:
      << "\tmesh:\n" << *_mesh << endl

    // variable groups:
      << "\tvariable groups :" << endl;
  int i = 0;
  list<Variable_Group *>::const_iterator end = _var_groups.end();
  for ( list<Variable_Group *>::const_iterator it = _var_groups.begin() ;
	it != end ; ++it )
    out << "\t\tgroup #" << i++ << ": " << **it << endl;
}
