/* nag_ode_ivp_rkts_range (d02pec) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 24, 2013.
 */
#include <math.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nagd02.h>

#ifdef __cplusplus
extern "C" {
#endif
static void NAG_CALL f(double  t, Integer  n, const double *y,
                       double *yp, Nag_Comm *comm);
#ifdef __cplusplus
}
#endif

#define N 2

int main(void)
{
  /* Scalars */
  double          tol0 = 1.0e-3;
  Integer         npts = 8, exit_status = 0;
  Integer         liwsav, lrwsav, n;
  double          hnext, hstart, tend, tgot, tinc, tol, tstart, twant, waste;
  Integer         fevals, i, j, k, stepcost, stepsok;
  /* Arrays */
  static double ruser[1] = {-1.0};
  double          *rwsav = 0, *thresh = 0, *ygot = 0, *yinit = 0, *ymax = 0;
  double          *ypgot = 0;
  Integer         *iwsav = 0;
  char            nag_enum_arg[40];
  /* NAG types */
  NagError        fail;
  Nag_RK_method   method;
  Nag_ErrorAssess errass;
  Nag_Comm        comm;

  INIT_FAIL(fail);

  n = N;
  liwsav = 130;
  lrwsav = 350 + 32 * n;

  printf("nag_ode_ivp_rkts_range (d02pec) Example Program Results\n\n");

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

  if (
      !(thresh = NAG_ALLOC(n, double)) ||
      !(ygot = NAG_ALLOC(n, double)) ||
      !(yinit = NAG_ALLOC(n, double)) ||
      !(ypgot = NAG_ALLOC(n, double)) ||
      !(ymax = NAG_ALLOC(n, double)) ||
      !(iwsav = NAG_ALLOC(liwsav, Integer)) ||
      !(rwsav = NAG_ALLOC(lrwsav, double))
    )
    {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

  /* Skip heading in data file*/
  scanf("%*[^\n] ");

  /* Set initial conditions for ODE and parameters for the integrator. */
  scanf(" %39s%*[^\n] ", nag_enum_arg);
  /* nag_enum_name_to_value (x04nac) Converts NAG enum member name to value. */
  method = (Nag_RK_method) nag_enum_name_to_value(nag_enum_arg);
  scanf(" %39s%*[^\n] ", nag_enum_arg);
  errass = (Nag_ErrorAssess)  nag_enum_name_to_value(nag_enum_arg);
  scanf("%lf%lf%*[^\n] ", &tstart, &tend);
  for (j = 0; j < n; j++)
    scanf("%lf", &yinit[j]);
  scanf("%*[^\n] ");
  scanf("%lf%*[^\n] ", &hstart);
  for (j = 0; j < n; j++)
    scanf("%lf", &thresh[j]);
  scanf("%*[^\n] ");

  /* Set control for output*/
  tinc = (tend - tstart)/(double) (npts);
  tol = 10.0 * tol0;
  for (i = 1; i <= 2; i++)
    {
      tol = tol * 0.1;
      /* Initialize Runge-Kutta method for integrating ODE using
       * nag_ode_ivp_rkts_setup (d02pqc).
       */
      nag_ode_ivp_rkts_setup(n, tstart, tend, yinit, tol, thresh, method,
                             errass, hstart, iwsav, rwsav, &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_ode_ivp_rkts_setup (d02pqc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }

      printf(" Calculation with tol = %8.1e\n", tol);
      printf("    t         y1        y2\n");
      printf("%6.3f", tstart);
      for (k = 0; k < n; k++)
        printf("   %7.3f", yinit[k]);
      printf("\n");

      twant = tstart;
      for (j = 0; j < npts; j++)
        {
          twant = twant + tinc;
          /* Solve ODE by Runge-Kutta method up to next time increment using
           * nag_ode_ivp_rkts_range (d02pec).
           */
          nag_ode_ivp_rkts_range(f, n, twant, &tgot, ygot, ypgot, ymax, &comm,
                                 iwsav, rwsav, &fail);
          if (fail.code != NE_NOERROR)
            {
              printf("Error from nag_ode_ivp_rkts_range (d02pec).\n%s\n",
                     fail.message);
              exit_status = 2;
              goto END;
            }

          printf("%6.3f", tgot);
          for (k = 0; k < n; k++)
            printf("   %7.3f", ygot[k]);
          printf("\n");
        }
      /* Get diagnostics on whole integration using
       * nag_ode_ivp_rkts_diag (d02ptc).
       */
      nag_ode_ivp_rkts_diag(&fevals, &stepcost, &waste, &stepsok, &hnext,
                            iwsav, rwsav,
                            &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_ode_ivp_rkts_diag (d02ptc).\n%s\n",
                 fail.message);
          exit_status = 3;
          goto END;
        }
      printf("Cost of the integration in evaluations of f is%6ld\n\n",
             fevals);
    }
 END:
  NAG_FREE(thresh);
  NAG_FREE(yinit);
  NAG_FREE(ygot);
  NAG_FREE(ypgot);
  NAG_FREE(ymax);
  NAG_FREE(rwsav);
  NAG_FREE(iwsav);
  return exit_status;
}

static void NAG_CALL f(double t, Integer n, const double *y, double *yp,
                       Nag_Comm *comm)
{
  if (comm->user[0] == -1.0)
    {
      printf("(User-supplied callback f, first invocation.)\n");
      comm->user[0] = 0.0;
    }
  yp[0] = y[1];
  yp[1] = -y[0];
}