/* nag_opt_nlp_sparse (e04ugc) Example Program.
 *
 * Copyright 2017 Numerical Algorithms Group.
 *
 * NAG C Library
 *
 * Mark 26.1, 2017.
 *
 */

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

#ifdef __cplusplus
extern "C"
{
#endif
  static void NAG_CALL confun(Integer ncnln, Integer njnln,
                              Integer nnzjac, const double x[], double conf[],
                              double conjac[], Nag_Comm *comm);

  static void NAG_CALL objfun(Integer nonln,
                              const double x[], double *objf,
                              double objgrad[], Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

#define NAMES(I, J) names[(I)*9+J]

int main(void)
{
  const char *optionsfile = "e04ugce.opt";
  Integer exit_status = 0, *ha = 0, i, icol, iobj, j, jcol, *ka = 0, m, n,
         ncnln;
  Integer ninf, njnln, nnz, nonln;
  Nag_E04_Opt options;
  char **crnames = 0, *names = 0;
  double *a = 0, *bl = 0, *bu = 0, obj, sinf, *xs = 0;
  Integer verbose_output;
  Nag_Comm comm;
  NagError fail;

  INIT_FAIL(fail);

  printf("nag_opt_nlp_sparse (e04ugc) Example Program Results\n");
  fflush(stdout);

  /* Skip heading in data file */
  scanf(" %*[^\n]");
  /* Read the problem dimensions */
  scanf(" %*[^\n]");
  scanf("%" NAG_IFMT "%" NAG_IFMT "", &n, &m);
  /* Read NCNLN, NONLN and NJNLN from data file. */
  scanf(" %*[^\n]");
  scanf("%" NAG_IFMT "%" NAG_IFMT "%" NAG_IFMT "", &ncnln, &nonln, &njnln);
  /* Read NNZ, IOBJ */
  scanf(" %*[^\n]");
  scanf("%" NAG_IFMT "%" NAG_IFMT "", &nnz, &iobj);

  if (!(a = NAG_ALLOC(nnz, double)) ||
      !(bl = NAG_ALLOC(n + m, double)) ||
      !(bu = NAG_ALLOC(n + m, double)) ||
      !(xs = NAG_ALLOC(n + m, double)) ||
      !(ha = NAG_ALLOC(nnz, Integer)) ||
      !(ka = NAG_ALLOC(n + 1, Integer)) ||
      !(crnames = NAG_ALLOC(n + m, char *)) ||
      !(names = NAG_ALLOC((n + m) * 9, char))
         )
  {
    printf("Allocation failure\n");
    exit_status = 1;
    goto END;
  }

  /* Read the column and row names */
  scanf(" %*[^\n]");
  scanf(" %*[^']");
  for (i = 0; i < n + m; ++i) {
    scanf(" '%8c'", &NAMES(i, 0));
    NAMES(i, 8) = '\0';
    crnames[i] = &NAMES(i, 0);
  }

  /* read the matrix and set up ka. */
  jcol = 1;
  ka[jcol - 1] = 0;
  scanf(" %*[^\n]");
  for (i = 0; i < nnz; ++i) {
    /* a[i] stores (ha[i], icol) element of matrix */
    scanf("%lf%" NAG_IFMT "%" NAG_IFMT "", &a[i], &ha[i], &icol);
    if (icol < jcol) {
      /* Elements not ordered by increasing column index. */
      printf("Element in column%5" NAG_IFMT " found after element in"
             " column%5" NAG_IFMT ". Problem abandoned.\n", icol, jcol);
      exit_status = 1;
      goto END;
    }
    else if (icol == jcol + 1) {
      /* Index in a of the start of the icol-th column equals i. */
      ka[icol - 1] = i;
      jcol = icol;
    }
    else if (icol > jcol + 1) {
      /* Index in a of the start of the icol-th column equals i,
       *  but columns jcol+1,jcol+2,...,icol-1 are empty. Set the
       *  corresponding elements of ka to i.
       */
      for (j = jcol + 1; j <= icol - 1; ++j)
        ka[j - 1] = i;

      ka[icol - 1] = i;
      jcol = icol;
    }
  }
  ka[n] = nnz;
  if (n > icol) {
    /* Columns N,N-1,...,ICOL+1 are empty. Set the
     *  corresponding elements of ka accordingly. */
    for (j = icol; j <= n - 1; ++j)
      ka[j] = nnz;
  }

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

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

  /* Initialize the options structure */
  /* nag_opt_init (e04xxc).
   * Initialization function for option setting
   */
  nag_opt_init(&options);

  /* Set this to 1 to cause e04ugc to produce intermediate
     progress output */
  verbose_output = 0;

  /* Read some option values from standard input */
  /* nag_opt_read (e04xyc).
   * Read options from a text file
   */
  nag_opt_read("e04ugc", optionsfile, &options,
               (Nag_Boolean)(verbose_output==1), "stdout", &fail);
  /* Set some other options directly */
  options.major_iter_lim = 100;
  options.crnames = crnames;

  if (verbose_output == 0)
    {
      options.list = Nag_FALSE;
      options.print_level = Nag_NoPrint;
      options.print_deriv = Nag_D_NoPrint;
    }

  /* Solve the problem. */
  /* nag_opt_nlp_sparse (e04ugc), see above. */
  nag_opt_nlp_sparse(confun, objfun, n, m,
                     ncnln, nonln, njnln, iobj, nnz,
                     a, ha, ka, bl, bu, xs,
                     &ninf, &sinf, &obj, &comm, &options, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_opt_nlp_sparse (e04ugc).\n%s\n", fail.message);
    exit_status = 1;
    goto END;
  }

  if (verbose_output == 0)
    {
      printf("\n");
      printf("Final objective value = %12.3f\n", obj);
      printf("Optimal X = ");
      for (i = 1; i <= n; ++i) {
        printf("%9.3f%s", xs[i - 1], i % 7 == 0 || i == n ? "\n" : " ");
      }
    }

  /* We perturb the solution and solve the
   * same problem again using a warm start.
   */
  printf("\nA run of the same example with a warm start:\n");
  printf("--------------------------------------------\n");
  options.start = Nag_Warm;

  /* Modify some printing options */
  options.print_deriv = Nag_D_NoPrint;
  if (verbose_output == 1)
    options.print_level = Nag_Iter;
  else
    options.print_level = Nag_NoPrint;

  /* Perturb xs */
  for (i = 0; i < n + m; i++)
    xs[i] += 0.2;

  /* Reset multiplier estimates to 0.0 */
  if (ncnln > 0) {
    for (i = 0; i < ncnln; i++)
      options.lambda[n + i] = 0.0;
  }
  /* Solve the problem again. */
  /* nag_opt_nlp_sparse (e04ugc), see above. */
  fflush(stdout);
  nag_opt_nlp_sparse(confun, objfun, n, m,
                     ncnln, nonln, njnln, iobj, nnz,
                     a, ha, ka, bl, bu, xs,
                     &ninf, &sinf, &obj, &comm, &options, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_opt_nlp_sparse (e04ugc).\n%s\n", fail.message);
    exit_status = 1;
  }

  if (verbose_output == 0)
    {
      printf("Final objective value = %12.3f\n", obj);
      printf("Optimal X = ");
      for (i = 1; i <= n; ++i) {
        printf("%9.3f%s", xs[i - 1], i % 7 == 0 || i == n ? "\n" : " ");
      }
    }

  /* Free memory allocated by nag_opt_nlp_sparse (e04ugc) to pointers in options
   */
  /* 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(a);
  NAG_FREE(bl);
  NAG_FREE(bu);
  NAG_FREE(xs);
  NAG_FREE(ha);
  NAG_FREE(ka);
  NAG_FREE(crnames);
  NAG_FREE(names);

  return exit_status;
}

/* Subroutine */
static void NAG_CALL confun(Integer ncnln, Integer njnln, Integer nnzjac,
                            const double x[], double conf[], double conjac[],
                            Nag_Comm *comm)
{
#define CONJAC(I) conjac[(I) -1]
#define CONF(I)   conf[(I) -1]
#define X(I)      x[(I) -1]
  /* Compute the nonlinear constraint functions and their Jacobian. */
  if (comm->flag == 0 || comm->flag == 2) {
    CONF(1) = sin(-X(1) - 0.25) * 1e3 + sin(-X(2) - 0.25) * 1e3;
    CONF(2) = sin(X(1) - 0.25) * 1e3 + sin(X(1) - X(2) - 0.25) * 1e3;
    CONF(3) = sin(X(2) - X(1) - 0.25) * 1e3 + sin(X(2) - 0.25) * 1e3;
  }
  if (comm->flag == 1 || comm->flag == 2) {
    /* Nonlinear Jacobian elements for column 1.0 */
    CONJAC(1) = cos(-X(1) - 0.25) * -1e3;
    CONJAC(2) = cos(X(1) - 0.25) * 1e3 + cos(X(1) - X(2) - 0.25) * 1e3;
    CONJAC(3) = cos(X(2) - X(1) - 0.25) * -1e3;
    /* Nonlinear Jacobian elements for column 2.0 */
    CONJAC(4) = cos(-X(2) - 0.25) * -1e3;
    CONJAC(5) = cos(X(1) - X(2) - 0.25) * -1e3;
    CONJAC(6) = cos(X(2) - X(1) - 0.25) * 1e3 + cos(X(2) - 0.25) * 1e3;
  }
}

static void NAG_CALL objfun(Integer nonln, const double x[], double *objf,
                            double objgrad[], Nag_Comm *comm)
{
#define OBJGRAD(I) objgrad[(I) -1]
#define X(I)       x[(I) -1]
  /* Compute the nonlinear part of the objective function and its grad */
  if (comm->flag == 0 || comm->flag == 2)
    *objf = X(3) * X(3) * X(3) * 1e-6 + X(4) * X(4) * X(4) * 2e-6 / 3.0;
  if (comm->flag == 1 || comm->flag == 2) {
    OBJGRAD(1) = 0.0;
    OBJGRAD(2) = 0.0;
    OBJGRAD(3) = X(3) * X(3) * 3e-6;
    OBJGRAD(4) = X(4) * X(4) * 2e-6;
  }
}