/******************************************************************************
* NOMAD -  Nonlinear Optimization for Mixed vAriables and Derivatives.        *
* Copyright (C) 2001-2004  Gilles Couture - Ecole Polytechnique,              *
*                          Charles Audet - Ecole Polytechnique,               *
*		           John Dennis - Rice University.                     *
*                                                                             *
* Author: Gilles Couture                                                      *
* École Polytechnique de Montréal - GERAD                                     *
* C.P. 6079, Succ. Centre-ville, Montréal (Québec) H3C 3A7 Canada             *
* Tel:  (514) 340-6053 x6928                                                  *
* Email: Gilles.Couture@gerad.ca                                              *
* Fax: (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. You can find the              *
* GNU General Public License at:                                              *
*             http://www.gnu.org/copyleft/gpl.html                            *
*                                                                             *
* 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, write to the Free Software                 *
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. *
*                                                                             *
* You can find information on the NOMAD software at:                          *
*             http://www.gerad.ca/NOMAD/                                      *
******************************************************************************/

// searchcontrol.cpp -- SearchControl class methods.

#include <cmath>
#include <fstream>
#include <iostream>
using namespace std;
#include "augmentedpoint.h"
#include "defines.h"
#include "description.h"
#include "evaluator.h"
#include "latinhypercubesearcher.h"
#include "parameters.h"
#include "randomsearcher.h"
#include "searchcontrol.h"
#include "searcher.h"
#include "speculativesearcher.h"
#include "surrogatecontrol.h"

// Private methods.
// ----------------

// This method deletes the list of points from memory.
void SearchControl::cleanList()
{
  if (!trial_points.empty())
    {
			// The AugmentedPoint objects of the last iteration are destroyed.
			for (a_point = trial_points.begin(); a_point != trial_points.end(); a_point++)
				delete *a_point;
			trial_points.clear();
    }
}

// This method deletes the Searcher objects. It's called by the destructor, and
// by the 'continueRun' method.
void SearchControl::deleteSearcherObjects()
{
  // The Searcher objects are deleted.
  delete initial_search;
  delete iterative_search;
  delete speculative_search;
}

// This method initializes the object's attributes.
void SearchControl::initialize()
{
  initial = true;
  a_point = trial_points.begin();
  initial_search = NULL;
  iterative_search = NULL;
  speculative_search = NULL;
	
  // First we build the 'initial_search' object.
  switch(parameters->getInitialSearch())
    {
		case 0  : break;
		case 1  : initial_search = new RandomSearcher(true, description, eval,
																									parameters);
			break;
		case 2  : initial_search = new LatinHypercubeSearcher(true, description,
																													eval, parameters);
			break;
		default : cout << "That's not a search type!\n";
    }
	
  // Then we build the 'iterative_search' object.
  switch(parameters->getIterativeSearch())
    {
		case 0  : break;
		case 1  : iterative_search = new RandomSearcher(false, description, eval,
																										parameters);
			break;
		case 2  : iterative_search = new LatinHypercubeSearcher(false,
																														description,
																														eval,
																														parameters);
			break;
		default : cout << "That's not a search type!\n";
    }
	
  // The SpeculativeSearcher object is created if the user requested it, AND
  // if MADS poll directions are used in the run.
  if ((parameters->getSpeculativeSearch()) && (parameters->getPollDirs() > 2))
    speculative_search = new SpeculativeSearcher(false, description, eval, parameters);
}

// This method puts the points generated by the search on a very fine mesh:
// 1/10000 of the current mesh size.
void SearchControl::mesher(const double * incumbent, const double delta)
{
  // 'pt' is a pointer to the current point.
  double * pt = NULL;
  int i = 0, max_dimension = description->getDimension();
  // The 'temp' array will hold some calculation results.
  double * temp = new double[max_dimension];
	
  for (a_point = trial_points.begin();
       a_point != trial_points.end(); a_point++)
    {
		pt = (*a_point)->getPoint();
		// The point is put on the mesh in this loop, coordinate by coordinate.
		for (i = 0; i < max_dimension; i++)
			{
			// We keep only the first four digits with the 'floor' function.
			temp[i] = floor((10000 * (pt[i] - incumbent[i]) / delta) + 0.5);
			/* The point is put on the finer mesh, and stored in the vector
	     'trial'. */
			pt[i] = incumbent[i] + (delta / 10000) * temp[i];
			}
    }
  if (display_factor >= 10)
    {
		cout << "List of points after 'mesher(...)':\n";
		showList();
    }
  // The memory is freed.
  delete [] temp;
}

// Public methods.
// ---------------

// Default constructor.
SearchControl::SearchControl(const Description * const des, Evaluator & e,
														 const Parameters * const par)
: description(des), eval(& e), parameters(par)
{
  // The object's variables are initialized.
  initialize();
  display_factor = 0;
  surr_control = NULL;
}

// Destructor.
SearchControl::~SearchControl()
{
  // The old list is erased.
  cleanList();
  // This private method will delete the Searcher objects.
  deleteSearcherObjects();
}

// This method controls the entire search routine.
int SearchControl::search(const double delta, const double * poll_center,
													int poll_success, int & term_flag, ofstream & fout,
													ofstream & fgraph, ofstream & history)
{
  // This variable will hold the return value.
  int search_success = 0;
	
  // If the user doesn't want to do a search we do nothing.
  if ((initial_search == NULL) && (iterative_search == NULL) && (speculative_search == NULL))
    return (search_success);
  else
    {
			if (display_factor > 3)
				cout << "    Search points:\n";
			// The old list is erased.
			cleanList();
			// The scaled bounds are retrieved (if bound constraints are used).
			double * lower = NULL, * upper = NULL;
			
			if (description->getBoundsUse() == true)
				{
					lower = eval->getLowerBounds();
					upper = eval->getUpperBounds();
				}
		
			// The 'complete' variable tells us if the search is complete or not.
			bool complete = false;
			
			// If no search has been done (we're doing the initial search).
			if (initial)
				{
					// We have to check if the 'initial_search' object exists, because
					// the user might have chosen not to do an initial search.
					if (initial_search != NULL)
						{
							search_success = initial_search->search(delta, poll_center, lower,
																											upper, INITIAL, trial_points,
																											term_flag, fout, fgraph, history);
							// Is the initial search complete?
							complete = parameters->getInitialComplete();
						}
			
					// The boolean is put to 'false' (there's only one initial search per run!).
					initial = false;
				}
			else   // We're possibly doing an iterative search.
				{
					if (display_factor >= 7)
						cout << "Poll success = " << poll_success << endl;
					// The speculative search is a one point search performed after a poll success.
					if ((speculative_search != NULL) && (poll_success >= 2))
						search_success = speculative_search->search(delta, poll_center, lower, upper,
																												SPECULATIVE, trial_points, term_flag,
																												fout, fgraph, history);
			
					// We have to check if the 'iterative_search' object exists, because
					// the user might have chosen not to do an iterative search. The MADS
					// dynamic search can be combined with a standard iterative search.
					if (iterative_search != NULL)
						{
							search_success = iterative_search->search(delta, poll_center, lower, upper,
																												ITERATIVE, trial_points, term_flag,
																												fout, fgraph, history);
							// Is the iterative search complete?
							complete = parameters->getIterativeComplete();
						}
				}
			
			if (display_factor >= 9)
				showList();
			// The points generated by the search are put on the mesh.
			// mesher(poll_center, delta);
		
			// If we use a surrogate ...
			if ((surr_control != NULL) && (trial_points.size() > 0))
				{
					// First we calculate the surrogate function values.
					eval->evaluateFlow(trial_points);
					// Then the points in the list are ordered by surrogate value.
					surr_control->sortThisList(trial_points);
				}
		
			// The value is returned.
			return search_success;
    }
}

// The user wants to continue the current run.
void SearchControl::continueRun()
{
  // The old list is erased.
  cleanList();
  // We don't know what searches the user wants to do, all search objects are
  // destroyed.
  deleteSearcherObjects();
  // The variables are reinitialized.
  initialize();
}

// This method links with the Surrogate object.
void SearchControl::linkWithSurrogate(SurrogateControl & surr)
{
  // SearchControl is linked with the SurrogateManager object.
  surr_control = & surr;
  // FunctionControl must also be linked to SurrogateManager (Evaluator has a
  // pointer to FunctionControl).
  eval->linkWithSurrogate(surr);
}

// The display factor is reset.
void SearchControl::setDisplay(const int display)
{
  display_factor = display;
  // The display factor must be changed in these objects.
  if (initial_search != NULL)
    initial_search->setDisplay(display_factor);
  if (iterative_search != NULL)
    iterative_search->setDisplay(display_factor);
  if (speculative_search != NULL)
    speculative_search->setDisplay(display_factor);
}

// Outputs the list of points generated by the search.
void SearchControl::showList()
{
	if (!trial_points.empty())
		{
			cout << "\nList of points generated by search:\n";
			// We traverse the list of points in this loop.
			for (a_point = trial_points.begin(); a_point != trial_points.end(); a_point++)
				cout << **a_point;
			cout << endl;
    }
}

// The Speculative search must be updated.
void SearchControl::update(const double delta, std::vector<double> & dir,
													 const AugmentedPoint * new_incumbent)
{
  if (display_factor >= 7)
		{
			cout << "\nIn 'SearchControl::update(...)'\n";
			cout << "*******************************\n";
			int i = 0, max_dimension = description->getDimension();
			double * new_inc = new_incumbent->getPoint();
			cout << "New incumbent = [";
			for (i = 0; i < max_dimension -1; i++)
				cout << new_inc[i] << ", ";
			cout << new_inc[max_dimension -1] << "]\n";
    }
	
  speculative_search->update(delta, dir, new_incumbent);
}
