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

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

#include <cmath>
#include <fstream>
#include <iostream>
using namespace std;
#include "description.h"
#include "mesh.h"
#include "parameters.h"

// Private methods.
// ****************************************************************************

// This method calculates the initial mesh size, maximum mesh size and mesh
// size termination criterion. These variables aren't equal to their poll size
// counterparts when the user chooses MADS poll directions.
void Mesh::calculateMeshSizes()
{
  int choice = parameters->getPollDirs();

  // In this case the user chose MADS 2*n directions.
  if (choice == 3)
    {
      // In the case where 'max_mesh_size' is smaller than one, then the
      // maximum mesh size will be even smaller.
      if (max_mesh_size < 1.0)
				max_mesh_size = pow(max_mesh_size, 2);

      // If the initial poll size is smaller than one, then the initial mesh
      // size will be even smaller.
      if (mesh_size < 1.0)
				mesh_size = pow(mesh_size, 2);

      // If 'mesh_size_term' is smaller or equal than zero it means that the
      // mesh size is not a termination criterion.
      if (0.0 < mesh_size_term < 1.0)
				mesh_size_term = pow(mesh_size_term, 2);
    }
  else
    // MADS n+1 directions.
    if (choice == 4)
      {
				int max_dimension = description->getDimension();

				// If 'max_mesh_size' is equal to 9.9e307, it means that the user
				// doesn't want a maximum mesh size.
				if (max_mesh_size < 9.9e307)
					{
						if (max_mesh_size <= max_dimension)
							max_mesh_size = pow((max_mesh_size / max_dimension), 2);
						else
							max_mesh_size = max_mesh_size / max_dimension;
					}

				// The mesh size is modified.
				if (mesh_size <= max_dimension)
					mesh_size = pow((mesh_size / max_dimension), 2);
				else
					mesh_size = mesh_size / max_dimension;

				// The mesh size termination criterion is modified.
				if (mesh_size_term <= max_dimension)
					mesh_size_term = pow((mesh_size_term / max_dimension), 2);
				else
					mesh_size_term = mesh_size_term / max_dimension;
      }
}

// Public methods.
// ****************************************************************************

// Default constructor. The Solver object creates Mesh at Start-up.
Mesh::Mesh(const Description * const des, const Parameters * const par)
  : description(des), parameters(par)
{
  // If the user chose GPS poll directions, the mesh size is equal to the poll
  // size, the maximum mesh size to the maximum poll size, and the mesh size
  // termination criterion to the poll size termination criterion.
  max_mesh_size = parameters->getMaxPollSize();
  mesh_size = parameters->getPollSize();
  mesh_size_term = parameters->getPollSizeTerm();
  display_factor = 0;

  // If the user chose MADS poll directions, the mesh size and maximum mesh
  // size have to be recalculated.
  if (parameters->getPollDirs() > 2)
    calculateMeshSizes();
}

// Destructor.
Mesh::~Mesh()
{}

// This method is called when there was an iteration failure, i.e. the
// iteration failed to produce a new incumbent point. The mesh is then refined.
// The boolean return value is true if the refined mesh size is smaller than
// the mesh size termination criterion, false otherwise.
bool Mesh::refine()
{
  // The mesh size is refined.
  mesh_size = mesh_size * pow(parameters->getPollBasis(),
			      parameters->getRefine());

  // The refined mesh size is compared to the termination criterion. If
  // (mesh_size_term <= 0.0), it's not a criterion.
  if ((mesh_size_term > 0.0) && (mesh_size <= mesh_size_term))
    // This return value will stop the run.
    return true;
  else
    // The mesh size isn't too small: the run can continue.
    return false;
}

// This method is called when there is a success in the poll, to coarsen the 
// mesh. For example, for the mesh built with the default constructor, we
// have (after a success in the poll): mesh_size = 1.0 * pow(2.0, 1) = 2.0
void Mesh::coarsen()
{
  // 'temp' contains the coarsened mesh size value.
  double temp = mesh_size * pow(parameters->getPollBasis(),
				(int)parameters->getCoarsen());

  // The mesh size can't become larger than 'max_mesh_size'.
  if (mesh_size > max_mesh_size)
    {
      if (display_factor >= 7)
	cout << "The mesh size can't get over the maximum mesh size!\n";
    }
  else
    // The mesh size is coarsened.
    mesh_size = temp;
}

// show -- Outputs the Mesh object.
void Mesh::showInput(ofstream & fout) const
{
  if (!fout.is_open() )
    return;
  fout << "\nMesh variables:\n";
  fout << "---------------\n";
  fout << "Initial mesh size: " << mesh_size << "\n";
  fout << "Maximum mesh size: " << max_mesh_size << "\n";
  if (mesh_size_term > 0.0)
    fout << "Mesh size termination criterion: " << mesh_size_term << "\n";
  fout << "\n";
}
