/*-------------------------------------------------------------------------------------*/
/*  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               */
/*-------------------------------------------------------------------------------------*/
#ifndef __DIRECTIONS__
#define __DIRECTIONS__

#include "Random_Pickup.hpp"
#include "Direction.hpp"
#include "Mesh.hpp"
#include <set>

class Directions {

  /*--------------------------------------------------------------*/
private:

  int                              _nc;                   // number of continuous variables

  std::set<direction_type>         _dir_types;            // types of directions for poll
  std::set<direction_type>         _sec_poll_dir_types;   // types of directions for sec. poll

  bool                             _binary;               // if all variables are binary
  bool                             _categorical;          // if all variables are categorical

  bool                             _is_orthomads;         // if Ortho-MADS directions are used

  // LT-MADS attributes:
  Direction                      * _bl   [1+2*L_LIMITS];  // b(l) directions
  int                              _hat_i[1+2*L_LIMITS];  // b(l) indexes
  bool                             _lt_initialized;       // if LT-MADS has been initialized

  // Ortho-MADS attributes:
  int                            * _primes;               // nc first prime numbers
  int                              _halton_seed;          // Halton seed t_0
  int                              _max_halton_index;     // max. Halton index t_i
  int                              _max_mesh_index;       // max. mesh index
                                                          // (not necessarily the same
                                                          //  as Mesh::_max_mesh_index)

  std::ostream                   & _out;                  // display

  /*--------------------------------------------------------------*/

  // private affectation operator:
  Directions & operator = ( const Directions & );

  // LT-MADS initializations:
  void lt_mads_init ( void );

  // compute the squared norm of normalized(2u_t-e) (Ortho-MADS):
  NOMAD::Double eval_ortho_norm ( const NOMAD::Double & x      ,
				  const NOMAD::Double & norm   ,
				  const NOMAD::Double & target ,
				  const Point         & b      ,
				  Point               & new_b    ) const;

  // Householder transformation to:
  void householder ( const Direction & halton_dir , Direction ** H ) const;

  // get the expression of an integer t in inverse base p:
  NOMAD::Double get_phi ( int t , int p ) const;

  // compute the halton direction q(t,l):
  bool compute_halton_dir ( int             halton_index ,
			    int             mesh_index   ,
			    NOMAD::Double & alpha_t_l    ,
			    Direction     & halton_dir     ) const;

  // get the LT-MADS b(l) direction:
  const Direction * get_bl ( int mesh_index , direction_type dtype , int & hat_i );
 
  // create a new LT-MADS direction:
  void create_lt_direction ( int                    mesh_index ,
			     direction_type         dtype      ,
			     int                    diag_i     ,
			     int                  & hat_i      ,
			     Direction           *& dir          );

  // permute the coordinates of a direction:
  void permute_coords ( Direction & dir , int * permutation_vector ) const;

  // compute binary directions (only if all group-variables are binary):
  void compute_binary_directions ( list<Direction> & dirs ,
				   poll_type         poll   ) const;

  /*--------------------------------------------------------------*/

public:

  // constructor (the Halton seed will be automatically computed if halton_seed==-1):
  Directions ( int                              nc                 ,
	       const std::set<direction_type> & dir_types          ,
	       const std::set<direction_type> & sec_poll_dir_types ,
	       int                              halton_seed        ,
	       std::ostream                   & out                  ) :
    _nc                 (nc                ) ,
    _dir_types          (dir_types         ) ,
    _sec_poll_dir_types (sec_poll_dir_types) ,
    _binary             (false             ) ,
    _categorical        (false             ) ,
    _is_orthomads       (false             ) ,
    _lt_initialized     (false             ) ,
    _primes             (NULL              ) ,
    _halton_seed        (halton_seed       ) ,
    _max_mesh_index     (-L_LIMITS-1       ) ,
    _out                (out               )   {}

  // copy constructor:
  Directions ( const Directions & d ) :
    _nc                 (d._nc                ) ,
    _dir_types          (d._dir_types         ) ,
    _sec_poll_dir_types (d._sec_poll_dir_types) ,
    _binary             (d._binary            ) ,
    _categorical        (d._categorical       ) ,
    _is_orthomads       (d._is_orthomads      ) ,
    _lt_initialized     (false                ) ,
    _primes             (NULL                 ) ,
    _halton_seed        (d._halton_seed       ) ,
    _max_mesh_index     (-L_LIMITS-1          ) ,
    _out                (d._out               )   {}

  // destructor:
  ~Directions ( void );

  // other initializations:
  void init ( const std::set<direction_type> & dir_types          ,
	      const std::set<direction_type> & sec_poll_dir_types ,
	      bb_output_type                   barrier_type         );

  // compute the directions for a given mesh:
  void compute ( std::list<Direction> & dirs         ,
		 poll_type              poll         ,
		 int                    mesh_index   ,
		 int                    halton_index   ); // -1 for auto. Halton index

  // GET methods:
  int           get_halton_seed ( void ) const { return _halton_seed;  }
  bool          is_orthomads    ( void ) const { return _is_orthomads; }

  // SET methods:
  void set_binary      ( void );
  void set_categorical ( void );

  // display:
  void display( std::ostream & out ) const;

  // Ortho-MADS initializations
  // (invoked by Directions::compute() and Variable_Group::set_halton_seed()):
  void ortho_mads_init ( int halton_seed );

};

// display:
inline std::ostream & operator << ( std::ostream     & out ,
				    const Directions & d     ) {
  d.display(out);
  return out;
}

#endif
