/* nag_ode_bvp_ps_lin_solve (d02uec) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 23, 2011.
 */
#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;
}