/*
 * Copyright (C) 1996-2011 Daniel Waggoner and Tao Zha
 *
 * This free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * It is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * If you did not received a copy of the GNU General Public License
 * with this software, see <http://www.gnu.org/licenses/>.
 */

#include "dw_state_space_forecast.h"
#include "dw_MSStateSpace.h"
#include "dw_matrix_array.h"
#include "dw_histogram.h"
#include "dw_error.h"
#include "dw_metropolis_simulation.h"
#include "dw_matrix_rand.h"
#include "dw_parse_cmd.h"
#include "dw_math.h"
#include "dw_std.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
   Assumes:
     forecast  : horizon x ny matrix or null pointer
     horizon   : positive integer - forecast horizon
     z_initial : initial value of state vector z[T].
     shocks    : array of length horizon of fundamental shocks or null pointer.
                   If null pointer, then the shocks are all zero. Each vector is 
                   of length nepsilon.
     S         : array of length horizon.  S[t] is the base regime at time T+1+t.
     model     : pointer to valid TStateModel structure.

   Results:
     Computes forecast

   Returns:
     The matrix forecast upon success and null upon failure.  If forecast is 
     null, then it created.
*/
TMatrix dw_state_space_forecast(TMatrix forecast, int horizon, TVector z_initial, TVector *shocks, int *S, TStateModel *model)
{
  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);
  TVector z1, z2, y;
  int i, t;

  // create forecast if necessary
  if (!forecast && !(forecast=CreateMatrix(horizon,statespace->ny)))
    return (TMatrix)NULL;

  // forecast
  z1=CreateVector(statespace->nz);
  z2=CreateVector(statespace->nz);
  y=CreateVector(statespace->ny);
  EquateVector(z1,z_initial);
  for (t=0; t < horizon; t++)
    {
      ProductMV(z2,statespace->F[S[t]],z1);
      AddVV(z1,statespace->b[S[t]],z2);
      if (shocks)
	{
      	  ProductMV(z2,statespace->Phiz[S[t]],shocks[t]);
      	  AddVV(z1,z1,z2);
      	}
      ProductMV(y,statespace->H[S[t]],z1);
      AddVV(y,statespace->a[S[t]],y);
      for (i=statespace->ny-1; i >= 0; i--)
	ElementM(forecast,t,i)=ElementV(y,i);
    }
  FreeVector(y);
  FreeVector(z2);
  FreeVector(z1);

  return forecast;
}

/*
   For 1 <= k < h, y[k][i] is null if the ith coordinate of y(t0+1+k) is 
   unrestricted and is its value otherwise.  In general, t0 is the last index for
   which we have full information.  It must be the case that t0 <= nobs.
*/
TVector* dw_state_space_mean_conditional_forecast(TVector *F, PRECISION ***y, int h, int t0, TStateModel *model)
{
  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);
  TVector *Pxi, *Pxi1, *SPxi, **IEz, **Ez1, **Ez, **SEz, **ISEz, SPzeta, SPs, u, *z;
  TMatrix **IEzz, **Ezz1, **Ezz;
  int i, k, s;

  if ((t0 > statespace->t0) && !Filter(t0,model)) return (TVector*)NULL;
  if ((t0 > model->t0) && !ForwardRecursion(t0,model)) return (TVector*)NULL;
  if (((t0 < model->sv->t0) || (model->sv->t1 < t0)) && !sv_ComputeTransitionMatrix(t0,model->sv,model)) return (TVector*)NULL;

  Pxi=dw_CreateArray_vector(h);
  Pxi1=dw_CreateArray_vector(h);
  SPxi=dw_CreateArray_vector(h);
  IEz=dw_CreateRectangularArray_vector(h,statespace->zeta_modulus);
  IEzz=dw_CreateRectangularArray_matrix(h,statespace->zeta_modulus);
  Ez1=dw_CreateRectangularArray_vector(h,statespace->nstates);
  Ezz1=dw_CreateRectangularArray_matrix(h,statespace->nstates);
  Ez=dw_CreateRectangularArray_vector(h,statespace->nstates);
  Ezz=dw_CreateRectangularArray_matrix(h,statespace->nstates);
  SEz=dw_CreateRectangularArray_vector(h,statespace->nstates);
  ISEz=dw_CreateRectangularArray_vector(h,statespace->zeta_modulus);
  for (k=h-1; k >= 0; k--)
    {
      Pxi[k]=CreateVector(statespace->nstates);
      Pxi1[k]=CreateVector(statespace->nstates);
      SPxi[k]=CreateVector(statespace->nstates);
      for (i=statespace->zeta_modulus-1; i >= 0; i--)
	{
	  IEz[k][i]=CreateVector(statespace->nz);
	  IEzz[k][i]=CreateMatrix(statespace->nz,statespace->nz);
	  ISEz[k][i]=CreateVector(statespace->nz);
	}
      for (i=statespace->nstates-1; i >= 0; i--)
	{
	  Ez1[k][i]=CreateVector(statespace->nz);
	  Ezz1[k][i]=CreateMatrix(statespace->nz,statespace->nz);
	  Ez[k][i]=CreateVector(statespace->nz);
	  Ezz[k][i]=CreateMatrix(statespace->nz,statespace->nz);
	  SEz[k][i]=CreateVector(statespace->nz);
	}
    }

  ConditionalFilter(0,h-1,y,statespace->Ez[t0],statespace->Ezz[t0],model->V[t0],model->sv->Q,
		    Pxi,Pxi1,IEz,IEzz,Ez1,Ezz1,Ez,Ezz,statespace);

  SmoothProbabilities_MSStateSpace(0,h-1,SPxi,Pxi,Pxi1,model->sv->Q);

  SmoothMean_MSStateSpace(0,h-1,SEz,ISEz,Ez1,Ezz1,IEz,IEzz,SPxi,statespace);

  SPzeta=CreateVector(statespace->zeta_modulus);
  SPs=CreateVector(statespace->nbasestates);
  u=CreateVector(statespace->ny);
  z=dw_CreateArray_vector(statespace->nbasestates);
  for (s=statespace->nbasestates-1; s >= 0; s--) z[s]=CreateVector(statespace->nz);

  if (!F)
    {
      F=dw_CreateArray_vector(h);
      for (k=h-1; k >= 0; k--)
	F[k]=CreateVector(statespace->ny);
    }

  for (k=h-1; k >= 0; k--)
    {
      InitializeVector(F[k],0.0);
      if (statespace->zeta_modulus > statespace->nbasestates)
	{
	  IntegrateStatesSingle(SPzeta,SPxi[k],statespace->zeta_modulus,statespace->nbasestates,2);
	  IntegrateStatesSingleV(z,SPzeta,ISEz[k],statespace->nbasestates,statespace->zeta_modulus/statespace->nbasestates,2);
	  IntegrateStatesSingle(SPs,SPzeta,statespace->nbasestates,statespace->zeta_modulus/statespace->nbasestates,2);
	  for (s=statespace->nbasestates-1; s >= 0; s--)
	    {
	      ProductMV(u,statespace->H[s],z[s]);
	      AddVV(u,statespace->a[s],u);
	      LinearCombinationV(F[k],1.0,F[k],ElementV(SPs,s),u);
	    }
	}
      else
	{
	  IntegrateStatesSingle(SPs,SPxi[k],statespace->nbasestates,statespace->zeta_modulus,2);
	  for (s=statespace->nbasestates-1; s >= 0; s--)
	    {
	      ProductMV(u,statespace->H[s],ISEz[k][s]);
	      AddVV(u,statespace->a[s],u);
	      LinearCombinationV(F[k],1.0,F[k],ElementV(SPs,s),u);
	    }
	}
    }

  // Clean up
  dw_FreeArray(z);
  FreeVector(u);
  FreeVector(SPs);
  FreeVector(SPzeta);
  dw_FreeArray(ISEz);
  dw_FreeArray(SEz);
  dw_FreeArray(Ezz);
  dw_FreeArray(Ez);
  dw_FreeArray(Ezz1);
  dw_FreeArray(Ez1);
  dw_FreeArray(IEzz);
  dw_FreeArray(IEz);
  dw_FreeArray(SPxi);
  dw_FreeArray(Pxi1);
  dw_FreeArray(Pxi);

  return F;
}

/*
 
*/
TVector* dw_state_space_mean_unconditional_forecast(TVector *F, int h, int t0, TStateModel *model)
{
  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);
  PRECISION ***y=(PRECISION***)dw_CreateMultidimensionalArrayList_scalar(3,h,statespace->ny,1);
  int i, j;
  for (i=h-1; i >= 0; i--)
    for (j=statespace->ny-1; j >= 0; j--)
      { dw_FreeArray(y[i][j]); y[i][j]=(PRECISION*)NULL; }
  F=dw_state_space_mean_conditional_forecast(F,y,h,t0,model);
  dw_FreeArray(y);
  return F;
}

/*
   Assumes
    f_out : valid FILE pointer
    percentiles : vector of numbers between 0 and 1 inclusive
    draws : number of draws of shocks and regimes to make for each posterior draw
    posterior_draws : each row contains log posterior, log likelihood, and 
      posterior draw.  If null, current parameters are used.
    thin : thinning factor
    T : last observation to treat as data.  Usually equals model->nobs.
    h : non-negative integer
    model : point to valid TStateModel/T_MSStateSpace structure
    nobs : the number of actual observations to print before the forecast

   Results:
    Computes and prints to the file f_out the requested percentiles for forecasts 
    of the observables.

   Returns:
    One upon success and zero otherwise.

*/
int dw_state_space_forecast_percentile(FILE *f_out, TVector percentiles, int draws, TMatrix posterior_draws, 
				       int thin, int T, int h, TStateModel *model, int nobs)
{
  T_MSStateSpace *statespace;
  int rtrn=0, ntheta, nq, *S, i, j, k, m, n,t;
  TVector x, init_prob, prob, *shocks, z_initial;
  TMatrix forecast, z_scale;
  TMatrixHistogram *histogram;

 // quick check of passed parameters
  if (!f_out || !percentiles || (draws <= 0) || (thin <= 0) || (T < 0) || (h < 0) || !model) return 0;

  statespace=(T_MSStateSpace*)(model->theta);
  ntheta=NumberFreeParametersTheta(model);
  nq=NumberFreeParametersQ(model);

  if (T > model->nobs) return 0;

  // allocate memory
  S=(int*)dw_malloc(h*sizeof(int));
  forecast=CreateMatrix(h,statespace->ny);
  histogram=CreateMatrixHistogram(h,statespace->ny,100,HISTOGRAM_VARIABLE);
  z_initial=CreateVector(statespace->nz);
  shocks=dw_CreateArray_vector(h);
  for (i=h-1; i >= 0; i--) shocks[i]=CreateVector(statespace->nepsilon);
  init_prob=CreateVector(model->sv->nbasestates);
  prob=CreateVector(model->sv->nbasestates);
  z_scale=CreateMatrix(statespace->nz,statespace->nz);

  if (posterior_draws)
    {
      x=CreateVector(ColM(posterior_draws));
      i=RowM(posterior_draws)-1;
    }
  else
    {
      x=(TVector)NULL;
      i=0;
    }
  for (n=i-1000*thin; i >= 0; i-=thin)
    {
      // Get free parameters and push them into model
      if (posterior_draws)
	      {
      	  RowVector(x,posterior_draws,i);
      	  if (ntheta > 0) ConvertFreeParametersToTheta(model,pElementV(x)+2);
      	  if (nq > 0) ConvertFreeParametersToQ(model,pElementV(x)+2+ntheta);

      	  if (i <= n)
      	    {
      	      printf("%d/%d posterior draws processed\n",(RowM(posterior_draws)-i)/thin,RowM(posterior_draws)/thin);
      	      n-=1000*thin;
      	    }
      	}

      // Ensure that the filter has been run through time T
      if ((statespace->t0 < T) && !Filter(T,model))
	      goto ERROR_EXIT;
      
      // Get transition matrix -- this must be modified if transition matrix is time varying
      if (((T < model->sv->t0) || (model->sv->t1 < T)) && !sv_ComputeTransitionMatrix(T,model->sv,model))
      	goto ERROR_EXIT;

      // Get filtered probability at time T
      for (j=model->sv->nbasestates-1; j >= 0; j--)
	      ElementV(init_prob,j)=ProbabilityBaseStateConditionalCurrent(j,T,model);

      for (k=draws; k > 0; k--)
      	{
      	  // Draw time T regime
      	  m=DrawDiscrete(init_prob);

      	  // Draw initial z
      	  dw_NormalVector(z_initial);  //InitializeVector(z_initial,0.0);
      	  MatrixSquareRoot(z_scale,statespace->Ezz[T][m]);
      	  ProductMV(z_initial,z_scale,z_initial);
      	  AddVV(z_initial,z_initial,statespace->Ez[T][m]);
              
      	  // Draw regimes from time T+1 through T+h inclusive
      	  for (j=0; j < h; j++)
      	    {
      	      ColumnVector(prob,model->sv->baseQ,m);
      	      m=DrawDiscrete(prob);
      	      S[j]=model->sv->lag_index[m][0];
      	    }

      	  // Draw shocks
      	  for (j=h-1; j >= 0; j--) dw_NormalVector(shocks[j]); // InitializeVector(shocks[i],0.0);

      	  // Compute forecast
      	  if (!dw_state_space_forecast(forecast,h,z_initial,shocks,S,model))
      	    goto ERROR_EXIT;

      	  // Accumulate impulse response
      	  AddMatrixObservation(forecast,histogram);
      	}
    }

  for (i=0; i < DimV(percentiles); i++)
    {
      MatrixPercentile(forecast,ElementV(percentiles,i),histogram);
      for (t=statespace->nobs-nobs+1; t <= statespace->nobs; t++)
  	    {
  	      fprintf(f_out,"%d ",t);
  	      dw_PrintVector(f_out,statespace->y[t],"%le ");
  	    }
  	  for (j=0; j < h; j++)
  	    {
  	      fprintf(f_out,"%d ",t++);
  	      for (k=0; k < ColM(forecast); k++) fprintf(f_out,"%le ",ElementM(forecast,j,k));
  	      fprintf(f_out,"\n");
  	    }
      fprintf(f_out,"\n");
    }

  rtrn=1;

ERROR_EXIT:
  FreeMatrixHistogram(histogram);
  FreeMatrix(forecast);
  dw_free(S);
  FreeVector(prob);
  FreeVector(init_prob);
  FreeMatrix(z_scale);
  dw_FreeArray(shocks);
  FreeVector(x);

  return rtrn;
}

/*
   Assumes
    f_out : valid FILE pointer
    percentiles : vector of numbers between 0 and 1 inclusive
    draws : number of draws of shocks and regimes to make for each posterior draw
    posterior_draws : each row contains log posterior, log likelihood, and 
      posterior draw.  If null, current parameters are used.
    thin : thinning factor
    s : base state
    T : last observation to treat as data.  Usually equals model->nobs.
    h : non-negative integer
    model : point to valid TStateModel/T_MSStateSpace structure
    nobs : the number of actual observations to print before the forecast

   Results:
    Computes and prints to the file f_out the requested percentiles for forecasts 
    of the observables.

   Returns:
    One upon success and zero otherwise.

   Notes:
    The regime at time T is drawn from the filtered probabilities at time t, and
    is set to s there after. 

*/
int dw_state_space_forecast_percentile_regime(FILE *f_out, TVector percentiles, int draws, TMatrix posterior_draws, 
					      int thin, int s, int T, int h, TStateModel *model, int nobs)
{
  T_MSStateSpace *statespace;
  int rtrn=0, ntheta, nq, *S, i, j, k, m, n,t;
  TVector x, init_prob, prob, *shocks, z_initial;
  TMatrix forecast, z_scale;
  TMatrixHistogram *histogram;

 // quick check of passed parameters
  if (!f_out || !percentiles || (draws <= 0) || (thin <= 0) || (T < 0) || (h < 0) || !model) return 0;

  statespace=(T_MSStateSpace*)(model->theta);
  ntheta=NumberFreeParametersTheta(model);
  nq=NumberFreeParametersQ(model);

  if (T > model->nobs) return 0;

  // allocate memory
  S=(int*)dw_malloc(h*sizeof(int));
  for (i=0; i < h; i++) S[i]=s;
  forecast=CreateMatrix(h,statespace->ny);
  histogram=CreateMatrixHistogram(h,statespace->ny,100,HISTOGRAM_VARIABLE);
  z_initial=CreateVector(statespace->nz);
  shocks=dw_CreateArray_vector(h);
  for (i=h-1; i >= 0; i--) shocks[i]=CreateVector(statespace->nepsilon);
  init_prob=CreateVector(model->sv->nbasestates);
  prob=CreateVector(model->sv->nbasestates);
  z_scale=CreateMatrix(statespace->nz,statespace->nz);

  if (posterior_draws)
    {
      x=CreateVector(ColM(posterior_draws));
      i=RowM(posterior_draws)-1;
    }
  else
    {
      x=(TVector)NULL;
      i=0;
    }
  for (n=i-1000*thin; i >= 0; i-=thin)
    {
      // Get free parameters and push them into model
      if (posterior_draws)
	{
      	  RowVector(x,posterior_draws,i);
      	  if (ntheta > 0) ConvertFreeParametersToTheta(model,pElementV(x)+2);
      	  if (nq > 0) ConvertFreeParametersToQ(model,pElementV(x)+2+ntheta);

      	  if (i <= n)
      	    {
      	      printf("%d/%d posterior draws processed\n",(RowM(posterior_draws)-i)/thin,RowM(posterior_draws)/thin);
      	      n-=1000*thin;
      	    }
      	}

      // Ensure that the filter has been run through time T
      if ((statespace->t0 < T) && !Filter(T,model))
	      goto ERROR_EXIT;

      // Get filtered probability at time T
      for (j=model->sv->nbasestates-1; j >= 0; j--)
	      ElementV(init_prob,j)=ProbabilityBaseStateConditionalCurrent(j,T,model);

      for (k=draws; k > 0; k--)
	      {
      	  // Draw time T regime
      	  m=DrawDiscrete(init_prob);

      	  // Draw initial z
      	  dw_NormalVector(z_initial);  //InitializeVector(z_initial,0.0);
      	  MatrixSquareRoot(z_scale,statespace->Ezz[T][m]);
      	  ProductMV(z_initial,z_scale,z_initial);
      	  AddVV(z_initial,z_initial,statespace->Ez[T][m]);
              
      	  // Draw shocks
      	  for (j=h-1; j >= 0; j--) dw_NormalVector(shocks[j]); // InitializeVector(shocks[j],0.0);

      	  // Compute forecast
      	  if (!dw_state_space_forecast(forecast,h,z_initial,shocks,S,model))
      	    goto ERROR_EXIT;

      	  // Accumulate impulse response
      	  AddMatrixObservation(forecast,histogram);
      	}
    }

  for (i=0; i < DimV(percentiles); i++)
    {
      MatrixPercentile(forecast,ElementV(percentiles,i),histogram);
      for (t=statespace->nobs-nobs+1; t <= statespace->nobs; t++)
  	    {
  	      fprintf(f_out,"%d ",t);
  	      dw_PrintVector(f_out,statespace->y[t],"%le ");
  	    }
  	  for (j=0; j < h; j++)
  	    {
  	      fprintf(f_out,"%d ",t++);
  	      for (k=0; k < ColM(forecast); k++) fprintf(f_out,"%le ",ElementM(forecast,j,k));
  	      fprintf(f_out,"\n");
  	    }
      fprintf(f_out,"\n");
    }

  rtrn=1;

ERROR_EXIT:
  FreeMatrixHistogram(histogram);
  FreeMatrix(forecast);
  dw_free(S);
  FreeVector(prob);
  FreeVector(init_prob);
  FreeMatrix(z_scale);
  dw_FreeArray(shocks);
  FreeVector(x);

  return rtrn;
}

// #define SHOCKS
//#define DATA
 #define DATA_SHOCKS
TMatrix Forecast_ConditionalShocks_Special(int horizon, TStateModel *model)
{

#ifdef SHOCKS
  int forecast_periods=horizon;

  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);
  TMatrix forecast;
  TVector *e=dw_CreateArray_vector(forecast_periods), z_initial;
  int *S, t;

  /************ Forecast Shocks *************/
  if (statespace->nepsilon > 0)
    {
      for (t=forecast_periods-1; t >= 0; t--)
	InitializeVector(e[t]=CreateVector(statespace->nepsilon),0.0);
    }

  /* shock 0 - Patience *
  ElementV(e[0],0)=-.80;
  ElementV(e[1],0)=-.80;
  ElementV(e[2],0)=-.80;
  ElementV(e[3],0)=-.80;
  /**/

  /* Shock 1 - neutral technolgy *
  ElementV(e[0],1)=-.80;
  ElementV(e[1],1)=-.80;
  ElementV(e[2],1)=-.80;
  ElementV(e[3],1)=-.80;
  /**/

  /* Shock 5 - housing *
  ElementV(e[0],5)=-0.49;
  ElementV(e[1],5)=-0.50;
  ElementV(e[2],5)=-0.50;
  ElementV(e[3],5)=-0.50;
  /**/
#define shock_value 0.0;
#define shock_increment -0.015
  ElementV(e[0],5)=0.00;
  ElementV(e[1],5)=-0.05;
  ElementV(e[2],5)=ElementV(e[1],5)+shock_increment;
  ElementV(e[3],5)=ElementV(e[2],5)+shock_increment;
  ElementV(e[4],5)=ElementV(e[3],5)+shock_increment;
  ElementV(e[5],5)=ElementV(e[4],5)+shock_increment;
  ElementV(e[6],5)=ElementV(e[5],5)+shock_increment;
  ElementV(e[7],5)=ElementV(e[6],5)+shock_increment + 0.02;
  ElementV(e[8],5)=ElementV(e[7],5)+shock_increment;
  ElementV(e[9],5)=ElementV(e[8],5)+shock_increment;
  ElementV(e[10],5)=ElementV(e[9],5)+shock_increment;
  ElementV(e[11],5)=ElementV(e[10],5)+shock_increment;
  int i;
  for (i=0; i < 12; i++) printf("ElementV(e[%d],5) = %lf\n",i,ElementV(e[i],5));
  /**/

  /* Shock 6 - labor *
  ElementV(e[0],6)=.80;
  ElementV(e[1],6)=.80;
  ElementV(e[2],6)=.80;
  ElementV(e[3],6)=.80;
  /**/
  /*****************************************/

  printf("Computing special forecasts\n");
  dw_InitializeArray_int(S=dw_CreateArray_int(forecast_periods),0);
  z_initial=EquateVector((TVector)NULL,statespace->Ez[statespace->nobs][0]);
  forecast=dw_state_space_forecast((TMatrix)NULL,forecast_periods,z_initial,e,S,model);

  dw_FreeArray(e);
  FreeVector(z_initial);
  dw_FreeArray(S);

  return forecast;
#endif

#ifdef DATA
  int i, j;
  PRECISION ***y;
  TVector *F;
  TMatrix forecast;
  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);

  y=(PRECISION***)dw_CreateMultidimensionalArrayList_scalar(3,horizon,statespace->ny,1);

  //=============================================================================
  // credit frictions model - constrain land price growth
  for (i=horizon-1; i >= 0; i--)
    for (j=statespace->ny-1; j >= 0; j--)
      if ((j == 0) && (i < 12))    
	y[i][j][0] = -0.03/4.0;
      else
	{ 
	  dw_FreeArray(y[i][j]); 
	  y[i][j]=(PRECISION*)NULL;
	}
  //=============================================================================

  printf("Constructing constrained forecast\n");
  if (F=dw_state_space_mean_conditional_forecast((TVector*)NULL,y,horizon,statespace->nobs,model))
    {
      forecast=CreateMatrix(horizon,statespace->ny);
      for (i=horizon-1; i >= 0; i--)
	for (j=statespace->ny-1; j >= 0; j--)
	  ElementM(forecast,i,j)=ElementV(F[i],j);
      dw_FreeArray(F);
    }

  dw_FreeArray(y);

  return forecast;
#endif

#ifdef DATA_SHOCKS
  T_MSStateSpace *statespace=(T_MSStateSpace*)(model->theta);
  TVector z1, z2, y, e;
  TMatrix forecast, H_Phi;
  int *S, i, t;


  // create forecast
  if (!(forecast=CreateMatrix(horizon,statespace->ny))) return (TMatrix)NULL;

  // allocate and initialize memory
  z1=CreateVector(statespace->nz);
  z2=CreateVector(statespace->nz);
  y=CreateVector(statespace->ny);
  InitializeVector(e=CreateVector(statespace->nepsilon),0.0);
  EquateVector(z1,statespace->Ez[statespace->nobs][0]);
  dw_InitializeArray_int(S=dw_CreateArray_int(horizon),0);
  H_Phi=ProductMM((TMatrix)NULL,statespace->H[0],statespace->Phiz[0]);  

  // forecast                        
  for (t=0; t < horizon; t++)
    {
      ProductMV(z2,statespace->F[S[t]],z1);
      AddVV(z1,statespace->b[S[t]],z2);

      ProductMV(y,statespace->H[S[t]],z1);
      AddVV(y,statespace->a[S[t]],y);

      if (t < 12)
	{
	  if (t < 4)
	    ElementV(e,5)=(-5.0/400.0 - ElementV(y,0))/ElementM(H_Phi,0,5);   // land prices series in position 0, land price shock in position 5
	  else if (t < 8)
	    ElementV(e,5)=(-5.0/400.0 - ElementV(y,0))/ElementM(H_Phi,0,5);
	  else
	    ElementV(e,5)=(-2.5/400.0 - ElementV(y,0))/ElementM(H_Phi,0,5);

     	  ProductMV(z2,statespace->Phiz[S[t]],e);
      	  AddVV(z1,z1,z2);
	  ProductMV(y,statespace->H[S[t]],z1);
	  AddVV(y,statespace->a[S[t]],y);
	}
 
      for (i=statespace->ny-1; i >= 0; i--)
	ElementM(forecast,t,i)=ElementV(y,i);
    }

  FreeMatrix(H_Phi);
  dw_FreeArray(S);
  FreeVector(e);
  FreeVector(y);
  FreeVector(z2);
  FreeVector(z1);

  return forecast;

#endif

}
#undef SHOCKS
#undef DATA
#undef DATA_SHOCKS


