/* nag_opt_nlp_option_set_file (e04wec) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 8, 2004.
 */

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

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL confun(Integer *mode, Integer ncnln, Integer n,
                            Integer ldcj, const Integer needc[],
                            const double x[], double ccon[], double cjac[],
                            Integer nstate, Nag_Comm *comm);
static void NAG_CALL objfun(Integer *mode, Integer n, const double x[],
                            double *objf, double grad[], Integer nstate,
                            Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

int main(void)
{
  const char   *optionsfile = "e04wece.opt";

  /* Scalars */
  double       bndinf, featol, objf;
  Integer      elmode, exit_status, i, j, majits, n, nclin, ncnln, nctotal,
               pda;
  Integer      pdcj, pdh;

  /* Arrays */
  static double ruser[2] = {-1.0, -1.0};
  double       *a = 0, *bl = 0, *bu = 0, *ccon = 0, *cjac = 0, *clamda = 0;
  double       *grad = 0, *hess = 0, *x = 0;
  Integer      *istate = 0, *iuser = 0;

  /* Nag Types */
  Nag_E04State state;
  NagError     fail;
  Nag_Comm     comm;
  Nag_FileID   fileidin;
  Nag_FileID   fileidout;

#define A(I, J) a[(I-1)*pda + J - 1]

  exit_status = 0;
  INIT_FAIL(fail);


  printf("%s", "nag_opt_nlp_option_set_file (e04wec) Example Program"
          " Results");
  printf("\n");

  /* For communication with user-supplied functions: */
  comm.user = ruser;

  fflush(stdout);

  /* This program demonstrates the use of routines to set and get values of
   * optional parameters associated with nag_opt_nlp_solve (e04wdc).
   */

  /* Skip heading in data file */
  scanf("%*[^\n] ");
  scanf("%ld %ld %ld ", &n, &nclin, &ncnln);
  scanf("%*[^\n] ");

  if (n > 0 && nclin >= 0 && ncnln >= 0)
    {
      /* Allocate memory */
      nctotal = n + nclin + ncnln;
      if (!(a = NAG_ALLOC(ncnln*n, double)) ||
          !(bl = NAG_ALLOC(nctotal, double)) ||
          !(bu = NAG_ALLOC(nctotal, double)) ||
          !(ccon = NAG_ALLOC(ncnln, double)) ||
          !(cjac = NAG_ALLOC(ncnln*n, double)) ||
          !(clamda = NAG_ALLOC(nctotal, double)) ||
          !(grad = NAG_ALLOC(n, double)) ||
          !(hess = NAG_ALLOC(n*n, double)) ||
          !(x = NAG_ALLOC(n, double)) ||
          !(istate = NAG_ALLOC(nctotal, Integer)) ||
          !(iuser = NAG_ALLOC(1, Integer)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
      pda = n;
      pdcj = n;
      pdh = n;
    }
  /* Read A, BL, BU and X from data file */
  if (nclin > 0)
    {
      for (i = 1; i <= nclin; ++i)
        {
          for (j = 1; j <= n; ++j)
            {
              scanf("%lf", &A(i, j));
            }
        }
      scanf("%*[^\n] ");
    }

  for (i = 1; i <= n + nclin + ncnln; ++i)
    {
      scanf("%lf", &bl[i - 1]);
    }
  scanf("%*[^\n] ");

  for (i = 1; i <= n + nclin + ncnln; ++i)
    {
      scanf("%lf", &bu[i - 1]);
    }
  scanf("%*[^\n] ");

  for (i = 1; i <= n; ++i)
    {
      scanf("%lf", &x[i - 1]);
    }
  scanf("%*[^\n] ");

  /* Call nag_opt_nlp_init (e04wcc) to initialise nag_opt_nlp_solve (e04wdc). */
  /* nag_opt_nlp_init (e04wcc).
   * Initialization function for nag_opt_nlp_solve (e04wdc)
   */
  nag_opt_nlp_init(&state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Initialisation of nag_opt_nlp_init (e04wcc) failed.\n%s\n",
              fail.message);
      exit_status = 1;
      goto END;
    }
  /* By default nag_opt_nlp_solve (e04wdc) does not print monitoring
   *  information. Call nag_open_file (x04acc) to set the print file fileid.
   */
  /* nag_open_file (x04acc).
   * Open unit number for reading, writing or appending, and
   * associate unit with named file
   */

  nag_open_file("", 2, &fileidout, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Fileidout could not be obtained.\n");
      exit_status = 1;
      goto END;
    }

  /* Use nag_opt_nlp_option_set_integer (e04wgc) to set the Integer-valued
   *  option 'Print file' */
  /* nag_opt_nlp_option_set_integer (e04wgc).
   * Set a single option for nag_opt_nlp_solve (e04wdc) from
   * an integer argument
   */
  nag_opt_nlp_option_set_integer("Print file", fileidout, &state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("nag_opt_nlp_option_set_integer (e04wgc) failed to set Print"
        " File\n");
      exit_status = 1;
      goto END;
    }

  /* Use nag_opt_nlp_option_set_file (e04wec) to read some options from
   * the options file. Firsr call nag_open_file (x04acc) to set the options file
   * fileid.
   */
  nag_open_file(optionsfile, 0, &fileidin, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Fileidin could not be obtained.\n");
      exit_status = 1;
      goto END;
    }
  /* nag_opt_nlp_option_set_file (e04wec).
   * Supply optional parameter values for nag_opt_nlp_solve
   * (e04wdc) from external file
   */
  nag_opt_nlp_option_set_file(fileidin, &state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("nag_opt_nlp_option_set_file (e04wec) could not read input"
        " File\n");
      exit_status = 1;
      goto END;
    }

  /* Use nag_opt_nlp_option_get_integer (e04wkc) to find the value of
   * Integer-valued option 'Elastic mode'.
   */
  /* nag_opt_nlp_option_get_integer (e04wkc).
   * Get the setting of an integer valued option of
   * nag_opt_nlp_solve (e04wdc)
   */
  nag_opt_nlp_option_get_integer("Elastic mode", &elmode, &state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf(
        "nag_opt_nlp_option_get_integer (e04wkc) failed to find the value"
        " of Elastic Mode\n");
      exit_status = 1;
      goto END;
    }
  printf("Option 'Elastic mode' has the value ");
  printf("%3ld.\n", elmode);

  /* Use nag_opt_nlp_option_set_double (e04whc) to set the value of real-valued
   *  option 'Infinite bound size'.
   */
  bndinf = 1e10;
  /* nag_opt_nlp_option_set_double (e04whc).
   * Set a single option for nag_opt_nlp_solve (e04wdc) from a
   * double argument
   */
  nag_opt_nlp_option_set_double("Infinite bound size", bndinf, &state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("nag_opt_nlp_option_set_double (e04whc) failed to set Infinite"
        " bound size\n");
      exit_status = 1;
      goto END;
    }

  /* Use nag_opt_nlp_option_get_double (e04wlc) to find the value of real-valued
   *  option 'Feasibility tolerance'.
   */
  /* nag_opt_nlp_option_get_double (e04wlc).
   * Get the setting of a double valued option of
   * nag_opt_nlp_solve (e04wdc)
   */
  nag_opt_nlp_option_get_double("Feasibility tolerance", &featol, &state,
                                &fail);
  if (fail.code != NE_NOERROR)
    {
      printf(
        "nag_opt_nlp_option_get_double (e04wlc) failed to find the value"
        " of a real-valued option\n");
      exit_status = 1;
      goto END;
    }
  printf("Option 'Feasibility tolerance' has the value %14.5e.\n",
          featol);

  /* Use nag_opt_nlp_option_set_string (e04wfc) to set the option 'Major
   *  iterations limit'.
   */
  /* nag_opt_nlp_option_set_string (e04wfc).
   * Set a single option for nag_opt_nlp_solve (e04wdc) from a
   * character string
   */
  nag_opt_nlp_option_set_string("Major iterations limit 50", &state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("nag_opt_nlp_option_set_string (e04wfc) failed to set Major"
        " iterations limit\n");
      exit_status = 1;
      goto END;
    }
  fflush(stdout);

  /* Solve the problem. */
  /* nag_opt_nlp_solve (e04wdc).
   * Solves the nonlinear programming (NP) problem
   */
  nag_open_file("", 2, &fileidout, &fail); /* Open library output */
  nag_opt_nlp_option_set_integer("Print file", fileidout, &state, &fail);
  fflush(stdout);
  nag_opt_nlp_solve(n, nclin, ncnln, pda, pdcj, pdh, a, bl, bu,
                    confun, objfun, &majits, istate, ccon, cjac, clamda, &
                    objf, grad, hess, x, &state, &comm, &fail);


  if (fail.code == NE_NOERROR)
    {
      printf("\nFinal objective value = %11.3f\n", objf);

      printf("Optimal X = ");
      for (i = 1; i <= n; ++i)
        {
          printf("%9.2f%s", x[i - 1], i%7 == 0 || i == n?"\n":" ");
        }
    }
  else
    {
      printf("Error message from nag_opt_nlp_solve (e04wdc).\no%s\n",
              fail.message);
    }
 END:
  NAG_FREE(a);
  NAG_FREE(bl);
  NAG_FREE(bu);
  NAG_FREE(ccon);
  NAG_FREE(cjac);
  NAG_FREE(clamda);
  NAG_FREE(grad);
  NAG_FREE(hess);
  NAG_FREE(x);
  NAG_FREE(istate);
  NAG_FREE(iuser);

  return exit_status;
}

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

  /* Function Body */
  if (comm->user[0] == -1.0)
    {
      fflush(stdout);
      printf("(User-supplied callback objfun, first invocation.)\n");
      comm->user[0] = 0.0;
      fflush(stdout);
    }
  if (*mode == 0 || *mode == 2)
    {
      *objf = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
    }

  if (*mode == 1 || *mode == 2)
    {
      grad[0] = x[3] * (x[0] * 2. + x[1] + x[2]);
      grad[1] = x[0] * x[3];
      grad[2] = x[0] * x[3] + 1.;
      grad[3] = x[0] * (x[0] + x[1] + x[2]);
    }

  return;
} /* objfun */

static void NAG_CALL confun(Integer *mode, Integer ncnln, Integer n,
                            Integer ldcj, const Integer needc[],
                            const double x[], double ccon[], double cjac[],
                            Integer nstate, Nag_Comm *comm)
{
  /* Scalars */
  Integer i, j;

#define CJAC(I, J) cjac[(I-1)*ldcj + J-1]

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


  /* Function Body */
  if (comm->user[1] == -1.0)
    {
      fflush(stdout);
      printf("(User-supplied callback confun, first invocation.)\n");
      comm->user[1] = 0.0;
      fflush(stdout);
    }
  if (nstate == 1)
    {
      /* First call to CONFUN.  Set all Jacobian elements to zero. */
      /* Note that this will only work when 'Derivative Level = 3' */
      /* (the default; see Section 11.2). */
      for (j = 1; j <= n; ++j)
        {
          for (i = 1; i <= ncnln; ++i)
            {
              CJAC(i, j) = 0.;
            }
        }
    }

  if (needc[0] > 0)
    {
      if (*mode == 0 || *mode == 2)
        {
          ccon[0] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];
        }
      if (*mode == 1 || *mode == 2)
        {
          CJAC(1, 1) = x[0] * 2.;
          CJAC(1, 2) = x[1] * 2.;
          CJAC(1, 3) = x[2] * 2.;
          CJAC(1, 4) = x[3] * 2.;
        }
    }

  if (needc[1] > 0)
    {
      if (*mode == 0 || *mode == 2)
        {
          ccon[1] = x[0] * x[1] * x[2] * x[3];
        }
      if (*mode == 1 || *mode == 2)
        {
          CJAC(2, 1) = x[1] * x[2] * x[3];
          CJAC(2, 2) = x[0] * x[2] * x[3];
          CJAC(2, 3) = x[0] * x[1] * x[3];
          CJAC(2, 4) = x[0] * x[1] * x[2];
        }
    }

  return;
} /* confun */