/* nag_estimate_garchGJR (g13fec) Example Program.
 *
 * Copyright 2014 Numerical Algorithms Group.
 *
 * NAG C Library
 *
 * Mark 6, 2000.
 *
 */
#include <nag.h>
#include <nag_stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <nagg05.h>
#include <nagg13.h>

#define X(I, J) x[(I) *tdx + (J)]

int main(void)
{
  /* Integer scalar and array declarations */
  Integer                    exit_status = 0;
  Integer                    i, j, k, npar, tdc, tdx, lr, lstate;
  Integer                    *state = 0;

  /* NAG structures and data types */
  NagError                   fail;
  Nag_Boolean                fcall;

  /* Double scalar and array declarations */
  double                     fac1, hp, lgf, xterm;
  double                     *covar = 0, *cvar = 0, *etm = 0, *ht = 0;
  double                     *htm = 0, *r = 0, *sc = 0, *se = 0, *theta = 0;
  double                     *x = 0, *yt = 0;

  /* Choose the base generator */
  Nag_BaseRNG                genid = Nag_Basic;
  Integer                    subid = 0;

  /* Set the seed */
  Integer                    seed[] = { 1762543 };
  Integer                    lseed = 1;

  /* Set parameters for the (randomly generated) time series ... */
  /* Generate data assuming normally distributed errors */
  Nag_ErrorDistn             dist = Nag_NormalDistn;
  double                     df = 0;

  /* Size of the time series */
  Integer                    num = 1000;

  /* MA and AR parameters */
  Integer                    ip = 1;
  Integer                    iq = 1;
  double                     param[] = { 0.4, 0.1, 0.7 };

  /* Asymmetry parameter */
  double                     gamma = 0.1;

  /* Regression parameters */
  Integer                    nreg = 2;
  double                     mean = 4.0;
  double                     bx[] = { 1.5, 2.5 };
  /* ... end of parameters for (randomly generated) time series */

  /* When fitting a model to the time series ... */
  /* Include mean in the model */
  Integer                    mn = 1;

  /* Use the following maaximum number of iterations and tolerance */
  Integer                    maxit = 50;
  double                     tol = 1e-12;

  /* Enforce stationary conditions */
  Nag_Garch_Stationary_Type  stat_opt = Nag_Garch_Stationary_True;

  /* Estimate initial values for regression parameters */
  Nag_Garch_Est_Initial_Type est_opt = Nag_Garch_Est_Initial_True;

  /* Set the number of values to forecast from the fitted model */
  Integer                    nt = 6;
  /* ... end of model fitting options */


  /* Initialise the error structure */
  INIT_FAIL(fail);

  printf("nag_estimate_garchGJR (g13fec) Example Program Results \n\n");

  /* Get the length of the state array */
  lstate = -1;
  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;
    }

  /* Derive various amounts */
  npar = iq + ip + 1;
  tdx = nreg;
  tdc = npar + mn + nreg + 1;

  /* Calculate the size of the reference vector */
  lr = 2 * (iq + ip + 2);

  if (!(covar = NAG_ALLOC((npar + mn + nreg + 1) * tdc, double))
     || !(etm = NAG_ALLOC(num, double))
     || !(ht = NAG_ALLOC(num, double))
     || !(htm = NAG_ALLOC(num, double))
     || !(r = NAG_ALLOC(lr, double))
     || !(state = NAG_ALLOC(lstate, Integer))
     || !(sc = NAG_ALLOC(npar + mn + nreg + 1, double))
     || !(se = NAG_ALLOC(npar + mn + nreg + 1, double))
     || !(theta = NAG_ALLOC(npar + mn + nreg + 1, double))
     || !(cvar = NAG_ALLOC(nt, double))
     || !(x = NAG_ALLOC(num * tdx, double))
     || !(yt = NAG_ALLOC(num, double)))
    {
      printf("Allocation failure\n");
      exit_status = -1;
      goto END;
    }

  /* Initialise the generator to a repeatable sequence */
  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;
    }

  /* Set up the time dependent exogenous matrix x */
  for (i = 0; i < num; ++i)
    {
      fac1 = (double)(i + 1) *0.01;
      X(i, 1) = sin(fac1) * 0.7 + 0.01;
      X(i, 0) = fac1 * 0.1 + 0.5;
    }

  /* Generate a realization of a random GARCH GJR time series and discard it */
  fcall = Nag_TRUE;
  nag_rand_garchGJR(dist, num, ip, iq, param, gamma, df, ht, yt, fcall, r, lr,
                    state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_rand_garchGJR (g05pfc).\n%s\n",
              fail.message);
      exit_status = 1;
      goto END;
    }

  /* Generate a realization of a random GARCH GJR time series to use */
  fcall = Nag_FALSE;
  nag_rand_garchGJR(dist, num, ip, iq, param, gamma, df, ht, yt, fcall, r, lr,
                    state, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_rand_garchGJR (g05pfc).\n%s\n",
              fail.message);
      exit_status = 1;
      goto END;
    }

  /* Adjust the randomly generated time series to take into account for the
     exogenous matrix x */
  for (i = 0; i < num; ++i)
    {
      xterm = 0.0;
      for (k = 0; k < nreg; ++k)
        xterm += X(i, k) * bx[k];

      if (mn == 1)
        yt[i] = mean + xterm + yt[i];
      else
        yt[i] = xterm + yt[i];
    }

  /* Set initial estimates for the parameters */
  for (i = 0; i < npar; ++i)
    theta[i] = param[i] * 0.5;
  theta[npar] = gamma * 0.5;
  if (mn == 1)
    theta[npar + 1] = mean * 0.5;
  for (i = 0; i < nreg; ++i)
    theta[npar + 1 + mn + i] = bx[i] * 0.5;

  /* nag_estimate_garchGJR (g13fec).
   * Univariate time series, parameter estimation for an
   * asymmetric Glosten, Jagannathan and Runkle (GJR) GARCH
   * process
   */
  nag_estimate_garchGJR(yt, x, tdx, num, ip, iq, nreg, mn,
                        theta, se, sc, covar, tdc, &hp,
                        etm, htm, &lgf, stat_opt, est_opt, maxit,
                        tol, &fail);
  if (fail.code != NE_NOERROR)
    {
      printf("Error from nag_estimate_garchGJR (g13fec).\n%s\n",
              fail.message);
      exit_status = 1;
      goto END;
    }

  /* Display the results */
  printf("       Parameter estimates     Standard errors       "
          "Correct values\n");
  for (j = 0; j < npar; ++j)
    printf("%20.4f             (%6.4f) %20.4f\n", theta[j], se[j],
            param[j]);
  printf("%20.4f             (%6.4f) %20.4f\n", theta[npar], se[npar],
          gamma);
  if (mn)
    printf("%20.4f             (%6.4f) %20.4f\n", theta[npar + 1],
            se[npar + 1], mean);
  for (j = 0; j < nreg; ++j)
    printf("%20.4f             (%6.4f) %20.4f\n",
            theta[npar + 1 + mn + j], se[npar + 1 + mn + j], bx[j]);

  /* Now forecast nt steps ahead */
  gamma = theta[npar];

  /* nag_forecast_garchGJR (g13ffc).
   * Univariate time series, forecast function for an
   * asymmetric Glosten, Jagannathan and Runkle (GJR) GARCH
   * process
   */
  nag_forecast_garchGJR(num, nt, ip, iq, theta, gamma, cvar, htm, etm, &fail);
  printf("\n%ld step forecast = %8.4f\n", nt, cvar[nt-1]);

 END:
  NAG_FREE(covar);
  NAG_FREE(etm);
  NAG_FREE(ht);
  NAG_FREE(htm);
  NAG_FREE(sc);
  NAG_FREE(se);
  NAG_FREE(theta);
  NAG_FREE(cvar);
  NAG_FREE(x);
  NAG_FREE(yt);
  NAG_FREE(r);
  NAG_FREE(state);

  return exit_status;
}