/* nag_opt_sparse_nlp_option_set_file (e04vkc) Example Program.
 *
 * Copyright 2017 Numerical Algorithms Group.
 *
 * Mark 26.1, 2017.
 */

#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)
{
  const char *optionsfile = "e04vkce.opt";

  /* Scalars */
  double bndinf, featol, objadd, sinf;
  Integer elmode, exit_status = 0, i, lena, leng, n, nea, neg, nf, nfname,
         ninf;
  Integer ns, nxname, objrow;

  /* Arrays */
  static double ruser[1] = { -1.0 };
  char nag_enum_arg[40];
  char **fnames = 0, *prob = 0, **xnames = 0;
  double *a = 0, *f = 0, *flow = 0, *fmul = 0, *fupp = 0;
  double *x = 0, *xlow = 0, *xmul = 0, *xupp = 0;
  Integer *fstate = 0, *iafun = 0, *igfun = 0, *iuser = 0, *javar = 0;
  Integer *jgvar = 0, *xstate = 0;

  /* Nag Types */
  Nag_E04State state;
  NagError fail;
  Nag_Comm comm;
  Nag_Start start;
  Nag_FileID optfileid;

  /* By default e04vhc does not print monitoring information.
     Define SHOW_MONITORING_INFO to turn it on - see further below. */
#ifdef SHOW_MONITORING_INFO
  Nag_FileID outfileid;
#endif

  INIT_FAIL(fail);

  printf("%s\n",
         "nag_opt_sparse_nlp_option_set_file (e04vkc) Example Program"
         " Results");

  /* 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_sparse_nlp_solve (e04vhc).
   */

  /* Skip heading in data file */
  scanf("%*[^\n] ");
  scanf("%" NAG_IFMT "%" NAG_IFMT "%*[^\n] ", &n, &nf);
  scanf("%" NAG_IFMT "%" NAG_IFMT "%" NAG_IFMT " %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;

    /* Allocate memory */
    if (!(fnames = NAG_ALLOC(nfname, char *)) ||
        !(prob = NAG_ALLOC(9, char)) ||
        !(xnames = NAG_ALLOC(nxname, char *)) ||
        !(a = NAG_ALLOC(300, double)) ||
        !(f = NAG_ALLOC(100, double)) ||
        !(flow = NAG_ALLOC(100, double)) ||
        !(fmul = NAG_ALLOC(100, double)) ||
        !(fupp = NAG_ALLOC(100, double)) ||
        !(x = NAG_ALLOC(100, double)) ||
        !(xlow = NAG_ALLOC(100, double)) ||
        !(xmul = NAG_ALLOC(100, double)) ||
        !(xupp = NAG_ALLOC(100, double)) ||
        !(fstate = NAG_ALLOC(100, Integer)) ||
        !(iafun = NAG_ALLOC(300, Integer)) ||
        !(igfun = NAG_ALLOC(300, Integer)) ||
        !(iuser = NAG_ALLOC(1, Integer)) ||
        !(javar = NAG_ALLOC(300, Integer)) ||
        !(jgvar = NAG_ALLOC(300, Integer)) ||
        !(xstate = NAG_ALLOC(100, 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;
  }
  lena = MAX(1, nea);
  leng = MAX(1, neg);
  objadd = 0.;
  strcpy(prob, "        ");

  /* Read the variable names xnames */

  for (i = 0; i < nxname; ++i) {
    xnames[i] = NAG_ALLOC(9, char);
    scanf(" ' %8s '", xnames[i]);
  }
  scanf("%*[^\n] ");

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

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

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

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

  /* Initialize x, xstate, xmul, f, fstate, fmul */
  for (i = 0; i < n; ++i) {
    scanf("%lf", &x[i]);
  }
  scanf("%*[^\n] ");

  for (i = 0; i < n; ++i) {
    scanf("%" NAG_IFMT "", &xstate[i]);
  }
  scanf("%*[^\n] ");

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

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

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

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

  /* Initialize e04vhc using 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("Initialization of nag_opt_sparse_nlp_init (e04vgc) failed.\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }

#ifdef SHOW_MONITORING_INFO
  /* Call nag_open_file (x04acc) to set the print file outfileid */
  /* nag_open_file (x04acc).
   * Open unit number for reading, writing or appending, and
   * associate unit with named file
   */
  nag_open_file("", 2, &outfileid, &fail);
  if (fail.code != NE_NOERROR) {
    exit_status = 2;
    goto END;
  }

  /* 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", outfileid, &state,
                                        &fail);

  if (fail.code != NE_NOERROR) {
    exit_status = 1;
    goto END;
  }
#endif

  /* Use nag_opt_sparse_nlp_option_set_file (e04vkc) to read some options from
   * the options file. Call nag_open_file (x04acc) to set the
   * options file optfileid.
   */
  nag_open_file(optionsfile, 0, &optfileid, &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }
  /* nag_opt_sparse_nlp_option_set_file (e04vkc).
   * Supply optional parameter values for
   * nag_opt_sparse_nlp_solve (e04vhc) from external file
   */
  nag_opt_sparse_nlp_option_set_file(optfileid, &state, &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }
  printf("\n");

  /* Find the value of Integer-valued option 'Elastic mode' using
   * nag_opt_sparse_nlp_option_get_integer (e04vrc):
   *  Get the setting of an integer valued option of
   *  nag_opt_sparse_nlp_solve (e04vhc)
   */
  nag_opt_sparse_nlp_option_get_integer("Elastic mode", &elmode, &state,
                                        &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }
  printf("Option 'Elastic mode' has the value %3" NAG_IFMT ".\n", elmode);

  /* Use nag_opt_sparse_nlp_option_set_double (e04vnc) to set the value of
   *  real-valued option 'Infinite bound size'.
   */
  bndinf = 1e10;
  /* nag_opt_sparse_nlp_option_set_double (e04vnc).
   * Set a single option for nag_opt_sparse_nlp_solve (e04vhc)
   * from a double argument
   */
  nag_opt_sparse_nlp_option_set_double("Infinite bound size", bndinf, &state,
                                       &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }

  /* Find the value of real-valued option 'Feasibility tolerance' using
   * nag_opt_sparse_nlp_option_get_double (e04vsc):
   *  Get the setting of a double valued option of
   *  nag_opt_sparse_nlp_solve (e04vhc)
   */
  nag_opt_sparse_nlp_option_get_double("Feasibility tolerance", &featol,
                                       &state, &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }
  printf("Option 'Feasibility tolerance' has the value %14.5e.\n", featol);

  /* Set the option 'Major iterations limit' using
   * nag_opt_sparse_nlp_option_set_string (e04vlc):
   *  Set a single option for nag_opt_sparse_nlp_solve (e04vhc)
   *  from a character string
   */
  nag_opt_sparse_nlp_option_set_string("Major iterations limit 50", &state,
                                       &fail);
  if (fail.code != NE_NOERROR) {
    nag_close_file(optfileid, &fail);
    exit_status = 1;
    goto END;
  }
  printf("\n");
  fflush(stdout);

  /* Solve the problem. */
  /* nag_opt_sparse_nlp_solve (e04vhc).
   * General sparse nonlinear optimizer
   */
  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("Error from nag_opt_sparse_nlp_solve (e04vhc).\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }
  nag_close_file(optfileid, &fail);

  printf("Final objective value = %11.1f\n", f[objrow - 1]);
  printf("Optimal X = ");

  for (i = 0; i < n; ++i)
    printf("%9.2f%s", x[i], i % 7 == 6 || i == n - 1 ? "\n" : " ");

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(prob);
  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(iuser);
  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)
{
  if (comm->user[0] == -1.0) {
    fflush(stdout);
    printf("(User-supplied callback usrfun, first invocation.)\n");
    comm->user[0] = 0.0;
    fflush(stdout);
  }
  if (needf > 0) {
    /* The nonlinear components of f_i(x) need to be assigned, */
    f[0] = sin(-x[0] - .25) * 1e3 + sin(-x[1] - .25) * 1e3;
    f[1] = sin(x[0] - .25) * 1e3 + sin(x[0] - x[1] - .25) * 1e3;
    f[2] = sin(x[1] - x[0] - .25) * 1e3 + sin(x[1] - .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[5] = x[2] * (x[2] * x[2]) * 1e-6 + x[3] * (x[3] * x[3]) * 2e-6 / 3.;
  }

  if (needg > 0) {
    /* The derivatives of the function f_i(x) need to be assigned.
     * g[k-1] should be set to partial derivative df_i(x)/dx_j where
     * i = igfun[k-1] and j = igvar[k-1], for k = 1 to LENG. 
     */
    g[0] = cos(-x[0] - .25) * -1e3;
    g[1] = cos(-x[1] - .25) * -1e3;
    g[2] = cos(x[0] - .25) * 1e3 + cos(x[0] - x[1] - .25) * 1e3;
    g[3] = cos(x[0] - x[1] - .25) * -1e3;
    g[4] = cos(x[1] - x[0] - .25) * -1e3;
    g[5] = cos(x[1] - x[0] - .25) * 1e3 + cos(x[1] - .25) * 1e3;
    g[6] = x[2] * x[2] * 3e-6;
    g[7] = x[3] * x[3] * 2e-6;
  }

  return;
} /* usrfun */