/*------------------------------------------------------------------------*/
/*       Example #2 of how to use the NOMAD library with a user function  */
/*------------------------------------------------------------------------*/
#include "Mads.hpp"
using namespace std;

/*------------------------------------------------*/
/*               The problem                      */
/*------------------------------------------------*/
/*  example 5.2 from Audet et al.,                */
/*  Multiobjective optimization through a series  */
/*  of single-objective formulations, 2008        */
/*------------------------------------------------*/
class My_Evaluator : public Evaluator {

private:
  NOMAD::Double _w1;  // weights on the two
  NOMAD::Double _w2;  // objectives

public:
  My_Evaluator  ( const Parameters & p ) :
    Evaluator ( p , cout ) , _w1(1.0) , _w2(0.0) {}

  ~My_Evaluator ( void ) {}

  // evaluation of a point:
  bool eval_x ( Eval_Point          & x          ,
		const NOMAD::Double & h_max      ,
		bool                & count_eval   ) const {

    NOMAD::Double f1  = x[0];
    NOMAD::Double g   = 1 + 10.0 * x[1];
    NOMAD::Double f1g = f1 / g;
    NOMAD::Double f2  = g *
      ( 1 - f1g.pow2() - sin ( (25.132741228718 * f1).value() ) * f1g);

    x.set_bb_output  ( 0 , f1 ); // objective #1
    x.set_bb_output  ( 1 , f2 ); // objective #2

    count_eval = true; // count a black-box evaluation

    return true;       // the evaluation succeeded
  }

  // computation of f, taking into account the two objectives,
  // with weights _w1 and _w2:
  void compute_f ( Eval_Point & x ) const {
    const Point & bbo = x.get_bb_outputs();
    x.set_f ( _w1 * bbo[0] + _w2 * bbo[1] );
  }

  // set the weights:
  void set_weights ( const NOMAD::Double & w1 ,
		     const NOMAD::Double & w2   ) {
    _w1 = w1;
    _w2 = w2;
  }

};

/*------------------------------------------*/
/*            NOMAD main function           */
/*------------------------------------------*/
int main ( void ) {

  // display precision:
  cout.precision(DISPLAY_PRECISION_STD);

  try {

    //  parameters creation:
    Parameters p ( cout );

    p.set_DIMENSION (2);             // number of variables

    vector<bb_output_type> bbot (2); // definition of
    bbot[0] = _OBJ_;                 // output types
    bbot[1] = _OBJ_;
    p.set_BB_OUTPUT_TYPE ( bbot );

    p.set_X0 ( Point ( 2 , 1.0 ) );  // starting point

    p.set_LOWER_BOUND ( Point ( 2 , 0.0 ) ); // all var. in
    p.set_UPPER_BOUND ( Point ( 2 , 1.0 ) ); // [0;1]

    p.set_MAX_BB_EVAL (2000);

    p.set_DISPLAY_DEGREE(0);


    p.set_TMP_DIR ("/tmp");      // repertory for
                                 // temporary files

    // parameters validation:
    p.check();

    // custom evaluator creation:
    My_Evaluator ev   ( p );

    // algorithm creation and execution:
    Mads         mads ( p , &ev , cout );

    const Eval_Point * x;
    NOMAD::Double w1 , w2;

    // the loop ends as soon as a false entry
    // is entered:
    while ( true ) {
    
      // set the weights:
      cout << "enter weights: ";
      cin  >> w1 >> w2;

      if ( cin.fail() || !w1.is_defined() || !w2.is_defined() )
	break;

      ev.set_weights ( w1 , w2 );

      // run MADS:
      mads.run();

      // display the solution:
      x = mads.get_best_feasible();
      if ( x )
	cout << "\t" << *x << endl;
      else
	cout << "failure" << endl;

      // reset MADS:
      mads.reset();
    }    
  }
  catch ( exception & e ) {
    cerr << "\nNOMAD has been interrupted (" << e.what() << ")\n\n";
    
    return 1;
  }

  return EXIT_SUCCESS;
}
