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

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

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL usrfun(Integer *status, Integer n, const double x[],
                            Integer needf, Integer nf, double f[],
                            Integer needg, Integer leng, double g[],
                            Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

int main(void)
{
  /* Scalars */
  double       objadd, sinf;
  Integer      exit_status = 0;
  Integer      i, lena, leng, n, nea, neg, nf, nfname, ninf, ns, nxname;
  Integer      objrow;
  /* Arrays */
  char         nag_enum_arg[40];
  char         **fnames = 0, **xnames = 0;
  char         prob[9];
  double       *a = 0, *f = 0, *flow = 0, *fmul = 0, *fupp = 0, *x = 0;
  double       *xlow = 0, *xmul = 0, *xupp = 0;
  Integer      *fstate = 0, *iafun = 0, *igfun = 0, *iw = 0, *javar = 0;
  Integer      *jgvar = 0, *xstate = 0;
  /* Nag Types */
  Nag_E04State state;
  NagError     fail;
  Nag_Start    start;
  Nag_Comm     comm;
  Nag_FileID   fileid;

  INIT_FAIL(fail);

  printf("nag_opt_sparse_nlp_solve (e04vhc) Example Program Results\n");

  /* Skip heading in data file */
  scanf("%*[^\n] ");
  scanf("%ld%ld%*[^\n] ", &n, &nf);
  scanf("%ld%ld%ld %39s %*[^\n] ", &nea, &neg,
        &objrow, nag_enum_arg);

  /* nag_enum_name_to_value (x04nac).
   * Converts NAG enum member name to value
   */
  start = (Nag_Start) nag_enum_name_to_value(nag_enum_arg);

  if (n > 0 && nf > 0 && nea >= 0 && neg >= 0)
    {
      nxname = n;
      nfname = nf;
      lena = MAX(1, nea);
      leng = MAX(1, neg);
      /* Allocate memory */
      if (!(fnames = NAG_ALLOC(nfname, char *)) ||
          !(xnames = NAG_ALLOC(nxname, char *)) ||
          !(a = NAG_ALLOC(lena, double)) ||
          !(f = NAG_ALLOC(nf, double)) ||
          !(flow = NAG_ALLOC(nf, double)) ||
          !(fmul = NAG_ALLOC(nf, double)) ||
          !(fupp = NAG_ALLOC(nf, double)) ||
          !(x = NAG_ALLOC(n, double)) ||
          !(xlow = NAG_ALLOC(n, double)) ||
          !(xmul = NAG_ALLOC(n, double)) ||
          !(xupp = NAG_ALLOC(n, double)) ||
          !(fstate = NAG_ALLOC(nf, Integer)) ||
          !(iafun = NAG_ALLOC(lena, Integer)) ||
          !(igfun = NAG_ALLOC(leng, Integer)) ||
          !(javar = NAG_ALLOC(lena, Integer)) ||
          !(jgvar = NAG_ALLOC(leng, Integer)) ||
          !(xstate = NAG_ALLOC(n, Integer)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
    }
  else
    {
      printf("Invalid n or nf or nea or neg\n");
      exit_status = 1;
      return exit_status;
    }
  objadd = 0.;
  strcpy(prob, "        ");

  /* Read the variable names xnames */
  for (i = 1; i <= nxname; ++i)
    {
      xnames[i-1] = NAG_ALLOC(9, char);
      scanf(" ' %8s '", xnames[i-1]);
    }
  scanf("%*[^\n] ");

  /* Read the function names fnames */
  for (i = 1; i <= nfname; ++i)
    {
      fnames[i -1] = NAG_ALLOC(9, char);
      scanf(" '%8s'", fnames[i-1]);
    }
  scanf("%*[^\n] ");

  /* Read the sparse matrix a, the linear part of f */
  for (i = 1; i <= nea; ++i)
    {
      /* For each element read row, column, A(row,column) */
      scanf("%ld%ld%lf%*[^\n] ", &iafun[i - 1], &javar[i - 1],
              &a[i - 1]);
    }
  /* Read the structure of sparse matrix G, the nonlinear part of f */
  for (i = 1; i <= neg; ++i)
    {
      /* For each element read row, column */
      scanf("%ld%ld%*[^\n] ", &igfun[i - 1], &jgvar[i - 1]);
    }

  /* Read the lower and upper bounds on the variables */
  for (i = 1; i <= n; ++i)
    scanf("%lf%lf%*[^\n] ", &xlow[i - 1], &xupp[i - 1]);

  /* Read the lower and upper bounds on the functions */
  for (i = 1; i <= nf; ++i)
    scanf("%lf%lf%*[^\n] ", &flow[i - 1], &fupp[i - 1]);

  /* Initialise x, xstate, xmul, f, fstate, fmul */
  for (i = 1; i <= n; ++i)
    scanf("%lf", &x[i - 1]);
  scanf("%*[^\n] ");

  for (i = 1; i <= n; ++i)
    scanf("%ld", &xstate[i - 1]);
  scanf("%*[^\n] ");

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

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

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

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

  /* Call nag_opt_sparse_nlp_init (e04vgc) to initialise e04vhc. */
  /* nag_opt_sparse_nlp_init (e04vgc).
   * Initialization function for nag_opt_sparse_nlp_solve (e04vhc)
   */
  nag_opt_sparse_nlp_init(&state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf(
        "Initialisation of nag_opt_sparse_nlp_init (e04vgc) failed.\n%s\n",
        fail.message);
      exit_status = 1;
      goto END;
    }

  /* By default e04vhc 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, &fileid, &fail);

  /* nag_opt_sparse_nlp_option_set_integer (e04vmc).
   * Set a single option for nag_opt_sparse_nlp_solve (e04vhc)
   * from an integer argument
   */
  nag_opt_sparse_nlp_option_set_integer("Print file", fileid, &state, &fail);

  /* Illustrate how to pass information to the user-supplied
     function usrfun via the comm structure */
  comm.p = 0;

  /* Solve the problem. */
  /* nag_opt_sparse_nlp_solve (e04vhc).
   * General sparse nonlinear optimizer
   */
  fflush(stdout);
  nag_opt_sparse_nlp_solve(start, nf, n, nxname, nfname, objadd, objrow, prob,
                           usrfun, iafun, javar, a, lena, nea, igfun, jgvar,
                           leng, neg, xlow, xupp,
                           (const char **) xnames, flow,
  fupp,
  (const char **) fnames, x, xstate, xmul, f,
                           fstate, fmul, &ns, &ninf, &sinf, &state, &comm,
                           &fail);

  if (fail.code == NE_NOERROR)
    {
      printf("Final objective value = %11.1f\n", f[objrow - 1]);
      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_sparse_nlp_solve (e04vhc).\n%s\n",
             fail.message);
      exit_status = 1;
      goto END;
    }
  fflush(stdout);

  if (fail.code != NE_NOERROR)
    exit_status = 2;

 END:
  for (i = 0; i < nxname; i++)
    NAG_FREE(xnames[i]);
  for (i = 0; i < nfname; i++)
    NAG_FREE(fnames[i]);
  NAG_FREE(fnames);
  NAG_FREE(xnames);
  NAG_FREE(a);
  NAG_FREE(f);
  NAG_FREE(flow);
  NAG_FREE(fmul);
  NAG_FREE(fupp);
  NAG_FREE(x);
  NAG_FREE(xlow);
  NAG_FREE(xmul);
  NAG_FREE(xupp);
  NAG_FREE(fstate);
  NAG_FREE(iafun);
  NAG_FREE(igfun);
  NAG_FREE(iw);
  NAG_FREE(javar);
  NAG_FREE(jgvar);
  NAG_FREE(xstate);

  return exit_status;
}

static void NAG_CALL usrfun(Integer *status, Integer n, const double x[],
                            Integer needf, Integer nf, double f[],
                            Integer needg, Integer leng, double g[],
                            Nag_Comm *comm)
{
  /* Parameter adjustments */
#define X(I) x[(I) -1]
#define F(I) f[(I) -1]
#define G(I) g[(I) -1]

  /* Check whether information came from the main program
     via the comm structure. Even if it was, we ignore it
     in this example. */
  if (comm->p)
    printf("Pointer %p was passed to usrfun via the comm struct\n", comm->p);

  /* Function Body */
  if (needf > 0)
    {
      /* The nonlinear components of F_i(x) need to be assigned, */
      /* for i = 1 to nf */
      F(1) = sin(-X(1) - .25) * 1e3 + sin(-X(2) - .25) * 1e3;
      F(2) = sin(X(1) - .25) * 1e3 + sin(X(1) - X(2) - .25) * 1e3;
      F(3) = sin(X(2) - X(1) - .25) * 1e3 + sin(X(2) - .25) * 1e3;
      /* N.B. in this example there is no need to assign for the wholly */
      /* linear components f_4(x) and f_5(x). */
      F(6) = X(3) * (X(3) * X(3)) * 1e-6 + X(4) * (X(4) * X(4)) * 2e-6 / 3.;
    }

  if (needg > 0)
    {
      /* The derivatives of the function F_i(x) need to be assigned.
       * G(k) should be set to partial derivative df_i(x)/dx_j where
       * i = IGFUN(k) and j = IGVAR(k), for k = 1 to leng.
       */
      G(1) = cos(-X(1) - .25) * -1e3;
      G(2) = cos(-X(2) - .25) * -1e3;
      G(3) = cos(X(1) - .25) * 1e3 + cos(X(1) - X(2) - .25) * 1e3;
      G(4) = cos(X(1) - X(2) - .25) * -1e3;
      G(5) = cos(X(2) - X(1) - .25) * -1e3;
      G(6) = cos(X(2) - X(1) - .25) * 1e3 + cos(X(2) - .25) * 1e3;
      G(7) = X(3) * X(3) * 3e-6;
      G(8) = X(4) * X(4) * 2e-6;
    }

  return;
} /* usrfun */