/*
 * Copyright (C) 2008-2009 Sébastien Villemot <sebastien.villemot@ens.fr>
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <cmath>
#include <cassert>
#include <iostream>

#include "ModelSpec.hh"

ModelSpec::ModelSpec(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, arc_resid_t arc_resid_arg) :
  arc_resid(arc_resid_arg), n(n_arg), alpha(alpha_arg), beta(beta_arg), delta(delta_arg), rho(rho_arg), sigma(sigma_arg),
  phi(phi_arg), A((1-beta)/(alpha*beta))
{
  taus = new double[n];
}

ModelSpec::~ModelSpec()
{
  delete[] taus;
}

double
ModelSpec::weighted_mass(double min, double max, int j) const
{
  double w = ((double) j)/(((double) n) - 1);
  return (1-w)*min + w*max;
}

void
ModelSpec::steady_state(gsl_vector *y_ss) const
{
  for(int j = 0; j < n; j++)
    {
      gsl_vector_set(y_ss, j, A);
      gsl_vector_set(y_ss, n+j, 1);
      gsl_vector_set(y_ss, 2*n+j, delta);
      gsl_vector_set(y_ss, 3*n+j, 1);
      gsl_vector_set(y_ss, 4*n+j, 1);
    }
  gsl_vector_set(y_ss, 5*n, 1);
}

void
ModelSpec::errors(const gsl_vector *y_prev, const gsl_vector *y_curr, const gsl_vector *fwd, const gsl_vector *shocks, gsl_vector *errors) const
{
  assert(y_prev->stride == 1 && y_curr->stride == 1 && errors->stride == 1);
  assert((int) y_prev->size == 5*n+1 && (int) y_curr->size == 5*n+1 && (int) fwd->size == n
         && (int) shocks->size == n+1 && (int) errors->size == 5*n+1);

  const double *c_curr = y_curr->data;
  const double *i_curr = y_curr->data + 2*n;
  const double *k_curr = y_curr->data + 3*n;
  const double *a_curr = y_curr->data + 4*n;
  const double lambda_curr = gsl_vector_get(y_curr, 5*n);

  const double *k_prev = y_prev->data + 3*n;
  const double *a_prev = y_prev->data + 4*n;

  double uc_curr[n], ul_curr[n], f_curr[n], fk_curr[n], fl_curr[n];

  marginal_utilities(y_curr->data, uc_curr, ul_curr);
  production(y_prev->data, y_curr->data, f_curr, fk_curr, fl_curr);

  double *errs = errors->data;

  for(int j = 0; j < n; j++)
    {
      errs[j] = (taus[j] * uc_curr[j] - lambda_curr)/(taus[j] * uc_curr[j]);
      errs[n+j] = (taus[j] * ul_curr[j] + lambda_curr * a_curr[j] * fl_curr[j]) / (taus[j] * ul_curr[j]);
      double lhs = lambda_curr * (1 + phi * (i_curr[j] / k_prev[j] - delta));
      errs[2*n+j] = (lhs - beta * gsl_vector_get(fwd, j))/lhs;
      errs[3*n+j] = (k_curr[j] - i_curr[j] - (1 - delta) * k_prev[j]) / k_curr[j];
      errs[4*n+j] = (a_curr[j] - exp(rho * log(a_prev[j]) + sigma * (gsl_vector_get(shocks, j) + gsl_vector_get(shocks, n)))) / a_curr[j];
    }

  double lhs = 0, rhs = 0;
  for(int j = 0; j < n; j++)
    {
      switch(arc_resid)
        {
        case BenARC:
          lhs += c_curr[j] + i_curr[j] + (1 - delta) * k_prev[j] + phi / 2 * k_prev[j] * pow(i_curr[j] / k_prev[j] - delta, 2);
          rhs += a_curr[j] * f_curr[j] + k_prev[j];
          break;
        case MichelARC:
          lhs += c_curr[j] + i_curr[j] - delta * k_prev[j];
          rhs += a_curr[j] * f_curr[j] - phi / 2 * k_prev[j] * pow(i_curr[j] / k_prev[j] - delta, 2);
          break;
        case PaulARC:
          lhs += c_curr[j] + i_curr[j] - delta * k_prev[j] + phi / 2 * k_prev[j] * pow(i_curr[j] / k_prev[j] - delta, 2);
          rhs += a_curr[j] * f_curr[j];
          break;
        }
    }
  errs[5*n] = 1 - rhs / lhs;
}

double
ModelSpec::max_error(const gsl_vector *y_prev, const gsl_vector *y_curr, const gsl_vector *fwd, const gsl_vector *shocks) const
{
  gsl_vector *errs = gsl_vector_alloc(5*n+1);
  errors(y_prev, y_curr, fwd, shocks, errs);

  double max_err = NAN;
  for(int i = 0; i < 5*n+1; i++)
    {
      double e = log10(fabs(gsl_vector_get(errs, i)));
#ifdef DEBUG
      if (std::isnan(e))
        std::cout << "ModelSpec::max_error: WARNING: error of equation " << i << " is NaN" << std::endl;
#endif
      if (std::isnan(max_err) || e > max_err)
        max_err = e;
    }
  gsl_vector_free(errs);
  return max_err;
}

void
ModelSpec::forward_part(const gsl_vector *y_prev, const gsl_vector *y_curr, const gsl_vector *y_next, const gsl_vector *shocks, gsl_vector *fwd) const
{
  assert(y_prev->stride == 1 && y_curr->stride == 1 && y_next->stride == 1);
  assert((int) y_prev->size == 5*n+1 && (int) y_curr->size == 5*n+1
         && (int) y_next->size == 5*n+1 && (int) fwd->size == n
         && (int) shocks->size == n+1);

  const double *k_curr = y_curr->data + 3*n;

  const double *i_next = y_next->data + 2*n;
  const double *a_next = y_next->data + 4*n;
  const double lambda_next = gsl_vector_get(y_next, 5*n);

  double f_next[n], fk_next[n], fl_next[n];
  production(y_curr->data, y_next->data, f_next, fk_next, fl_next);

  for(int j = 0; j < n; j++)
    gsl_vector_set(fwd, j, lambda_next * (1.0 + a_next[j] * fk_next[j] + phi * (1.0 + 0.5 * (i_next[j] / k_curr[j] - delta)) * (i_next[j] / k_curr[j] - delta)));
}

void
ModelSpec::write_common_params(std::ostream &output) const
{
  output << "n=" << n << ", alpha=" << alpha << ", beta=" << beta
         << ", delta=" << delta << ", sigma=" << sigma
         << ", rho=" << rho << ", phi=" << phi << ", ";
}

void
ModelSpec::write_state(std::ostream &output, const gsl_vector *y) const
{
  int i;
  output << "Consumption:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(y, i);
  output << std::endl
         << "Labor:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(y, i+n);
  output << std::endl
         << "Investment:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(y, i+2*n);
  output << std::endl
         << "Capital:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(y, i+3*n);
  output << std::endl
         << "Technology:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(y, i+4*n);
  output << std::endl
         << "Lambda: " << gsl_vector_get(y, 5*n) << std::endl;
}

void
ModelSpec::write_errors(std::ostream &output, const gsl_vector *errs) const
{
  int i;
  output << "Marginal utility of consumption:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(errs, i);
  output << std::endl
         << "Marginal utility of labor:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(errs, i+n);
  output << std::endl
         << "Euler equation:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(errs, i+2*n);
  output << std::endl
         << "Law of motion of capital:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(errs, i+3*n);
  output << std::endl
         << "Law of motion of technology:";
  for(i = 0; i < n; i++)
    output << " " << gsl_vector_get(errs, i+4*n);
  output << std::endl
         << "World budget constraint: " << gsl_vector_get(errs, 5*n) << std::endl;
}

/* Parts common to A1 and A5 */

ModelSpecA1A5::ModelSpecA1A5(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, arc_resid_t arc_resid_arg) : ModelSpec(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg)
{
  gammas = new double[n];
}

ModelSpecA1A5::~ModelSpecA1A5()
{
  delete[] gammas;
}

void
ModelSpecA1A5::production(const double y_prev[], const double y_curr[], double f[], double fk[], double fl[]) const
{
  const double *k_prev = y_prev + 3*n;
  for(int j = 0; j < n; j++)
    {
      f[j] = A * pow(k_prev[j], alpha);
      fk[j] = alpha * A * pow(k_prev[j], alpha - 1);
      fl[j] = NAN;
    }
}

void
ModelSpecA1A5::marginal_utilities(const double y_curr[], double uc[], double ul[]) const
{
  const double *c_curr = y_curr;
  for(int j = 0; j < n; j++)
    {
      uc[j] = pow(c_curr[j], -1/gammas[j]);
      ul[j] = NAN;
    }
}


/* Production function common to A2, A3, A6 and A7 */

ModelSpecSKLCB::ModelSpecSKLCB(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, arc_resid_t arc_resid_arg) : ModelSpec(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg)
{
}

void
ModelSpecSKLCB::production(const double y_prev[], const double y_curr[], double f[], double fk[], double fl[]) const
{
  const double *k_prev = y_prev + 3*n;
  const double *l_curr = y_curr + n;
  for(int j = 0; j < n; j++)
    {
      f[j] = A * pow(k_prev[j], alpha) * pow(l_curr[j], 1 - alpha);
      fk[j] = alpha * A * pow(k_prev[j] / l_curr[j], alpha - 1);
      fl[j] = (1 - alpha) * A * pow(k_prev[j] / l_curr[j], alpha);
    }
}


/* Parts common to A2 and A6 */

ModelSpecA2A6::ModelSpecA2A6(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, arc_resid_t arc_resid_arg) : ModelSpecSKLCB(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg)
{
  gammas = new double[n];
  etas = new double[n];
  b = new double[n];
}

ModelSpecA2A6::~ModelSpecA2A6()
{
  delete[] gammas;
  delete[] etas;
  delete[] b;
}

void
ModelSpecA2A6::marginal_utilities(const double y_curr[], double uc[], double ul[]) const
{
  const double *c_curr = y_curr;
  const double *l_curr = y_curr + n;
  for(int j = 0; j < n; j++)
    {
      uc[j] = pow(c_curr[j], -1/gammas[j]);
      ul[j] = -b[j]*pow(l_curr[j], 1/etas[j]);
    }
}


/* Parts common to A3 and A7 */

ModelSpecA3A7::ModelSpecA3A7(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, arc_resid_t arc_resid_arg) : ModelSpecSKLCB(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), time_endowment(time_endowment_arg), psi(1/(time_endowment-alpha*(time_endowment-1)))
{
  gammas = new double[n];
}

ModelSpecA3A7::~ModelSpecA3A7()
{
  delete[] gammas;
}

void
ModelSpecA3A7::marginal_utilities(const double y_curr[], double uc[], double ul[]) const
{
  const double *c_curr = y_curr;
  const double *l_curr = y_curr + n;
  for(int j = 0; j < n; j++)
    {
      uc[j] = psi*pow(c_curr[j], psi*(1-1/gammas[j])-1)*pow(time_endowment-l_curr[j], (1-psi)*(1-1/gammas[j]));
      ul[j] = -(1-psi)*pow(c_curr[j], psi*(1-1/gammas[j]))*pow(time_endowment-l_curr[j], -psi-1/gammas[j]+psi/gammas[j]);
    }
}


/* Parts common to A4 and A8 */

ModelSpecA4A8::ModelSpecA4A8(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, arc_resid_t arc_resid_arg) : ModelSpec(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), time_endowment(time_endowment_arg)
{
  gammas = new double[n];
  mus = new double[n];
  xis = new double[n];
  b = new double[n];
}

ModelSpecA4A8::~ModelSpecA4A8()
{
  delete[] gammas;
  delete[] mus;
  delete[] xis;
  delete[] b;
}

void
ModelSpecA4A8::marginal_utilities(const double y_curr[], double uc[], double ul[]) const
{
  const double *c_curr = y_curr;
  const double *l_curr = y_curr + n;
  for(int j = 0; j < n; j++)
    {
      double x = pow(pow(c_curr[j], 1-1/xis[j]) + b[j]*pow(time_endowment - l_curr[j], 1-1/xis[j]), (1-1/gammas[j])/(1-1/xis[j])-1);
      uc[j] = pow(c_curr[j], -1/xis[j])*x;
      ul[j] = -b[j]*pow(time_endowment - l_curr[j], -1/xis[j])*x;
    }
}

void
ModelSpecA4A8::production(const double y_prev[], const double y_curr[], double f[], double fk[], double fl[]) const
{
  const double *k_prev = y_prev + 3*n;
  const double *l_curr = y_curr + n;
  for(int j = 0; j < n; j++)
    {
      double x = alpha*pow(k_prev[j], mus[j]) + (1-alpha)*pow(l_curr[j], mus[j]);
      f[j] = A*pow(x, 1/mus[j]);
      double y = A*pow(x, 1/mus[j]-1);
      fk[j] = alpha*pow(k_prev[j], mus[j]-1)*y;
      fl[j] = (1-alpha)*pow(l_curr[j], mus[j]-1)*y;
    }
}


/* Specification A1 */

ModelSpecA1::ModelSpecA1(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double gamma_arg, arc_resid_t arc_resid_arg) : ModelSpecA1A5(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), gamma(gamma_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = gamma;
      taus[j] = pow(A, 1/gamma);
    }
}

void
ModelSpecA1::write_output(std::ostream &output) const
{
  output << "Spec A1: ";
  write_common_params(output);
  output << "gamma=" << gamma << std::endl;
}

int
ModelSpecA1::spec_no() const
{
  return 1;
}


/* Specification A2 */

ModelSpecA2::ModelSpecA2(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double gamma_arg, double eta_arg, arc_resid_t arc_resid_arg) : ModelSpecA2A6(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), gamma(gamma_arg), eta(eta_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = gamma;
      etas[j] = eta;
      b[j] = (1-alpha)*pow(A, 1-1/gamma);
      taus[j] = pow(A, 1/gamma);
    }
}

void
ModelSpecA2::write_output(std::ostream &output) const
{
  output << "Spec A2: ";
  write_common_params(output);
  output << "gamma=" << gamma << ", eta=" << eta << std::endl;
}

int
ModelSpecA2::spec_no() const
{
  return 2;
}


/* Specification A3 */

ModelSpecA3::ModelSpecA3(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, double gamma_arg, arc_resid_t arc_resid_arg) : ModelSpecA3A7(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, time_endowment_arg, arc_resid_arg), gamma(gamma_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = gamma;
      taus[j] = (1/psi)*pow(A, 1-psi*(1-1/gamma))*pow(time_endowment-1, -(1-psi)*(1-1/gamma));
    }
}

void
ModelSpecA3::write_output(std::ostream &output) const
{
  output << "Spec A3: ";
  write_common_params(output);
  output << "time_endowment=" << time_endowment
         << ", gamma=" << gamma << std::endl;
}

int
ModelSpecA3::spec_no() const
{
  return 3;
}


/* Specification A4 */

ModelSpecA4::ModelSpecA4(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, double gamma_arg, double mu_arg, double xi_arg, arc_resid_t arc_resid_arg) : ModelSpecA4A8(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, time_endowment_arg, arc_resid_arg), gamma(gamma_arg), mu(mu_arg), xi(xi_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = gamma;
      mus[j] = mu;
      xis[j] = xi;
      b[j] = (1-alpha)*pow(A, 1-1/xi)/pow(time_endowment-1, -1/xi);
      taus[j] = pow(A, 1/xi)*pow(pow(A, 1-1/xi) + b[j]*pow(time_endowment - 1, 1-1/xi), 1-(1-1/gamma)/(1-1/xi));
    }
}

void
ModelSpecA4::write_output(std::ostream &output) const
{
  output << "Spec A4: ";
  write_common_params(output);
  output << "time_endowment=" << time_endowment
         << ", gamma=" << gamma << ", mu=" << mu << ", xi=" << xi
         << std::endl;
}

int
ModelSpecA4::spec_no() const
{
  return 4;
}


/* Specification A5 */

ModelSpecA5::ModelSpecA5(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double gamma_min_arg, double gamma_max_arg, arc_resid_t arc_resid_arg) : ModelSpecA1A5(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), gamma_min(gamma_min_arg), gamma_max(gamma_max_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = weighted_mass(gamma_min, gamma_max, j);
      taus[j] = pow(A, 1/gammas[j]);
    }
}

void
ModelSpecA5::write_output(std::ostream &output) const
{
  output << "Spec A5: ";
  write_common_params(output);
  output << "gamma=[ ";
  for(int j = 0; j < n; j++)
    output << gammas[j] << " ";
  output << "]" << std::endl;
}

int
ModelSpecA5::spec_no() const
{
  return 5;
}


/* Specification A6 */

ModelSpecA6::ModelSpecA6(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double gamma_min_arg, double gamma_max_arg, double eta_min_arg, double eta_max_arg, arc_resid_t arc_resid_arg) : ModelSpecA2A6(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, arc_resid_arg), gamma_min(gamma_min_arg), gamma_max(gamma_max_arg), eta_min(eta_min_arg), eta_max(eta_max_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = weighted_mass(gamma_min, gamma_max, j);
      etas[j] = weighted_mass(eta_min, eta_max, j);
      b[j] = (1-alpha)*pow(A, 1-1/gammas[j]);
      taus[j] = pow(A, 1/gammas[j]);
    }
}

void
ModelSpecA6::write_output(std::ostream &output) const
{
  output << "Spec A6: ";
  write_common_params(output);
  output << "gamma=[ ";
  for(int j = 0; j < n; j++)
    output << gammas[j] << " ";
  output << "], eta=[ ";
  for(int j = 0; j < n; j++)
    output << etas[j] << " ";
  output << "]" << std::endl;
}

int
ModelSpecA6::spec_no() const
{
  return 6;
}


/* Specification A7 */

ModelSpecA7::ModelSpecA7(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, double gamma_min_arg, double gamma_max_arg, arc_resid_t arc_resid_arg) : ModelSpecA3A7(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, time_endowment_arg, arc_resid_arg), gamma_min(gamma_min_arg), gamma_max(gamma_max_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = weighted_mass(gamma_min, gamma_max, j);
      taus[j] = (1/psi)*pow(A, 1-psi*(1-1/gammas[j]))*pow(time_endowment-1, -(1-psi)*(1-1/gammas[j]));
    }
}

void
ModelSpecA7::write_output(std::ostream &output) const
{
  output << "Spec A7: ";
  write_common_params(output);
  output << "time_endowment=" << time_endowment << ", gamma=[ ";
  for(int j = 0; j < n; j++)
    output << gammas[j] << " ";
  output << "]" << std::endl;
}

int
ModelSpecA7::spec_no() const
{
  return 7;
}


/* Specification A8 */

ModelSpecA8::ModelSpecA8(int n_arg, double alpha_arg, double beta_arg, double delta_arg, double rho_arg, double sigma_arg, double phi_arg, double time_endowment_arg, double gamma_min_arg, double gamma_max_arg, double mu_min_arg, double mu_max_arg, double xi_min_arg, double xi_max_arg, arc_resid_t arc_resid_arg) : ModelSpecA4A8(n_arg, alpha_arg, beta_arg, delta_arg, rho_arg, sigma_arg, phi_arg, time_endowment_arg, arc_resid_arg), gamma_min(gamma_min_arg), gamma_max(gamma_max_arg), mu_min(mu_min_arg), mu_max(mu_max_arg), xi_min(xi_min_arg), xi_max(xi_max_arg)
{
  for(int j = 0; j < n; j++)
    {
      gammas[j] = weighted_mass(gamma_min, gamma_max, j);
      mus[j] = weighted_mass(mu_min, mu_max, j);
      xis[j] = weighted_mass(xi_min, xi_max, j);
      b[j] = (1-alpha)*pow(A, 1-1/xis[j])/pow(time_endowment-1, -1/xis[j]);
      taus[j] = pow(A, 1/xis[j])*pow(pow(A, 1-1/xis[j]) + b[j]*pow(time_endowment - 1, 1-1/xis[j]), 1-(1-1/gammas[j])/(1-1/xis[j]));
    }
}

void
ModelSpecA8::write_output(std::ostream &output) const
{
  output << "Spec A8: ";
  write_common_params(output);
  output << "time_endowment=" << time_endowment
         << ", gamma=[ ";
  for(int j = 0; j < n; j++)
    output << gammas[j] << " ";
  output << "], mu=[ ";
  for(int j = 0; j < n; j++)
    output << mus[j] << " ";
  output << "], xi=[ ";
  for(int j = 0; j < n; j++)
    output << xis[j] << " ";
  output << "]" << std::endl;
}

int
ModelSpecA8::spec_no() const
{
  return 8;
}
