/* nag_opt_bounds_2nd_deriv (e04lbc) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 5, 1998.
 *
 * Mark 6 revised, 2000.
 * Mark 7 revised, 2001.
 * Mark 8 revised, 2004.
 *
 */

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

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL funct(Integer n, const double xc[], double *fc,
                           double gc[], Nag_Comm *comm);
static void NAG_CALL h(Integer n, const double xc[], double fhesl[],
                       double fhesd[], Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

int main(void)
{
  const char  *optionsfile = "e04lbce.opt";
  static double ruser[2] = {-1.0, -1.0};
  Integer     exit_status = 0;
  Nag_Boolean print;
  Integer     n = 4;
  Nag_Comm    comm;
  Nag_E04_Opt options;
  double      *bl = 0, *bu = 0, f, *g = 0, *x = 0;
  NagError    fail;

  INIT_FAIL(fail);


  printf("nag_opt_bounds_2nd_deriv (e04lbc) Example Program Results\n");

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

  if (n >= 1)
    {
      if (!(x = NAG_ALLOC(n, double)) ||
          !(bl = NAG_ALLOC(n, double)) ||
          !(bu = NAG_ALLOC(n, double)) ||
          !(g = NAG_ALLOC(n, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
    }
  else
    {
      printf("Invalid n.\n");
      exit_status = 1;
      return exit_status;
    }

  bl[0] = 1.0;
  bu[0] = 3.0;
  bl[1] = -2.0;
  bu[1] = 0.0;

  /* x[2] is not bounded, so we set bl[2] to a large negative
   *  number and bu[2] to a large positive number
   */
  bl[2] = -1e6;
  bu[2] = 1e6;
  bl[3] = 1.0;
  bu[3] = 3.0;

  /* Set up starting point */
  x[0] = 3.0;
  x[1] = -1.0;
  x[2] = 0.0;
  x[3] = 1.0;

  print = Nag_TRUE;
  /* nag_opt_init (e04xxc).
   * Initialization function for option setting
   */
  nag_opt_init(&options);
  /* nag_opt_read (e04xyc).
   * Read options from a text file
   */
  fflush(stdout);
  nag_opt_read("e04lbc", optionsfile, &options, print, "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_bounds_2nd_deriv (e04lbc), see above. */
  nag_opt_bounds_2nd_deriv(n, funct, h, Nag_Bounds, bl, bu, x, &f, g,
                           &options, &comm, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error or warning from "
              "nag_opt_bounds_2nd_deriv (e04lbc).\n%s\n", fail.message);
      if (fail.code != NW_COND_MIN)
        exit_status = 1;
    }

  /* Free memory allocated by nag_opt_bounds_deriv (e04kbc) to pointers hesd,
   * hesl and state.
   */
  /* 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_bounds_2nd_deriv (e04lbc).\n%s\n",
              fail.message);
      exit_status = 1;
      goto END;
    }

 END:
  NAG_FREE(x);
  NAG_FREE(bl);
  NAG_FREE(bu);
  NAG_FREE(g);

  return exit_status;
}

static void NAG_CALL funct(Integer n, const double xc[], double *fc,
                           double gc[], Nag_Comm *comm)
{
  /* Function to evaluate objective function and its 1st derivatives. */
  double term1, term1_sq;
  double term2, term2_sq;
  double term3, term3_sq, term3_cu;
  double term4, term4_sq, term4_cu;

  if (comm->user[0] == -1.0)
    {
      printf("(User-supplied callback funct, first invocation.)\n");
      fflush(stdout);
      comm->user[0] = 0.0;
    }
  term1 = xc[0] + 10.0*xc[1];
  term1_sq = term1*term1;

  term2 = xc[2] - xc[3];
  term2_sq = term2*term2;

  term3 = xc[1] - 2.0*xc[2];
  term3_sq = term3*term3;
  term3_cu = term3*term3_sq;

  term4 = xc[0] - xc[3];
  term4_sq = term4*term4;
  term4_cu = term4_sq*term4;

  *fc = term1_sq + 5.0*term2_sq
        + term3_sq*term3_sq + 10.0*term4_sq*term4_sq;

  gc[0] = 2.0*term1 + 40.0*term4_cu;
  gc[1] = 20.0*term1 +  4.0*term3_cu;
  gc[2] = 10.0*term2 -  8.0*term3_cu;
  gc[3] = -10.0*term2 - 40.0*term4_cu;
}
/* funct */

static void NAG_CALL h(Integer n, const double xc[], double fhesl[],
                       double fhesd[], Nag_Comm *comm)
{
  /* Routine to evaluate 2nd derivatives */
  double term3_sq;
  double term4_sq;

  if (comm->user[1] == -1.0)
    {
      printf("(User-supplied callback h, first invocation.)\n");
      fflush(stdout);
      comm->user[1] = 0.0;
    }
  term3_sq = (xc[1] - 2.0*xc[2])*(xc[1] - 2.0*xc[2]);
  term4_sq = (xc[0] - xc[3])*(xc[0] - xc[3]);

  fhesd[0] = 2.0 + 120.0*term4_sq;
  fhesd[1] = 200.0 + 12.0*term3_sq;
  fhesd[2] = 10.0 + 48.0*term3_sq;
  fhesd[3] = 10.0 + 120.0*term4_sq;

  fhesl[0] = 20.0;
  fhesl[1] = 0.0;
  fhesl[2] = -24.0*term3_sq;
  fhesl[3] = -120.0*term4_sq;
  fhesl[4] = 0.0;
  fhesl[5] = -10.0;
}
/* h */