/* nag_ode_bvp_ps_lin_solve (d02uec) Example Program.
 *
 * Copyright 2017 Numerical Algorithms Group.
 *
 * Mark 26.1, 2017.
 */
#include <math.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nagd02.h>
#include <nagx01.h>
#include <nagx02.h>

#ifdef __cplusplus
extern "C"
{
#endif
  static double NAG_CALL exact(double x, Integer q);
  static void NAG_CALL bndary(Integer m, double a, double b, double y[],
                              double bmat[], double bvec[]);
  static void NAG_CALL pdedef(Integer m, double f[]);
#ifdef __cplusplus
}
#endif

int main(void)
{
  /* Scalars */
  Integer exit_status = 0;
  double a = -nag_pi / 2.0, b = nag_pi / 2.0, resid;
  Integer i, j, n, m = 3;
  double teneps = 10.0 * nag_machine_precision;
  /* Arrays */
  double *bmat = 0, *bvec = 0, *f = 0, *uerr = 0, *y = 0, *c = 0, *f0 = 0,
         *u = 0, *uc = 0, *x = 0;
  /* NAG types */
  Nag_Boolean reqerr = Nag_FALSE;
  NagError fail;

  INIT_FAIL(fail);

  printf("nag_ode_bvp_ps_lin_solve (d02uec) Example Program Results\n\n");

  /* Skip heading in data file */
  scanf("%*[^\n] ");
  scanf("%" NAG_IFMT "%*[^\n] ", &n);
  if (!(u = NAG_ALLOC((n + 1) * (m + 1), double)) ||
      !(f0 = NAG_ALLOC((n + 1), double)) ||
      !(c = NAG_ALLOC((n + 1), double)) ||
      !(uc = NAG_ALLOC((n + 1) * (m + 1), double)) ||
      !(x = NAG_ALLOC((n + 1), double)) ||
      !(bmat = NAG_ALLOC(m * (m + 1), double)) ||
      !(bvec = NAG_ALLOC(m, double)) ||
      !(f = NAG_ALLOC((m + 1), double)) ||
      !(uerr = NAG_ALLOC((m + 1), double)) || !(y = NAG_ALLOC(m, double))
         )
  {
    printf("Allocation failure\n");
    exit_status = -1;
    goto END;
  }

  /* Set up domain, boundary conditions and definition. */
  bndary(m, a, b, y, bmat, bvec);
  pdedef(m, f);

  /* nag_ode_bvp_ps_lin_cgl_grid (d02ucc).
   * Generate Chebyshev Gauss-Lobatto solution grid.
   */
  nag_ode_bvp_ps_lin_cgl_grid(n, a, b, x, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_ode_bvp_ps_lin_cgl_grid (d02ucc).\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }

  /* Set up problem right-hand sides for grid and transform. */
  for (i = 0; i < n + 1; i++)
    f0[i] = 2.0 * sin(x[i]) - 2.0 * cos(x[i]);

  /* nag_ode_bvp_ps_lin_coeffs (d02uac).
   * Coefficients of Chebyshev interpolating polynomial from function values f0
   * on Chebyshev grid.
   */
  nag_ode_bvp_ps_lin_coeffs(n, f0, c, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_ode_bvp_ps_lin_coeffs (d02uac).\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }

  /* nag_ode_bvp_ps_lin_solve (d02uec).
   * Solve given boundary value problem on Chebyshev grid, in coefficient space
   * using an integral formulation of the pseudospectral method.
   */
  nag_ode_bvp_ps_lin_solve(n, a, b, m, c, bmat, y, bvec, f, uc, &resid,
                           &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_ode_bvp_ps_lin_solve (d02uec).\n%s\n",
           fail.message);
    exit_status = 1;
    goto END;
  }

  /* nag_ode_bvp_ps_lin_cgl_vals (d02ubc).
   * Obtain function values from coefficients of Chebyshev polynomial.
   * Also obtain first- to third-derivative values.
   */
  for (i = 0; i < m + 1; i++) {
    nag_ode_bvp_ps_lin_cgl_vals(n, a, b, i, &uc[(n + 1) * i], &u[(n + 1) * i],
                                &fail);
    if (fail.code != NE_NOERROR) {
      printf("Error from nag_ode_bvp_ps_lin_cgl_vals (d02ubc).\n%s\n",
             fail.message);
      exit_status = 1;
      goto END;
    }
  }
  /* Print solution. */
  printf("Numerical Solution U and its first three derivatives\n\n");
  printf("%7s%12s%12s%11s%11s\n", "x", "U", "Ux", "Uxx", "Uxxx");
  for (i = 0; i < n + 1; i++)
    printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", x[i], u[i], u[(n + 1) + i],
           u[(n + 1) * 2 + i], u[(n + 1) * 3 + i]);

  if (reqerr) {
    for (i = 0; i < m + 1; i++)
      uerr[i] = 0.0;

    for (i = 0; i < n + 1; i++)
      for (j = 0; j <= m; j++)
        uerr[j] = MAX(uerr[j], fabs(u[(n + 1) * j + i] - exact(x[i], j)));

    for (i = 0; i <= m; i++) {
      printf("Error in the order %1" NAG_IFMT " derivative of U is < %8"
             NAG_IFMT " * machine precision.\n", i,
             10 * ((Integer) (uerr[i] / teneps) + 1));
    }
  }
END:
  NAG_FREE(c);
  NAG_FREE(f0);
  NAG_FREE(u);
  NAG_FREE(uc);
  NAG_FREE(x);
  NAG_FREE(bmat);
  NAG_FREE(bvec);
  NAG_FREE(f);
  NAG_FREE(uerr);
  NAG_FREE(y);
  return exit_status;
}

static double NAG_CALL exact(double x, Integer q)
{
  switch (q) {
  case 0:
    return cos(x);
    break;
  case 1:
    return -sin(x);
    break;
  case 2:
    return -cos(x);
    break;
  case 3:
    return sin(x);
    break;
  }
  return 0.0;
}

static void NAG_CALL bndary(Integer m, double a, double b, double y[],
                            double bmat[], double bvec[])
{
  Integer i;
  /* Boundary condition on left side of domain. */
  for (i = 0; i < 2; i++)
    y[i] = a;

  y[2] = b;
  /* Set up Dirichlet condition using exact solution at x = a. */
  for (i = 0; i < m * (m + 1); i++)
    bmat[i] = 0.0;
  for (i = 0; i < 3; i++)
    bmat[i] = 1.0;
  for (i = 1; i < 3; i++)
    bmat[m + i] = 2.0;
  for (i = 1; i < 3; i++)
    bmat[m * 2 + i] = 3.0;

  bvec[0] = 0.0;
  bvec[1] = 2.0;
  bvec[2] = -2.0;
}

static void NAG_CALL pdedef(Integer m, double f[])
{
  f[0] = 1.0;
  f[1] = 2.0;
  f[2] = 3.0;
  f[3] = 4.0;
}