/* nag_opt_nlp (e04ucc) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 4, 1996.
 * Mark 5 revised, 1998.
 * Mark 7 revised, 2001.
 * Mark 8 revised, 2004.
 *
 */

#include <nag.h>
#include <stdio.h>
#include <string.h>
#include <nag_stdlib.h>
#include <nage04.h>

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL objfun(Integer n, const double x[], double *objf,
                            double objgrd[], Nag_Comm *comm);
static void NAG_CALL confun(Integer n, Integer ncnlin, const Integer needc[],
                            const double x[], double conf[], double conjac[],
                            Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

static void NAG_CALL objfun(Integer n, const double x[], double *objf,
                            double objgrd[], Nag_Comm *comm)
{
  /* Routine to evaluate objective function and its 1st derivatives. */

  if (comm->flag == 0 || comm->flag == 2)
    *objf = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];

  /* Note, elements of the objective gradient have not been
     specified.
   */
} /* objfun */

static void NAG_CALL confun(Integer n, Integer ncnlin, const Integer needc[],
                            const double x[], double conf[], double conjac[],
                            Nag_Comm *comm)
{
#define CONJAC(I, J) conjac[((I) -1)*n + (J) -1]

  /* Routine to evaluate the nonlinear constraints and
   * their 1st derivatives.
   */

  /* Function Body */
  if (needc[0] > 0)
    {
      if (comm->flag == 0 || comm->flag == 2)
        conf[0] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];

      if (comm->flag == 2)
        {
          CONJAC(1, 3) = x[2] * 2.0;
          /* Note only one constraint gradient has been specified
           * in the first row of the constraint Jacobian.
           */
        }
    }
  if (needc[1] > 0)
    {
      if (comm->flag == 0 || comm->flag == 2)
        conf[1] = x[0] * x[1] * x[2] * x[3];
      if (comm->flag == 2)
        {
          CONJAC(2, 2) = x[0] * x[2] * x[3];
          CONJAC(2, 3) = x[0] * x[1] * x[3];
          /* Note only two constraint gradients have been specified
           * in the second row of the constraint Jacobian.
           */
        }
    }
} /* confun */

#define A(I, J) a[(I) *tda + J]

int main(void)
{
  const char  *optionsfile = "e04ucce.opt" ;
  Integer     exit_status = 0, i, j, n, nclin, ncnlin, tda, totalvars;
  Nag_Comm    comm;
  NagError    fail;
  Nag_E04_Opt options;
  double      *a = 0, *bl = 0, *bu = 0, objf, *objgrd = 0, *x = 0;

  INIT_FAIL(fail);


  printf("nag_opt_nlp (e04ucc) Example Program Results\n");
  fflush(stdout);
  scanf(" %*[^\n]");  /* Skip heading in data file */
  scanf("%ld%ld%ld%*[^\n]", &n, &nclin, &ncnlin);
  if (n > 0 && nclin >= 0 && ncnlin >= 0)
    {
      totalvars = n + nclin + ncnlin;
      if (!(x = NAG_ALLOC(n, double)) ||
          !(a = NAG_ALLOC(nclin*n, double)) ||
          !(bl = NAG_ALLOC(totalvars, double)) ||
          !(bu = NAG_ALLOC(totalvars, double)) ||
          !(objgrd = NAG_ALLOC(n, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
      tda = n;
    }
  else
    {
      printf("Invalid n or nclin or ncnlin.\n");
      exit_status = 1;
      return exit_status;
    }
  /* Read a, bl, bu and x from data file */

  /* Read the matrix of linear constraint coefficients */
  if (nclin > 0)
    {
      for (i = 0; i < nclin; ++i)
        for (j = 0; j < n; ++j)
          scanf("%lf", &A(i, j));
    }
  scanf("%*[^\n]"); /* Remove remainder of line */

  /* Read lower bounds */
  for (i = 0; i < n + nclin + ncnlin; ++i)
    scanf("%lf", &bl[i]);
  scanf("%*[^\n]");

  /* Read upper bounds */
  for (i = 0; i < n + nclin + ncnlin; ++i)
    scanf("%lf", &bu[i]);
  scanf("%*[^\n]");

  /* Read the initial point x */
  for (i = 0; i < n; ++i)
    scanf("%lf", &x[i]);
  scanf("%*[^\n]");

  /* nag_opt_init (e04xxc).
   * Initialization function for option setting
   */
  nag_opt_init(&options);
  /* nag_opt_read (e04xyc).
   * Read options from a text file
   */
  nag_opt_read("e04ucc", optionsfile, &options, (Nag_Boolean) Nag_TRUE,
               "stdout", &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_opt_read (e04xyc).\n%s\n", fail.message);
      exit_status = 1;
      goto END;
    }

  /* nag_opt_nlp (e04ucc), see above. */
  nag_opt_nlp(n, nclin, ncnlin, a, tda, bl, bu, objfun, confun, x, &objf,
              objgrd, &options, &comm, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_opt_nlp (e04ucc).\n%s\n", fail.message);
      exit_status = 1;
    }

  /* nag_opt_free (e04xzc).
   * Memory freeing function for use with option setting
   */
  nag_opt_free(&options, "all", &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_opt_free (e04xzc).\n%s\n", fail.message);
      exit_status = 1;
      goto END;
    }

 END:
  NAG_FREE(x);
  NAG_FREE(a);
  NAG_FREE(bl);
  NAG_FREE(bu);
  NAG_FREE(objgrd);

  return exit_status;
}