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

// generalconstraints.cpp -- GeneralConstraints class methods.

#include <cmath>
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
#include "augmentedpoint.h"
#include "defines.h"
#include "description.h"
#include "generalconstraints.h"
#include "parameters.h"

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

// This method creates the strings that will be used as system calls to general
// constraint black boxes.
char * GeneralConstraints::createBoxString(char * file, const char * box) const
{
  const char * directory = description->getDirectory();
  const char * out_file = "output.txt";
  const char * str1 = "cd ";
  const char * str2 = "; ";
  const char * str3 = " > ";
  file = new char[9 + strlen(directory) + strlen(box) + strlen(out_file)];
  strcpy(file, str1);
  strcat(file, directory);
  strcat(file, str2);
  strcat(file, box);
  strcat(file, str3);
  strcat(file, out_file);
  return file;
}

// This method computes h(x), the constraint violation function which is an
// aggregate of the general constraint functions gi(x). The constraint
// violation function has these properties:
// - h(x) = 0 if x is feasible.
// - h(x) > 0 if x is unfeasible.
double GeneralConstraints::calculateHx(const double coord[],
				       bool startingPoint, int pos) const
{
  int filter_norm, gen_cons_nb, i;
  filter_norm = parameters->getFilterNorm();
  gen_cons_nb = description->getGeneralConstraintsNumber();
  double Hx = 0.0;
  double temp, val;
  for (i = 0; i < gen_cons_nb; i++)
    {
      val = coord[pos++];
      temp = 0.0 > val ? 0.0 : val;
      switch(filter_norm)
	{
	  case 0  : Hx = Hx < temp ? temp : Hx;
                    break;
	  case 1  : Hx += temp;
                    break;
	  case 2  : Hx += pow(temp, 2);
	            break;
	  default : cerr << "This is not a norm!\n";
	}
    }

  // The 'hmin' variable is retrieved from Parameters.
  double hmin = parameters->getHmin();
  // h(x) is computed. Its value is changed in these cases:
  // - h(x) is set to '0' if its value is less than 'hmin', a user-defined
  //   parameter.
  // - h(x) is set to 'infinity' if it is over 'hmax', another user-defined
  //   parameter.
  if ((Hx != 0.0) && (Hx < hmin))
    {
      if (display_factor >= 7)
	{
	  cout << "h(x) < hmin: " << Hx << " < " << hmin << "\n";
	  cout << "h(x) is reset to 0.0\n";
	}
      Hx = 0.0;
    }
  // There's an 'else' here because hmin must always be strictly inferior than
  // hmax. Thus the two cases can't both be true, it's one or the other.
  else
    if ((!startingPoint) && (Hx > hmax))
      Hx = INFINIT;
  return Hx;
}

// This method calculates the general constraints function values for a given
// point 'ap'.
void GeneralConstraints::blackBoxRoutine(AugmentedPoint & ap, int pos)
{
  double val;
  for (current = gen_constraints_boxes.begin();
       current != gen_constraints_boxes.end(); current++)
    {
      val = ap.getOutput(pos);
      if (val == INFINIT)
	{
	  system(* current);
	  ifstream output(output_file, ios::in);
	  output >> val;
	  output.close();
	  ap.setOutput(pos, val);
	}
      pos++;
    }
}

// This method creates the black boxes for the general constraints.
void GeneralConstraints::captureBlackBoxes()
{
  // First we capture the general constraints' black boxes.
  int gen_cons_nb = description->getGeneralConstraintsNumber();
  ifstream fin(description->getGeneralConstraints());
  char line[80];
  for (int i = 0; i < gen_cons_nb; i++)
    {
      char * temp = new char[80];
      fin.getline(line, 80);
      strcpy(temp, line);
      char * black_box = NULL;
      black_box = createBoxString(black_box, temp);
      gen_constraints_boxes.push_back(black_box);
    }
  fin.close();
}

// This method initializes the object's variables.
void GeneralConstraints::initialize()
{
  input_file = output_file = 0;
  hmax = parameters->getHmax();
  display_factor = 0;
  current = gen_constraints_boxes.begin();
}

// The user enters his general constraints functions here.
// - 'ap' is the current iterate.
// - 'pos' indicates where the first ci(x) is located in the output array.
void GeneralConstraints::userRoutine(AugmentedPoint &, int) const
{}

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

// This constructor is used with black boxes.
GeneralConstraints::GeneralConstraints(const Description * const des,
				       const Parameters * const par,
				       const char * input, const char * output)
  : description(des), parameters(par)
{
  // The variables are initialized.
  initialize();

  // The strings are created.
  if (input != 0)
    {
      input_file = new char[strlen(input) + 1];
      strcpy(input_file, input);
    }
  if (output != 0)
    {
      output_file = new char[strlen(output) + 1];
      strcpy(output_file, output);
    }

  // Strings for the general constraints black boxes are captured.
  if (description->getBlackBoxUse())
    captureBlackBoxes();

  if (display_factor >= 9)
    {
      cout << "In GeneralConstraints' constructor\n";
      cout << "input_file = " << input_file << "\n";
      cout << "output_file = " << output_file << "\n";
      for (current = gen_constraints_boxes.begin();
	   current != gen_constraints_boxes.end(); current++)
	cout << * current << "\n";
      cout << "\n";
    }
}

// Destructor.
GeneralConstraints::~GeneralConstraints()
{
  if (description->getBlackBoxUse())
    {
      delete [] input_file;
      delete [] output_file;
      for (current = gen_constraints_boxes.begin();
	   current != gen_constraints_boxes.end(); current++)
	delete * current;
      gen_constraints_boxes.clear();
    }
}

// This method evaluates the general constraints function values and h(x).
void GeneralConstraints::evaluate(AugmentedPoint & ap, bool startingPoint)
{
  // The integer variable 'pos' tells us at which position is stored the first
  // general constraint function value in the point's output array. All the
  // other general constraint values are stored sequentially after that.
  int pos = ap.getPosition(2);
  // The proper general constraints evaluation routine is called.
  if (description->getBlackBoxUse())
    blackBoxRoutine(ap, pos);
  else
    userRoutine(ap, pos);

  // The general constraints values are calculated. It's time to calculate h(x)
  double Hx = calculateHx(ap.getOutputValues(), startingPoint, pos);

  // IF we just evaluated h(x) for the starting point, AND
  // hmax is relative to the starting point's h(x)...
  if ((startingPoint) && (parameters->getFilterRelative()))
    {
      // IF the h(x) value is over hmin and finite...
      if ((Hx > parameters->getHmin()) && (Hx != INFINIT))
	// ... THEN hmax is calculated relatively to the starting point's h(x).
	hmax = hmax * Hx;
      else
	// The user requested a relative hmax, but the starting point's h(x)
	// value is incorrect (the point is probably feasible): hmax is given
	// an arbitrary value.
	hmax = 100.0;
    }

  // The point's h(x) value is stored.
  ap.setHx(Hx);
}

// show -- Outputs the GeneralConstraints object.
void GeneralConstraints::showInput(ofstream & fout) const
{
  if (!fout.is_open())
    return;
  fout << "hmax: " << hmax << "\n";
}
