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

/*-----------------------------------*/
/*   static members initialization   */
/*-----------------------------------*/
double Mesh::_mesh_update_basis        = 4.0;
int    Mesh::_mesh_coarsening_exponent =  1;
int    Mesh::_mesh_refining_exponent   = -1;
int    Mesh::_initial_mesh_index       =  0;
int    Mesh::_mesh_index               =  0;
int    Mesh::_min_mesh_index           =  0;
int    Mesh::_max_mesh_index           =  0;

/*-----------------------------------------------------------*/
/*                          constructor                      */
/*-----------------------------------------------------------*/
Mesh::Mesh ( const Point            & delta_m_0   ,
	     const Point            & delta_m_min ,
	     const Point            & delta_p_min   ) :
  _initial_mesh_size ( delta_m_0   ) ,
  _min_mesh_size     ( delta_m_min ) ,
  _min_poll_size     ( delta_p_min )   {
 
  bool chkp  = delta_p_min.is_defined();
  bool chkm  = delta_m_min.is_defined();

  int  k , n = delta_m_0.size();

  if ( chkp && delta_p_min.size() != n )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
                 "Mesh::Mesh() error: delta_m_0 and delta_p_min have different sizes" );

  if ( chkm && delta_m_min.size() != n )
    throw NOMAD::Exception ( __FILE__ , __LINE__ ,
                 "Mesh::Mesh() error: delta_m_0 and delta_m_min have different sizes" );

  // we check that delta_p_min <= delta_p_0 and that delta_m_min <= delta_m_0:
  if ( chkp || chkm ) {

    Point * delta_p_0 = NULL , * delta_m_0 = NULL;

    if ( chkp ) {
      delta_p_0 = new Point(n);
      get_delta_p ( *delta_p_0 , Mesh::_initial_mesh_index );
    }

    if ( chkm ) {
      delta_m_0 = new Point(n);
      get_delta_m ( *delta_m_0 , Mesh::_initial_mesh_index );
    }
    
    for ( k = 0 ; k < n ; ++k ) {

      if ( delta_p_0 && (*delta_p_0)[k] < _min_poll_size[k] )
	throw NOMAD::Exception ( __FILE__ , __LINE__ ,
				 "Mesh::Mesh() error: delta_p_0 < delta_p_min" );

      if ( delta_m_0 && (*delta_m_0)[k] < _min_mesh_size[k] )
	throw NOMAD::Exception ( __FILE__ , __LINE__ ,
				 "Mesh::Mesh() error: delta_m_0 < delta_m_min" );
    }
    delete delta_p_0;
    delete delta_m_0;
  }
}

/*-----------------------------------------------------------*/
/*               Mesh static members initialization          */
/*-----------------------------------------------------------*/
void Mesh::init ( double mesh_update_basis        ,
		  int    mesh_coarsening_exponent ,
		  int    mesh_refining_exponent   ,
		  int    initial_mesh_index         ) {
  Mesh::_mesh_update_basis        = mesh_update_basis;
  Mesh::_mesh_coarsening_exponent = mesh_coarsening_exponent;
  Mesh::_mesh_refining_exponent   = mesh_refining_exponent;
  Mesh::_mesh_index           =
    Mesh::_initial_mesh_index =
    Mesh::_min_mesh_index     =
    Mesh::_max_mesh_index     = initial_mesh_index;
}

/*-----------------------------------------------------------*/
/*                        update the mesh                    */
/*-----------------------------------------------------------*/
void Mesh::update ( success_type success , int & mesh_index ) {

  // defaults:
  //  full success: lk = lk - 1
  //  failure     : lk = lk + 1

  if ( success == _FULL_SUCCESS_ )
    mesh_index -= Mesh::_mesh_coarsening_exponent;

  else if ( success == _UNSUCCESSFUL_ )
    mesh_index -= Mesh::_mesh_refining_exponent;

  if ( mesh_index > _max_mesh_index )
    _max_mesh_index = mesh_index;

  if ( mesh_index < _min_mesh_index )
    _min_mesh_index = mesh_index;
}

/*-----------------------------------------------------------*/
/*                           display                         */
/*-----------------------------------------------------------*/
void Mesh::display( ostream & out ) const {
  out << "\t\tn                        : " << get_n()                    << endl
      << "\t\tmesh update basis        : " << _mesh_update_basis         << endl
      << "\t\tmesh coarsening exponent : " << _mesh_coarsening_exponent  << endl
      << "\t\tmesh refining exponent   : " << _mesh_refining_exponent    << endl
      << "\t\tinitial mesh index       : " << _initial_mesh_index        << endl
      << "\t\tinitial mesh size        : " << "( " << _initial_mesh_size << ")\n"
      << "\t\tminimal mesh size        : " << "( " << _min_mesh_size     << ")\n"
      << "\t\tminimal poll size        : " << "( " << _min_poll_size     << ")\n";
}

/*-----------------------------------------------------------*/
/*                 check the minimal poll size               */
/*-----------------------------------------------------------*/
bool Mesh::check_min_poll_size_criterion ( int mesh_index ) const {

  if ( !_min_poll_size.is_defined() )
    return false;

  int   n = get_n();
  Point delta_p(n);
  get_delta_p ( delta_p , mesh_index );

  for ( int i = 0 ; i < n ; ++i )
    if ( _min_poll_size[i].is_defined() && delta_p[i] < _min_poll_size[i] )
      return true;

  return false;
}

/*-----------------------------------------------------------*/
/*                 check the minimal mesh size               */
/*-----------------------------------------------------------*/
bool Mesh::check_min_mesh_size_criterion ( int mesh_index ) const {

  if ( !_min_mesh_size.is_defined() )
    return false;

  int   n = get_n();
  Point delta_m(n);
  get_delta_m ( delta_m , mesh_index );

  for ( int i = 0 ; i < n ; ++i )
    if ( _min_mesh_size[i].is_defined() && delta_m[i] < _min_mesh_size[i] )
      return true;

  return false;
}

/*-----------------------------------------------------------*/
/*  get delta_m (mesh size parameter)                        */
/*       \Delta_k^m = \Delta_0^m \tau^{\ell_0^+ - \ell_k^+}  */
/*-----------------------------------------------------------*/
void Mesh::get_delta_m ( Point & delta_m , int mesh_index ) const {

  int n = get_n();

  delta_m.reset(n);

  // power_of_tau = tau^{ max{0,l0} - max{0,lk} }:
  NOMAD::Double power_of_tau
    = pow ( _mesh_update_basis ,
	    ( (_initial_mesh_index > 0) ? _initial_mesh_index : 0) -
	    ( (mesh_index          > 0) ? mesh_index          : 0)   );

  // Delta_k^m = power_of_tau * Delta_0^m:
  for ( int i = 0 ; i < n ; ++i )
    delta_m[i] = _initial_mesh_size[i] * power_of_tau;
}

/*--------------------------------------------------------------------*/
/*  get delta_p (poll size parameter)                                 */
/*       \Delta_k^p = \Delta_k^m \tau^{ |lk|/2 }                      */
/*                  = \Delta_0^m \tau^{\ell_0^+ - \ell_k^+ + |lk|/2}  */
/*--------------------------------------------------------------------*/
void Mesh::get_delta_p ( Point & delta_p , int mesh_index ) const {

  int n = get_n();

  delta_p.reset(n);

  // power_of_tau = tau^{ max{0,l0} - max{0,lk} + |lk|/2}:
  NOMAD::Double power_of_tau
    = pow ( _mesh_update_basis , abs(mesh_index) / 2.0             +
	    ( (_initial_mesh_index > 0) ? _initial_mesh_index : 0) -
	    ( (mesh_index          > 0) ? mesh_index          : 0)   );

  // Delta_k^p = power_of_tau * Delta_0^m:
  for ( int i = 0 ; i < n ; ++i )
    delta_p[i] = _initial_mesh_size[i] * power_of_tau;
}
