/* nag_opt_nlp (e04ucc) Example Program.
 *
 * Copyright 2017 Numerical Algorithms Group.
 *
 * Mark 26.1, 2017.
 *
 */

#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("%" NAG_IFMT "%" NAG_IFMT "%" NAG_IFMT "%*[^\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;
}