/* nag_ode_ivp_rkts_errass (d02puc) Example Program.
 *
 * Copyright 2017 Numerical Algorithms Group.
 *
 * Mark 26.1, 2017.
 */
#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 4

int main(void)
{
  /* Scalars */
  Integer exit_status = 0;
  Integer liwsav, lrwsav, lwcomm, n;
  double errmax, hnext, hstart, tend, terrmx, tgot, tol, tstart, twant, waste;
  Integer fevals, j, k, stepcost, stepsok;
  /* Arrays */
  static double ruser[1] = { -1.0 };
  double *rmserr = 0, *rwsav = 0, *thresh = 0, *wcomm = 0;
  double *ygot = 0, *yinit = 0, *ymax = 0, *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;
  lwcomm = 6 * n;

  printf("nag_ode_ivp_rkts_errass (d02puc) Example Program Results\n\n");

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

  if (!(rmserr = NAG_ALLOC(n, double)) ||
      !(thresh = NAG_ALLOC(n, double)) ||
      !(ygot = NAG_ALLOC(n, double)) ||
      !(yinit = NAG_ALLOC(n, double)) ||
      !(ymax = NAG_ALLOC(n, double)) ||
      !(ypgot = NAG_ALLOC(n, double)) ||
      !(wcomm = NAG_ALLOC(lwcomm, double)) ||
      !(rwsav = NAG_ALLOC(lrwsav, double)) ||
      !(iwsav = NAG_ALLOC(liwsav, Integer))
         )
  {
    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%lf%*[^\n] ", &hstart, &tol);
  for (j = 0; j < n; j++)
    scanf("%lf", &thresh[j]);
  scanf("%*[^\n] ");

  /* 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        y1'\n");
  printf("%6.3f", tstart);
  for (k = 0; k < n; k++)
    printf("   %8.4f", yinit[k]);
  printf("\n");

  twant = tend;
  tgot = tstart;
  while (tgot < twant) {
    /* 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("   %8.4f", ygot[k]);
    printf("\n");

  }

  /* Compute and print error estimates using 
   * nag_ode_ivp_rkts_errass (d02puc).
   */
  nag_ode_ivp_rkts_errass(n, rmserr, &errmax, &terrmx, iwsav, rwsav, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_ode_ivp_rkts_errass (d02puc).\n%s\n",
           fail.message);
    exit_status = 3;
    goto END;
  }

  printf("\n Componentwise error assessment\n");
  printf("       ");
  for (j = 0; j < n; j++)
    printf("%11.2e", rmserr[j]);

  printf("\n\n Worst global error observed was %9.2e", errmax);
  printf(" - occuring at t = %6.3f\n\n", terrmx);

  /* 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 = 4;
    goto END;
  }
  printf(" Cost of the integration in evaluations of f is %6" NAG_IFMT "\n\n",
         fevals);

END:
  NAG_FREE(rmserr);
  NAG_FREE(thresh);
  NAG_FREE(ygot);
  NAG_FREE(yinit);
  NAG_FREE(ymax);
  NAG_FREE(ypgot);
  NAG_FREE(rwsav);
  NAG_FREE(iwsav);
  NAG_FREE(wcomm);
  return exit_status;
}

static void NAG_CALL f(double t, Integer n, const double *y, double *yp,
                       Nag_Comm *comm)
{
  double r;

  if (comm->user[0] == -1.0) {
    printf("(User-supplied callback f, first invocation.)\n");
    comm->user[0] = 0.0;
  }
  r = sqrt(y[0] * y[0] + y[1] * y[1]);
  r = r * r * r;

  yp[0] = y[2];
  yp[1] = y[3];
  yp[2] = -y[0] / r;
  yp[3] = -y[1] / r;
}