/******************************************************************************
* 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/                                      *
******************************************************************************/

// pollcontrol.cpp -- PollControl class methods. All methods of the class are
// called from the Solver object, which has direct access to PollControl.

#include <cmath>
#include <fstream>
#include <iostream>
using namespace std;
#include "augmentedpoint.h"
#include "continuouspollster.h"
#include "defines.h"
#include "description.h"
#include "directionset.h"
#include "evaluator.h"
#include "parameters.h"
#include "pollcontrol.h"
#include "pollster.h"
#include "surrogatecontrol.h"

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

// Are these two integer arrays the same? (all their elements are equal).
bool PollControl::equal(int * old, int * _new) const
{
  int i, max_dimension = description->getDimension();
  if (display_factor >= 5)
    {
      cout << "Old proximity array: ";
      for (i = 0; i < max_dimension; i++)
	cout << old[i] << " ";
      cout << "\n";
      cout << "New proximity array: ";
      for (i = 0; i < max_dimension; i++)
	cout << _new[i] << " ";
      cout << "\n";
    }
  for (i = 0; i < max_dimension; i++)
    if (old[i] != _new[i])
      return false;
  return true;
}

// This method deletes the list of points from memory.
void PollControl::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();
    }
}

// Copy of an integer array into another.
void PollControl::copy(int * a, int * b) const
{
  int max_dimension = description->getDimension();
  for (int i = 0; i < max_dimension; i++)
    a[i] = b[i];
}

// The object's attributes are initialized.
void PollControl::initialize()
{
  // We need the user's poll directions choice and the problem dimension.
  int choice = parameters->getPollDirs();
  int max_dimension = description->getDimension();

  // A proximity array is built only when there are GPS n+1 ('choice' equals 1)
  // or GPS uniform directions ('choice' equals 2).
  if ((choice == 1) || (choice == 2))
    {
      // If the array is null it's created. If it already exists it means that
      // a previous run is continued, so nothing is done.
      if (prox_array == NULL)
				{
					prox_array = new int[max_dimension];
					// 'prox_array' is initialized.
					for (int i = 0; i < max_dimension; i++)
						prox_array[i] = 0;
				}
    }
  else   // The poll directions don't need a proximity array.
    {
      // The old proximity array is destroyed.
      if (prox_array != NULL)
				{
					delete [] prox_array;
				prox_array = NULL;
				}
    }

  a_point = trial_points.begin();

  // The old DirectionSet object is destroyed.
  if (direction_set != NULL)
    delete direction_set;
  // The new DirectionSet is built.
  direction_set = new DirectionSet(description, parameters);
}

// Outputs the list of points generated by the poll.
void PollControl::showList()
{
  cout << "List of points generated by poll:\n";
  if (trial_points.empty())
    cout << "List of points empty!\n";
  else
    {
      // 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;
}

// This method is called when there is a success in the poll , i.e. when the
// point returned by the poll has a better truth function value than the 
// incumbent.
void PollControl::success(const int index)
{
  if (display_factor >= 5)
    cout << "\nChecking to reorder poll directions...\n";
  // We check if the direction set has to be updated (the new incumbent is
  // close to a bound).
  bool direction_set_updated = updateDirectionSet();
  // The poll directions are not reordered in these four cases:
  // 1- The successful poll direction was the first one.
  // 2- The user chose a fixed order of poll directions.
  // 3- The user chose a complete poll (all directions will be evaluated
  //    anyway, why bother reordering them?).
  // 4- The direction set was updated (the incumbent is within epsilon of a
  //    constraint).
  if ((index != 0) && (!parameters->getPollOrder()) &&
      (!parameters->getPollType()) && (direction_set_updated == false))
    // We tell 'direction_set' that there's a success.
    direction_set->success(index);
  else
    if (display_factor >= 5)
      cout << "No reordering.\n";
}

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

// Default constructor. PollControl is created at Start-up by the Solver
// object.
PollControl::PollControl(const Description * const des, Evaluator & e,
			 const Parameters * const par)
  : description(des), eval(&e), parameters(par)
{
  display_factor = 0;
  // These pointers start out null.
  prox_array = NULL;
  direction_set = NULL;
  surr_control = NULL;

  // The object's attributes are initialized.
  initialize();

  // The Pollster object is built, with default parameters.
  pollster = new ContinuousPollster(description);
}

// Destructor.
PollControl::~PollControl()
{
  if (prox_array != NULL)
    delete [] prox_array;
  // The old list is erased.
  cleanList();
  // The DirectionSet object is destroyed.
  delete direction_set;
  // The Pollster object is destroyed.
  delete pollster;
}

// This function is called when a new incumbent is found. It modifies the poll
// directions set when the incumbent is within epsilon of a boundary, if
// certain conditions are met (see below).
bool PollControl::updateDirectionSet()
{
  bool value, use_constraints = description->getBoundsUse();
  int choice = parameters->getPollDirs();

  // Nothing is done in these three cases:
  // 1- The user chose 2n poll directions.
  // 2- The user chose MADS poll directions.
  // 3- There are no bound constraints.
  if ((choice == 0) || (choice > 2) || (use_constraints == false))
    {
      if (display_factor >= 7)
				{
					if (choice == 0)
						cout << "GPS 2*n poll directions! No need to rebuild.\n";
					if (choice > 2)
						cout << "MADS poll directions! No need to rebuild.\n";
					if (use_constraints == false)
						cout << "No constraints in the problem.\n";
				}
      value = false;
    }
  else   // We possibly need to update the set.
    {
      int max_dimension = description->getDimension();
      // "temp" will contain the last proximity array from the bounds object.
      // It's initialized with '0' values.
      int * temp = new int[max_dimension];
      for (int i = 0; i < max_dimension; i++)
				temp[i] = 0;
      // ... we get the new proximity array (because the poll center has
      // changed, maybe the proximity array has too).
      copy(temp, eval->getProximityArray());
      // If the arrays are different, we need to change the polldirs set.
      if (!equal(prox_array, temp))
				{
					// The proximity array has changed.
					copy(prox_array, temp);
					// The poll directions set is rebuilt.
					direction_set->rebuildPollDirSet(prox_array);
					value = true;
				}
      else   // The proximity array is the same.
				value = false;
      // The "temp" array is destroyed.
      delete [] temp;
    }
  return value;
}

//
std::vector<double> & PollControl::getSuccessfulDirection(
						       const int polldir_index)
{
  return direction_set->getSuccessfulDirection(polldir_index);
}

// This method does the poll phase (called every time the search fails).
int PollControl::poll(const double delta, const double * primary_poll_center,
		      const double * secondary_poll_center, int & term_flag,
		      ofstream & fout, ofstream & fgraph, ofstream & history)
{
  int z = 0;
  // The old list is erased.
  cleanList();
  int max_dimension = description->getDimension();
  if (display_factor >= 5)
    {
      cout << "\nStart of local poll, delta = " << delta << endl;
      cout << "Primary poll center: ";
      for (z = 0; z < max_dimension - 1; z++)
				cout << primary_poll_center[z] << "  ";
      cout << primary_poll_center[max_dimension - 1] << "\n";
      cout << "Secondary poll center: ";
      if (secondary_poll_center == NULL)
				cout << "NA\n\n";
      else
				{
					for (z = 0; z < max_dimension - 1; z++)
						cout << secondary_poll_center[z] << "  ";
					cout << secondary_poll_center[max_dimension - 1] << "\n\n";
				}
    }

  // If the user chose a MADS set of directions, we have to build them now.
  int poll_directions = parameters->getPollDirs();
  if ((poll_directions == 3) || (poll_directions == 4))
    direction_set->buildMADSPollSet(delta);
  // We call the poll method of Pollster.
  pollster->poll(delta, primary_poll_center, direction_set->getPrimaryDirs(),
								 secondary_poll_center, direction_set->getSecondaryDirs(),
								 trial_points);
  // If we use a surrogate, we have to do these things:
  if (surr_control != NULL)
    {
      // First we calculate the surrogate function values.
      eval->evaluateFlow(trial_points);
      // Then the points are sorted in increasing order of surrogate values.
      surr_control->sortThisList(trial_points);
    }
  if (display_factor >= 9)
    showList();
  // The list of points is sent to Evaluator, to be evaluated by the objective
  // function. The 'processThisList(...)' method returns an integer that
  // indicates the degree of success of the poll.
  int poll_success = eval->processThisList
   (trial_points, parameters->getPollType(), term_flag, fout, fgraph, history);
  // There are 3 degrees of success:
  // - poll_success = 0: Failure! The poll center stays the same, no points 
  //                     were added to the filter.
  // - poll_success = 1: Partial success! The poll center stays the same, at
  //                     least one point was added to the filter.
  // - poll_success = 2: Success! A new least infeasible incumbent was found.
  // - poll_success = 3: Success! A new best feasible incumbent was found.
  if (poll_success >= 2)
    success(eval->getPollDirIndex());
  return poll_success;
}

// The user wants to continue the current run.
void PollControl::continueRun()
{
  // The old list is erased.
  cleanList();
  // The variables are reinitialized.
  initialize();
}

// This method links this object to SurrogateControl.
void PollControl::linkWithSurrogate(SurrogateControl & surr)
{
  surr_control = & surr;
}

// The display factor is reset.
void PollControl::setDisplay(const int display)
{
  display_factor = display;
  // The display factor must be changed in these objects.
  direction_set->setDisplay(display_factor);
  pollster->setDisplay(display_factor);
}
