/* nag_regsn_quant_linear (g02qgc) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * Mark 23, 2011.
 */
/* Pre-processor includes */
#include <stdio.h>
#include <string.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nagg02.h>
#include <nagg05.h>
#include <nagx04.h>

#define DAT(i,j)    dat[(order==Nag_RowMajor) ? (i*pddat+j) : (j*pddat+i)]
#define CH(i, j, k) ch[k*ip*ip + j*ip + i]

#define LOPTSTR 80

int main(void)
{
  /* Integer scalar and array declarations */
  Integer              lseed = 1, liopts = 100, lopts = 100, lcvalue = LOPTSTR;
  Integer              exit_status = 0;
  Integer              genid, i, ip, ivalue, j, l, lc, lstate, loptstr,
                       m, n, ntau, subid, tdch, pddat;
  Integer              *info = 0, *iopts = 0, *isx = 0, *state = 0;
  Integer              seed[1];

  /* NAG structures */
  NagError             fail;
  Nag_OrderType        order;
  Nag_IncludeIntercept intcpt;
  Nag_Boolean          weighted;
  Nag_VariableType     optype;

  /* Double scalar and array declarations */
  double               df, rvalue;
  double               *b = 0, *bl = 0, *bu = 0, *ch = 0, *dat = 0,
                       *opts = 0, *res = 0, *tau = 0, *wt = 0, *y = 0;

  /* Character scalar and array declarations */
  char                 semeth[30], *poptstr, *cvalue = 0;
  char                 optstr[LOPTSTR], corder[40], cintcpt[40],
                       cweighted[40], cgenid[40];
  char                 *clabs = 0, **clabsc = 0;

  /* Initialise the error structure to print out any error messages */
  INIT_FAIL(fail);

  printf("nag_regsn_quant_linear (g02qgc) Example Program Results\n\n");
  fflush(stdout);

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

  /* Read in the problem size */
  scanf("%39s%*[^\n]", corder);
  scanf("%39s%39s%*[^\n]", cintcpt, cweighted);
  scanf("%ld%ld%ld%*[^\n]", &n, &m, &ntau);
  order = (Nag_OrderType) nag_enum_name_to_value(corder);
  intcpt = (Nag_IncludeIntercept) nag_enum_name_to_value(cintcpt);
  /* weighted is a Nag_Boolean flag used in this example program to indicate
   * whether weights are being supplied (weighted=Nag_TRUE)
   * or not (weighted=Nag_FALSE)
   */
  weighted = (Nag_Boolean) nag_enum_name_to_value(cweighted);

  pddat = (order == Nag_RowMajor) ? m : n;

  /* Allocate memory for input arrays */
  if (!(y = NAG_ALLOC(n, double)) ||
      !(tau = NAG_ALLOC(ntau, double)) ||
      !(isx = NAG_ALLOC(m, Integer)) ||
      !(dat = NAG_ALLOC(m*n, double)) ||
      !(cvalue = NAG_ALLOC(lcvalue, char)) ||
      !(clabs = NAG_ALLOC(10*10, char)) ||
      !(clabsc = NAG_ALLOC(10, char *))
      )
    {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

  if (weighted)
    {
      /* Data includes a weight */
      if (!(wt = NAG_ALLOC(n, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }
      for (i = 0; i < n; i++)
        {
          for (j = 0; j < m; j++) scanf("%lf", &DAT(i, j));
          scanf("%lf%lf", &y[i], &wt[i]);
        }
      scanf("%*[^\n] ");
    }
  else
    {
      /* No weights supplied */
      for (i = 0; i < n; i++)
        {
          for (j = 0; j < m; j++) scanf("%lf", &DAT(i, j));
          scanf("%lf", &y[i]);
        }
      scanf("%*[^\n] ");
    }

  /* Read in variable inclusion flags and calculate IP */
  ip = (intcpt == Nag_Intercept) ? 1 : 0;
  for (j = 0; j < m; j++)
    {
      scanf("%" NAG_IFMT, &isx[j]);
      if (isx[j] == 1) ip++;
    }
  scanf("%*[^\n] ");

  /* Read in the quantiles required */
  for (l = 0; l < ntau; l++) scanf("%lf", &tau[l]);
  scanf("%*[^\n] ");

  /* Allocate memory for option arrays */
  if (!(opts = NAG_ALLOC(lopts, double)) ||
      !(iopts = NAG_ALLOC(liopts, Integer)))
    {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

  /* Initialize the optional argument array with nag_g02_opt_set (g02zkc) */
  nag_g02_opt_set("INITIALIZE = G02QG", iopts, liopts, opts, lopts, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_g02_opt_set (g02zkc).\n%s\n",
             fail.message);
      exit_status = 1;
      goto END;
    }

  /* Read in any optional arguments. Reads in to the end of
     the input data, or until a blank line is reached */
  for (;;)
    {
      if (!fgets(optstr, LOPTSTR, stdin)) break;

      /* Left justify the option */
      poptstr = (optstr+strspn(optstr, " \n\t"));
      /* Get the string length */
      loptstr = strlen(poptstr);
      if (poptstr[loptstr-1] == '\n')
        {
          /* Remove any trailing line breaks */
          poptstr[(--loptstr)] = '\0';
        }
      else
        {
          /* Clear the rest of the line */
          scanf("%*[^\n] ");
        }

      /* Break if read in a blank line */
      if (!*(poptstr)) break;

      /* Set the supplied option (g02zkc) */
      nag_g02_opt_set(optstr, iopts, liopts, opts, lopts, &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_g02_opt_set (g02zkc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }
    }

  /* Allocate memory for the output arrays */
  if (!(b = NAG_ALLOC(ip*ntau, double)) ||
      !(info = NAG_ALLOC(ntau, Integer)))
    {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

  /* Query optional arguments via nag_g02_opt_get (g02zlc) and calculate which
   * of the optional arrays are required and their sizes
   * ...
   */
  nag_g02_opt_get("INTERVAL METHOD", &ivalue, &rvalue, cvalue, lcvalue,
                  &optype, iopts, opts, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_g02_opt_get (g02zlc).\n%s\n",
             fail.message);
      exit_status = 1;
      goto END;
    }
  strcpy(semeth, cvalue);
  if (strcmp(semeth, "NONE") != 0)
    {
      /* Require the intervals to be output */
      if (!(bl = NAG_ALLOC(ip*ntau, double)) ||
          !(bu = NAG_ALLOC(ip*ntau, double)))
        {
          printf("Allocation failure\n");
          exit_status = -1;
          goto END;
        }

      /* Decide whether the state array is required, and initialise if it is */
      if (strcmp(semeth, "BOOTSTRAP XY") == 0)
        {
          /* Read in the generator ID and a seed */
          scanf("%39s%ld%ld%*[^\n] ", cgenid, &subid, &seed[0]);
          genid = (Nag_BaseRNG) nag_enum_name_to_value(cgenid);

          /* Query the length of the state array (g05kfc) */
          lstate = 0;
          nag_rand_init_repeatable(genid, subid, seed, lseed, state, &lstate,
                                   &fail);
          if (fail.code != NE_NOERROR)
            {
              printf("Error from nag_rand_init_repeatable (g05kfc).\n%s\n",
                     fail.message);
              exit_status = 1;
              goto END;
            }

          /* Allocate memory to state */
          if (!(state = NAG_ALLOC(lstate, Integer)))
            {
              printf("Allocation failure\n");
              exit_status = -1;
              goto END;
            }

          /* Initialise the RNG (g05kfc) */
          nag_rand_init_repeatable(genid, subid, seed, lseed, state, &lstate,
                                   &fail);
          if (fail.code != NE_NOERROR)
            {
              printf("Error from nag_rand_init_repeatable (g05kfc).\n%s\n",
                     fail.message);
              exit_status = 1;
              goto END;
            }
        }

      /* Calculate the size of the covariance matrix, ch. */
      tdch = 0;
      nag_g02_opt_get("MATRIX RETURNED", &ivalue, &rvalue, cvalue, lcvalue,
                      &optype, iopts, opts, &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_g02_opt_get (g02zlc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }

      if (strcmp(cvalue, "COVARIANCE") == 0)
        {
          tdch = ntau;
        }
      else if (strcmp(cvalue, "H INVERSE") == 0)
        {
          /* NB: If we are using bootstrap or IID errors then any request for
             H INVERSE is ignored */
          if (strcmp(semeth,
                     "BOOTSTRAP XY") != 0 && strcmp(semeth, "IID") != 0)
            tdch = ntau + 1;
        }
      if (tdch > 0)
        {
          /* Need to allocate ch */
          if (!(ch = NAG_ALLOC(ip*ip*tdch, double)))
            {
              printf("Allocation failure\n");
              exit_status = -1;
              goto END;
            }
        }

      /* Calculate the size of the residual array, res */
      nag_g02_opt_get("RETURN RESIDUALS", &ivalue, &rvalue, cvalue, lcvalue,
                      &optype, iopts, opts, &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_g02_opt_get (g02zlc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }

      if (strcmp(cvalue, "YES") == 0)
        {
          /* Need to allocate res */
          if (!(res = NAG_ALLOC(n*ntau, double)))
            {
              printf("Allocation failure\n");
              exit_status = -1;
              goto END;
            }
        }
    }
  /* ...
   * end of handling the optional arguments, and allocating optional arrays
   */

  /* Call the model fitting routine (nag_regsn_quant_linear (g02qgc)) */
  nag_regsn_quant_linear(order, intcpt, n, m, dat, pddat, isx, ip, y, wt, ntau,
                         tau, &df, b, bl, bu, ch, res, iopts, opts, state,
                         info, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_regsn_quant_linear (g02qgc).\n%s\n", fail.message);
      if (fail.code == NW_POTENTIAL_PROBLEM)
        {
          printf("Additional error information: ");
          for (i = 0; i < ntau; i++)
            printf("%ld ", info[i]);
          printf("\n");
        }
      else
        {
          printf("Error from nag_regsn_quant_linear (g02qgc).\n%s\n",
                 fail.message);
          exit_status = -1;
          goto END;
        }
    }

  /* Display the parameter estimates */
  for (l = 0; l < ntau; l++)
    {
      printf(" Quantile: %6.3f\n\n", tau[l]);
      if (bl && bu)
        {
          printf("         Lower   Parameter   Upper\n");
          printf("         Limit   Estimate    Limit\n");
          for (j = 0; j < ip; j++)
            printf(" %3ld%10.3f%10.3f%10.3f\n", j+1, bl[l*ip+j],
                   b[l*ip+j], bu[l*ip+j]);
        }
      else
        {
          printf("       Parameter\n");
          printf("       Estimate\n");
          for (j = 0; j < ip; j++)
            printf(" %3ld%10.3f\n", j+1, b[l*ip+j]);
        }
      printf("\n\n");
      fflush(stdout);
      if (ch)
        {
          lc = l*ip*ip;
          if (tdch == ntau)
            {
              /* nag_gen_real_mat_print_comp (x04cbc).
               * Print real general matrix (comprehensive).
               */
              nag_gen_real_mat_print_comp(Nag_ColMajor, Nag_UpperMatrix,
                                          Nag_NonUnitDiag, ip, ip, &ch[lc], ip,
                                          "%9.2e", "Covariance matrix",
                                          Nag_NoLabels, 0, Nag_NoLabels, 0, 80,
                                          0, 0, &fail);
            }
          else
            {
              if (l == 0)
                {
                  nag_gen_real_mat_print_comp(Nag_ColMajor, Nag_UpperMatrix,
                                              Nag_NonUnitDiag, ip, ip, ch, ip,
                                              "%9.2e", "J", Nag_NoLabels, 0,
                                              Nag_NoLabels, 0, 80, 0, 0, &fail);
                  printf("\n");
                }
              lc = lc + ip*ip;
              nag_gen_real_mat_print_comp(Nag_ColMajor, Nag_UpperMatrix,
                                          Nag_NonUnitDiag, ip, ip, &ch[lc], ip,
                                          "%9.2e", "H inverse",
                                          Nag_NoLabels, 0, Nag_NoLabels, 0, 80,
                                          0, 0, &fail);
            }
          if (fail.code != NE_NOERROR)
            {
              printf("Error from nag_gen_real_mat_print_comp (x04cbc).\n%s\n",
                     fail.message);
              exit_status = 1;
              goto END;
            }
          printf("\n");
        }
    }

  if (res)
    {
      printf(" First 10 Residuals\n");
      fflush(stdout);
      /* set up column labels for matrix printer */
      for (l = 0; l < ntau; l++) sprintf(&clabs[10*l], "%6.3f", tau[l]);
      for (l = 0; l < ntau; l++) clabsc[l] = &clabs[l*10];
      /* nag_gen_real_mat_print_comp (x04cbc).
       * Print real general matrix (comprehensive).
       */
      nag_gen_real_mat_print_comp(Nag_ColMajor, Nag_GeneralMatrix,
                                  Nag_NonUnitDiag, MIN(10, n), ntau, res, n,
                                  "%10.5f", "                        Quantile",
                                  Nag_IntegerLabels, NULL, Nag_CharacterLabels,
                                  (const char **) clabsc, 80, 2, NULL, &fail);
      if (fail.code != NE_NOERROR)
        {
          printf("Error from nag_gen_real_mat_print_comp (x04cbc).\n%s\n",
                 fail.message);
          exit_status = 1;
          goto END;
        }
    }
  else
    {
      printf(" Residuals not returned\n");
    }

 END:

  NAG_FREE(info);
  NAG_FREE(iopts);
  NAG_FREE(isx);
  NAG_FREE(state);
  NAG_FREE(b);
  NAG_FREE(bl);
  NAG_FREE(bu);
  NAG_FREE(ch);
  NAG_FREE(dat);
  NAG_FREE(opts);
  NAG_FREE(res);
  NAG_FREE(tau);
  NAG_FREE(wt);
  NAG_FREE(y);
  NAG_FREE(cvalue);
  NAG_FREE(clabs);
  NAG_FREE(clabsc);

  return(exit_status);
}