/* nag_1d_cheb_fit_constr (e02agc) Example Program.
 *
 * NAGPRODCODE Version.
 *
 * Copyright 2016 Numerical Algorithms Group.
 *
 * Mark 26, 2016.
 */

#include <stdio.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nage02.h>

int main(void)
{
  /* Scalars */
  double fiti, xmax, xmin;
  Integer exit_status, i, iy, j, k, h, m, mf, n, pda, stride;
  NagError fail;
  Nag_OrderType order;

  /* Arrays */
  double *a = 0, *s = 0, *w = 0, *resid = 0, *x = 0, *xf = 0, *y = 0, *yf = 0;
  Integer *p = 0;

#ifdef NAG_COLUMN_MAJOR
#define A(I, J) a[(J-1)*pda + I - 1]
  order = Nag_ColMajor;
#else
#define A(I, J) a[(I-1)*pda + J - 1]
  order = Nag_RowMajor;
#endif

  INIT_FAIL(fail);

  exit_status = 0;
  printf("nag_1d_cheb_fit_constr (e02agc) Example Program Results\n");

  /* Skip heading in data file */
  scanf("%*[^\n] ");
  while (scanf("%" NAG_IFMT "%*[^\n] ", &mf) != EOF)
  {
    if (mf > 0) {
      /* Allocate memory for p and xf. */
      if (!(p = NAG_ALLOC(mf, Integer)) || !(xf = NAG_ALLOC(mf, double)))
      {
        printf("Allocation failure\n");
        exit_status = -1;
        goto END;
      }

      /* Read p, xf and yf arrays */
      iy = 1;
      n = mf;
      for (i = 0; i < mf; ++i) {
        scanf("%" NAG_IFMT "%lf", &p[i], &xf[i]);
        h = iy + p[i] + 1;
        /* We need to extend array yf as we go along */
        if (!(yf = NAG_REALLOC(yf, h - 1, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
        for (j = iy - 1; j < h - 1; ++j)
          scanf("%lf", &yf[j]);
        scanf("%*[^\n] ");
        n += p[i];
        iy = h;
      }
      scanf("%" NAG_IFMT "%*[^\n] ", &m);

      if (m > 0) {
        /* Allocate memory for x, y and w. */
        if (!(x = NAG_ALLOC(m, double)) ||
            !(y = NAG_ALLOC(m, double)) || !(w = NAG_ALLOC(m, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
        for (i = 0; i < m; ++i)
          scanf("%lf%lf%lf", &x[i], &y[i], &w[i]);
        scanf("%*[^\n] ");
        scanf("%" NAG_IFMT "%lf%lf%*[^\n] ", &k, &xmin, &xmax);
        pda = k + 1;

        /* Allocate arrays a, s and resid */
        if (!(a = NAG_ALLOC((k + 1) * (k + 1), double)) ||
            !(s = NAG_ALLOC((k + 1), double)) ||
            !(resid = NAG_ALLOC(m, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }

        /* nag_1d_cheb_fit_constr (e02agc).
         * Least squares polynomial fit, values and derivatives may
         * be constrained, arbitrary data points
         */
        nag_1d_cheb_fit_constr(order, m, k, xmin, xmax, x, y, w, mf, xf,
                               yf, p, a, s, &n, resid, &fail);
        if (fail.code != NE_NOERROR) {
          printf("Error from nag_1d_cheb_fit_constr (e02agc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }

        printf("\n");
        printf("Degree  RMS residual\n");
        for (i = n; i <= k; ++i)
          printf("%4" NAG_IFMT "%15.2e\n", i, s[i]);
        printf("\n");

        printf("Details of the fit of degree %2" NAG_IFMT "\n", k);
        printf("\n");
        printf("  Index   Coefficient\n");
        for (i = 0; i < k + 1; ++i)
          printf("%6" NAG_IFMT "%11.4f\n", i, A(k + 1, i + 1));
        printf("\n");

        printf("     i      x(i)       y(i)       Fit     Residual\n");
        for (i = 0; i < m; ++i) {
          /* Note that the coefficients of polynomial are stored in the
           * rows of A hence when the storage is in Nag_ColMajor order
           * then stride is the first dimension of A, k + 1.
           * When the storage is in Nag_RowMajor order then stride is 1.
           */
#ifdef NAG_COLUMN_MAJOR
          stride = k + 1;
#else
          stride = 1;
#endif
          /* nag_1d_cheb_eval2 (e02akc).
           * Evaluation of fitted polynomial in one variable from
           * Chebyshev series form
           */
          nag_1d_cheb_eval2(k, xmin, xmax, &A(k + 1, 1), stride, x[i],
                            &fiti, &fail);
          if (fail.code != NE_NOERROR) {
            printf("Error from nag_1d_cheb_eval2 (e02akc).\n%s\n",
                   fail.message);
            exit_status = 1;
            goto END;
          }
          printf("%6" NAG_IFMT "%11.4f%11.4f%11.4f", i, x[i], y[i], fiti);
          printf("%11.2e\n", fiti - y[i]);
        }
      }
    }
  }

END:
  NAG_FREE(a);
  NAG_FREE(s);
  NAG_FREE(w);
  NAG_FREE(resid);
  NAG_FREE(x);
  NAG_FREE(xf);
  NAG_FREE(y);
  NAG_FREE(yf);
  NAG_FREE(p);

  return exit_status;
}