#ifndef __FILE_EMMUSR_H_SEEN__
#define __FILE_EMMUSR_H_SEEN__
/* ----------------------------------------------------------------------------

Copyright (C) 2009.

A. Ronald Gallant
Post Office Box 659
Chapel Hill NC 27514-0659
USA

This program is 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 2 of the License, or
(at your option) any later version.

This program 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.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

-----------------------------------------------------------------------------*/

//#define DEBUG_EMMUSR
#undef DEBUG_EMMUSR

//#define MONTHLY_FREQUENCY
#undef MONTHLY_FREQUENCY

//#define USE_ACCURATE_RBAR
#undef USE_ACCURATE_RBAR

/*
Economy II

Parameter  Name     Meaning                            Plausible

theta[1]   g_c      Consumption growth                 0.0184/12.0
theta[2]   g_d      Dividend growth                    0.0184/12.0
theta[3]   sig_c    Sdev of consumption growth         0.0379/sqrt(12.0)
theta[4]   sig_d    Sdev of dividend growth            0.12/sqrt(12.0)
theta[5]   omega    Correlation of cg & dg             0.15
theta[6]   gamma    Risk aversion parameter            1.0
theta[7]   rho      Subjective discount factor         pow(0.98,1.0/12.0)
theta[8]   lambda   Intercept of lambda function       2.25
theta[9]   k        Slope of lambda function           10.0 but 3.0 for f plots
theta[10]  b_0      Scaling of prospect part           2.0/12.0
theta[11]  eta      Autoregressive parameter of z      pow(0.9,1.0/12.0)
theta[12]  mudc     Mean log dividend/consumption      -3.3857


stats[1]   br       Bond returns                       1.000743618
stats[2]   bv       Variance of bond returns           0.000024083
stats[3]   sr       Stock returns                      1.002851
stats[4]   sv       Variance of stock returns          0.001771989

Parameter  Name     Reasonable support

theta[1]   g_c       [-0.02, 0.02]         [-0.24, 0.24]
theta[2]   g_c       [-0.02, 0.02]         [-0.24, 0.24]
theta[3]   sig_c     [ 0.0, 1.0]           [0.0, 3.46]
theta[4]   sig_d     [ 0.0, 3.0]           [0.0, 10.39]
theta[5]   omega     [-1.0, 1.0]           [-1.0, 1.0]
theta[6]   gamma     [ 0.25, 10.0]         [ 0.25, 10.0]
theta[7]   rho       [ 0.9, 0.99999]       [ 0.9, 0.99999]
theta[8]   lambda    [ 0.0, 10.0]          [ 0.0, 10.0]
theta[9]   k         [ 0.0, 20.0]          [ 0.0, 20.0]
theta[10]  b_0       [ 0.0, 10.0]          [ 0.0, 120.0]
theta[11]  eta       [ 0.25, 1.0]          [ 0.25, 1.0] 
theta[12]  phi_u     [-10.0, 10.0]         [-10.0, 10.0]

*/

#include "libsnp.h"
#include "libsmm.h"
#include "emm_base.h"
#include "snp.h"


namespace emm {

  class prospect_usrmod;

  typedef prospect_usrmod usrmod_type;

  const INTEGER n_state = 1;   //Number of state variables
  const INTEGER n_parms = 12;  //Number of sci mod parameters
  const INTEGER n_funcs = 4;   //Number of sci mod functionals
  const INTEGER n_datum = 4;   //Number of sci mod functionals
  
  class linear_function {
  private:
    REAL a;
    REAL b;
    REAL x0;
  public:
    void initialize(REAL intercept, REAL slope, REAL origin)
      { a = intercept; b = slope; x0 = origin; }
    REAL operator()(REAL x) { return a + b*(x - x0); }
    REAL intercept() { return a; }
    REAL slope() { return b; }
    REAL origin() { return x0; }
  };

  class linear_interpolater {
  private:
    std::vector<linear_function> funcs;
    typedef std::vector<linear_function>::size_type lfst;
    REAL xmin;
    REAL xmax;
    lfst N;
    lfst hash(REAL x) { return lfst( REAL(N-2)*(x-xmin)/(xmax-xmin) ); }
  public:
    linear_interpolater() 
    {
      scl::realmat grid(2,1) ; grid[1] = 0.0; grid[2] = 1.0;
      scl::realmat vals(2,1) ; vals[1] = 0.0; vals[2] = 1.0;
      update(grid,vals);
    }
    linear_interpolater(scl::realmat& x, scl::realmat& y) { update(x,y); };
    void update(scl::realmat& x, scl::realmat& y) 
    { 
      INTEGER n = x.size(); N = lfst(n);  
      funcs.clear(); funcs.reserve(N);
      if (n<2) 
        scl::error("Error, linear_interpolater, x.size() < 2");
      if (x.ncol() != 1 || y.ncol() != 1) 
        scl::error("Error, linear_interpolater, x or y not a vector");
      if (n != y.size()) 
        scl::error("Error, linear_interpolater, x and y sizes differ");
      scl::intvec rank = x.sort(); 
      y = y(rank,"");
      xmin = x[1]; xmax = x[n];
      linear_function f;
      for (INTEGER i=1; i<n; ++i) {
        f.initialize(y[i], (y[i+1]-y[i])/(x[i+1]-x[i]), x[i]);
        funcs.push_back(f);
      }
      funcs.push_back(f);
    }
    REAL operator()(REAL x)
    {
       if (x <= funcs[0].origin()) return funcs[0](x);
       if (x >= funcs[N-1].origin()) return funcs[N-1](x);
       lfst i = hash(x);
       if (x < funcs[i].origin()) while(x < funcs[--i].origin());
       else if (x >= funcs[i+1].origin()) while(x >= funcs[++i+1].origin());
       return funcs[i](x);
    }
    REAL get_xmin() {return xmin;}
    REAL get_xmax() {return xmax;}
   };

  struct prospect_usrmod_variables {
    INTEGER n0;  // for lags and to allow transients to dissipate
    INTEGER n;   // simulation length
    INTEGER n1;  // for leads
    INTEGER nt;  // nt = n0 + n + n1
    scl::realmat log_consumption_growth;
    scl::realmat log_dividend_growth;
    scl::realmat consumption_growth;               // cg_t 
    scl::realmat dividend_growth;                  // dg_t
    scl::realmat consumption;                      // C_t
    scl::realmat dividend;                         // D_t
    scl::realmat state_variable;                   // z_t
    scl::realmat marginal_rate_of_substitution;    // need to derive a formula
    scl::realmat price_dividend_ratio;             // PD_t
    scl::realmat gross_stock_return;               // R_t
    scl::realmat gross_risk_free_rate;             // Rf
    scl::realmat geometric_stock_return;
    scl::realmat geometric_risk_free_rate;
    prospect_usrmod_variables() { }
    prospect_usrmod_variables(INTEGER n_lag, INTEGER n_sim, INTEGER n_lead)
    : n0(n_lag), n(n_sim), n1(n_lead), nt(n0+n+n1),
      log_consumption_growth(nt,1,0.0),
      log_dividend_growth(nt,1,0.0),
      consumption_growth(nt,1,0.0),
      dividend_growth(nt,1,0.0),
      consumption(nt,1,0.0),
      dividend(nt,1,0.0),
      state_variable(nt,1,1.0),
      marginal_rate_of_substitution(nt,1,1.0),
      price_dividend_ratio(nt,1,0.0),
      gross_stock_return(nt,1,0.0),
      gross_risk_free_rate(nt,1,0.0),
      geometric_stock_return(nt,1,0.0),
      geometric_risk_free_rate(nt,1,0.0)
    { }
    std::vector<std::string> get_prospect_usrmod_variables(scl::realmat& mv);
  };
  
  struct prospect_usrmod_parameters {
    REAL g_c;        // Consumption growth              0.001533    0.0184
    REAL g_d;        // Dividend growth                 0.001533    0.0184
    REAL sig_c;      // Sdev of consumption growth      0.010941    0.0379
    REAL sig_d;      // Sdev of dividend growth         0.034641    0.12
    REAL omega;      // Correlation of cg & dg          0.15        0.15
    REAL gamma;      // Risk aversion parameter         1.0         1.0
    REAL rho;        // Subjective discount factor      0.998318    0.98
    REAL lambda;     // Intercept of lambda function    2.25        2.25
    REAL k;          // Slope of lambda function        10.0        10.0
    REAL b_0;        // Scaling of prospect part        0.166667    2.0
    REAL eta;        // Autoregressive parameter of z   0.991258    0.9
    REAL mudc;       // Mean log dividend/consumption  -3.3857     -3.3857
    INTEGER p;       // Number of model parameters      12          12
    prospect_usrmod_parameters() 
    : p(n_parms)
    {
      scl::realmat theta(p,1); 
      #if defined MONTHLY_FREQUENCY
        theta[1]  = 0.0015333333333334;      
	theta[2]  = 0.0015333333333334;
        theta[3]  = 0.0109407876011434;      
	theta[4]  = 0.0346410161513775;
        theta[5]  = 0.15;                    
	theta[6]  = 1.0;
        theta[7]  = 0.99831785744726;        
	theta[8]  = 2.25;
        theta[9]  = 10.0;                    
	theta[10] = 0.1666666666666667;
        theta[11] = 0.991258389045303;
	theta[12] = -3.3857;
      #else 
        theta[1]  = 0.0184;                  
	theta[2]  = 0.0184;
        theta[3]  = 0.0379;                  
	theta[4]  = 0.12;
        theta[5]  = 0.15;                    
	theta[6]  = 1.0;
        theta[7]  = 0.98;                    
	theta[8]  = 2.25;
        theta[9]  = 10.0;                     
	theta[10] = 2.0;
        theta[11] = 0.9;
	theta[12] = -3.3857;
      #endif
      set_theta(theta);
    }
    void set_theta(const scl::realmat& theta)
    {
      if (theta.nrow() != p || theta.ncol() != 1) 
        scl::error("Error, prospect_usrmod, set_theta, bad dimension");
      g_c    = theta[1];
      g_d    = theta[2];
      sig_c  = theta[3];
      sig_d  = theta[4];
      omega  = theta[5];
      gamma  = theta[6];
      rho    = theta[7];
      lambda = theta[8];
      k      = theta[9];
      b_0    = theta[10];
      eta    = theta[11];
      mudc   = theta[12];
    }
    void get_theta(scl::realmat& theta)
    {
      theta.resize(p,1);
      theta[1]  = g_c;
      theta[2]  = g_d;
      theta[3]  = sig_c;
      theta[4]  = sig_d;
      theta[5]  = omega;
      theta[6]  = gamma;
      theta[7]  = rho;
      theta[8]  = lambda;
      theta[9]  = k;
      theta[10] = b_0;
      theta[11] = eta;
      theta[12] = mudc;
    }
  };

  class prospect_usrmod : public libsmm::usrmod_base {
  private:
    prospect_usrmod_parameters mp;
    prospect_usrmod_variables mv;
    bool make_sim(INT_32BIT& seed, scl::realmat& sim);
    bool make_sim(INT_32BIT& seed, scl::realmat& sim, scl::realmat& func);
    linear_interpolater BHSpdval;    
    std::ostream* debug;
    bool debug_switch;
    scl::realmat data;
    INT_32BIT bootstrap_seed;
    INT_32BIT simulation_seed;
  public:
    prospect_usrmod (const scl::realmat& dat, INTEGER len_mod_parm,
    INTEGER len_mod_func, const std::vector<std::string>& pfvec,
    const std::vector<std::string>& alvec, std::ostream& detail);
    INTEGER len_rho() { return n_parms; }
    INTEGER len_stats() { return n_funcs; }
    void get_rho(scl::realmat& parm) { mp.get_theta(parm); }
    void set_rho(const scl::realmat& parm) { mp.set_theta(parm); }
    bool support(const scl::realmat& parm);
    bool gen_sim(scl::realmat& sim);
    bool gen_sim(scl::realmat& sim, scl::realmat& func);
    bool gen_bootstrap(std::vector<scl::realmat>& bs);
    scl::den_val prior(const scl::realmat& parm, const scl::realmat& func);
    scl::realmat annualize_parms(const scl::realmat& theta);
    scl::realmat annualize_funcs(const scl::realmat& stats);
    void write_usrvar(const char* filename);
  };
  
}
#endif
