/*
 * 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_switch.h"
#include "dw_switchio.h"
#include "dw_metropolis_theta.h"
#include "dw_rand.h"
#include "dw_matrix_rand.h"
#include "dw_error.h"
#include "dw_ascii.h"
#include "dw_parse_cmd.h"
#include "dw_array.h"
#include "dw_matrix_array.h"
#include "dw_switch_sim.h"
#include "dw_std.h"

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <time.h>

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/*
   The file contains 2 + nparameters columns which are to be interpreted as

      Column  0 : 0               = log posterior density
      Column  1 : 1               = log likelihood
      Columns 2 : 1 + nparameters = parameters

   The file is a delimited ascii file and the delimiter is a space (' ').

   Creates and returns a m x (2 + nparameters) matrix containing the data in the
   file.

   Fields that are empty or do not parse into valid floating point numbers are 
   ignored.

   Rows that do not contain 2 + nparameter valid fields are ignored.

   One of f_in, filename or tag must not be null.  If f_in is not null, this file 
   is read, starting at the current pointer.  Otherwise, if filename is not null 
   then it is opened and read.  If both f_in and filename are null, then the file
   simulation_<tag>.out is opened and read.

*/
TMatrix dw_ReadPosteriorDraws(FILE *f_in, char *filename, char *tag, int nparameters)
{
  char *fmt="simulation_%s.out", delimiter=' ';
  TMatrix posterior_draws=(TMatrix)NULL;
  int free_filename=0, m=0, cols=2+nparameters, i, j, k;
  double** Y;

  // setup filename
  if (!f_in && !filename)
    if (!tag)
      {
	printf("dw_ReadPosterior(): Must specify FILE pointer, filename or tag\n");
	return (TMatrix)NULL;
      }
    else
      {
	sprintf(filename=(char*)dw_malloc(strlen(fmt) + strlen(tag) - 1),fmt,tag);
	free_filename=1;
      }
   
  // Get matrix
  if (Y=dw_ReadDelimitedFile_double(f_in,filename,delimiter,REMOVE_EMPTY_FIELDS,0.0))
    {
      for (k=dw_DimA(Y)-1; k >= 0; k--)
	if (dw_DimA(Y[k]) == cols) m++;
      if ((m > 0) && (posterior_draws=CreateMatrix(m,cols)))
	{
	  for (k=i=0; k < dw_DimA(Y); k++)
	    if (dw_DimA(Y[k]) == cols)
	      {
		for (j=cols-1; j >= 0; j--) ElementM(posterior_draws,i,j)=Y[k][j];
		i++;
	      }
	}
      dw_FreeArray(Y);
    }

  // Clean up
  if (free_filename) dw_free(filename);

  // Return
  return posterior_draws;
}

/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/



/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*
   Assumes
    model        : pointer to valid TStateModel structure
    center_s     : target acceptance rate for each direction invidually
    peroid_s     : initial length of sub-runs to calibrate scale for each direction individually
    max_period_s : maximum length of sub-runs to calibrate scale for each direction individually
    center_a     : target acceptance rate for each block
    peroid_a     : initial length of sub-runs to calibrate scales for each block
    max_period_a : maximum length of sub-runs to calibrate scales for each block
    verbose      : non-zero for output to stdout

   Returns
    The total number of theta draws made.

   Results
    The values of model->metropolis_scale_theta are calibrated to obtain the 
    desired acceptance ratio.
*/
int calibrate_metropolis_theta_two_pass(TStateModel *model, PRECISION center_s, int period_s, int max_period_s, 
					PRECISION center_a, int period_a, int max_period_a)
{  
  int n_draws=0, i, j, n_theta=NumberFreeParametersTheta(model), n_q=NumberFreeParametersQ(model);
  TVector initial_theta=(TVector)NULL, initial_q=(TVector)NULL; 
  int *dims, n_blocks;

  // Save initial value
  if (n_theta) ConvertThetaToFreeParameters(model,pElementV(initial_theta=CreateVector(n_theta)));
  if (n_q) ConvertQToFreeParameters(model,pElementV(initial_q=CreateVector(n_q)));

  // Individual direction run
  if (period_s > 0)
    {
      // Save old blocks
      n_blocks=model->metropolis_theta->n_blocks;
      memcpy(dims=(int*)dw_malloc(n_blocks*sizeof(int)),model->metropolis_theta->block_dims,n_blocks*sizeof(int));

      // Setup one-dimensional blocks 
      Setup_metropolis_theta_blocks_full(model->metropolis_theta);

      // Calibrate
      AdaptiveScale_metropolis_theta(model,center_s,period_s,max_period_s,1);

      // Number theta draws
      for (j=0; j < model->metropolis_theta->n_blocks; j++)
	n_draws+=model->metropolis_theta->draws[j];

      // Reset blocks
      Setup_metropolis_theta_blocks(model->metropolis_theta,n_blocks,dims);
      dw_free(dims);

      // Reset initial value
      if (n_theta) ConvertFreeParametersToTheta(model,pElementV(initial_theta));
      if (n_q) ConvertFreeParametersToQ(model,pElementV(initial_q));
    }

  // Final run
  if (period_a > 0)
    {
      AdaptiveScale_metropolis_theta(model,center_a,period_a,max_period_a,2);

      for (j=0; j < model->metropolis_theta->n_blocks; j++)
      n_draws+=model->metropolis_theta->draws[0];

      // Reset initial value
      if (n_theta) ConvertFreeParametersToTheta(model,pElementV(initial_theta));
      if (n_q) ConvertFreeParametersToQ(model,pElementV(initial_q));
    }

  if (n_theta) FreeVector(initial_theta);
  if (n_q) FreeVector(initial_q);

  return n_draws;
}

/* Undefine to use wall time instead of process time */
#define WALL_TIME

//=== Static declarations 
static int Simulate(int burn_in, int draws, int thin, int period, TStateModel *model, FILE *f_out);
static int WriteMetropolisThetaInfo(char *filename, char *tag, int type, PRECISION center_s, int period_s, int max_period_s, 
				    PRECISION center_a, int period_a, int max_period_a, int ndraws, int elapsed_time, 
				    TStateModel *model);
static int SetUpMetropolisSimulation(int n_args, char **args, char *tag, TStateModel *model, int default_period);
//=======================

static int Simulate(int burn_in, int draws, int thin, int period, TStateModel *model, FILE *f_out)
{
  PRECISION logposterior;
#ifdef WALL_TIME
  time_t begin_time;
#else
   clock_t begin_time;
#endif
  int i, j, check, ntheta, nq;
  TVector theta, q;

  // Initialization
  ntheta=NumberFreeParametersTheta(model);
  nq=NumberFreeParametersQ(model);
  if (ntheta) theta=CreateVector(ntheta);
  if (nq) q=CreateVector(nq);

  // Initial log posterior
  logposterior=LogPosterior_StatesIntegratedOut(model);

  // Burn-in
  ResetCounts_metropolis_theta(model->metropolis_theta);
  ResetMetropolisHastingsCounts_q(model); 
  if (burn_in > 0)
    { 
      printf("(0 / %d)   Beginning burn in.\n",burn_in);
#ifdef WALL_TIME
      begin_time=time((time_t*)NULL);
#else
      begin_time=clock();
#endif
      for (check=period, i=1; i <= burn_in; i++)
	{
	  logposterior=draw_metropolis(model,logposterior);                 

	  if (i == check)
	    {
	      check+=period;
#ifdef WALL_TIME
	      printf("(%d / %d)  Total Elapsed Time: %d seconds\n",i,burn_in,(int)(time((time_t*)NULL) - begin_time));
#else
	      printf("(%d / %d)  Total Elapsed Time: %lf seconds\n",i,burn_in,((double)(clock() - begin_time))/CLOCKS_PER_SEC);
#endif
	    }
	}
#ifdef WALL_TIME
      printf("(%d / %d)   Elapsed Time: %d seconds\n",burn_in,burn_in,(int)(time((time_t*)NULL) - begin_time));
#else
      printf("(%d / %d)   Elapsed Time: %lf seconds\n",burn_in,burn_in,((double)(clock() - begin_time))/CLOCKS_PER_SEC);
#endif
    }

  // Simulate
  if (draws > 0)
    { 
      printf("(0 / %d)   Beginning simulation.\n",draws);
#ifdef WALL_TIME
      begin_time=time((time_t*)NULL);
#else
      begin_time=clock();
#endif
      for (check=period, i=1; i <= draws; i++)
	{
	  for (j=thin; j > 0; j--) logposterior=draw_metropolis(model,logposterior);    

	  fprintf(f_out,"%.14le %.14le",LogPosterior_StatesIntegratedOut(model),LogLikelihood_StatesIntegratedOut(model));
	  if (ntheta) 
	    {
	      ConvertThetaToFreeParameters(model,pElementV(theta));
	      for (j=0; j < ntheta; j++) fprintf(f_out," %.14le",ElementV(theta,j));
	    }
	  if (nq)
	    {
	      ConvertQToFreeParameters(model,pElementV(q));
	      for (j=0; j < nq; j++) fprintf(f_out," %.14le",ElementV(q,j));
	    }
	  fprintf(f_out,"\n");

	  if (i == check)
	    {
	      check+=period;
#ifdef WALL_TIME
	      printf("(%d / %d)  Total Elapsed Time: %d seconds\n",i,draws,(int)(time((time_t*)NULL) - begin_time));
#else
	      printf("(%d / %d)  Total Elapsed Time: %lf seconds\n",i,draws,((double)(clock() - begin_time))/CLOCKS_PER_SEC);
#endif
	    }
	}
#ifdef WALL_TIME
      printf("(%d / %d)   Elapsed Time: %d seconds\n",draws,draws,(int)(time((time_t*)NULL) - begin_time));
#else
      printf("(%d / %d)   Elapsed Time: %lf seconds\n",draws,draws,((double)(clock() - begin_time))/CLOCKS_PER_SEC);
#endif
    }

  // Clean up
  if (ntheta) FreeVector(theta);
  if (nq) FreeVector(q);
  return 0;
}

#define READ_METROPOLIS_INFO  1
#define DIAGONAL_CALIBRATION  4
#define TWO_PASS              8
#define VARIANCE_CALIBRATION 32
static int WriteMetropolisThetaInfo(char *filename, char *tag, int type, PRECISION center_s, int period_s, int max_period_s, 
				    PRECISION center_a, int period_a, int max_period_a, int ndraws, int elapsed_time, 
				    TStateModel *model)
{
  FILE *f_out;
  time_t ltime=time((time_t*)NULL);
  int i, j;
  PRECISION accept;

  if (!(f_out=fopen(filename,"wt")))
    {
      printf("Unable to create %s for output\n",filename);
      return 0;
    }

  fprintf(f_out,"//== Calibration info ==//\n");

  switch(type)
    {
    case DIAGONAL_CALIBRATION | TWO_PASS: fprintf(f_out,"type - diagonal two pass calibration\n"); break;
    case VARIANCE_CALIBRATION | TWO_PASS: fprintf(f_out,"type - variance two pass calibration\n"); break;
    default: fprintf(f_out,"type - unknown\n"); break;
    }

  if (type & TWO_PASS)
    {
      fprintf(f_out,"target acceptance rate single variable - %lf\n",center_s);
      fprintf(f_out,"initial period single variable - %d\n",period_s);
      fprintf(f_out,"maximum period single variable - %d\n",max_period_s);
      fprintf(f_out,"target acceptance rate overall - %lf\n",center_a);
      fprintf(f_out,"initial period overall - %d\n",period_a);
      fprintf(f_out,"maximum period overall - %d\n",max_period_a);
    }
  else
    {
      fprintf(f_out,"target acceptance rate - %lf\n",center_a);
      fprintf(f_out,"initial period - %d\n",period_a);
      fprintf(f_out,"maximum period - %d\n",max_period_a);
    }

  fprintf(f_out,"acceptance rates final run\n");
  for (j=0; j < model->metropolis_theta->n_blocks; j++)
    {
      accept=model->metropolis_theta->draws[j] ? (PRECISION)(model->metropolis_theta->jumps[j])/(PRECISION)(model->metropolis_theta->draws[j]) : -1.0;
      fprintf(f_out,"block[%d] = %lf\n",j,accept);
    }

  fprintf(f_out,"total draws - %d\n",ndraws);
  fprintf(f_out,"elapsed time - %d seconds\n",elapsed_time);
  fprintf(f_out,"date - %s\n",ctime(&ltime));
  if (tag)
    fprintf(f_out,"file tag - %s\n",tag);
  else
    fprintf(f_out,"No file tag\n");


  fprintf(f_out,"\n");

  Write_metropolis_theta(f_out,(char*)NULL,(char*)NULL,model->metropolis_theta);

  fclose(f_out);

  return 1;
}

/*
   Returns 1 if metropolis_theta parameters properly set and 0 otherwise.  See dw_Simulate() for 
   command line options.
*/
static int SetUpMetropolisSimulation(int n_args, char **args, char *tag, TStateModel *model, int default_period)
{  
  FILE *f_in, *f_out;
  int i, k, ndraws, ntheta, nq, period_s, max_period_s, period_a, max_period_a, type, 
    begin_time, end_time, free_filename_metropolis=0;
  PRECISION center_s, center_a;
  char *filename_variance, *filename_hessian, *filename_metropolis, *id, *fmt, *header;
  TMatrix posterior_draws=(TMatrix)NULL, variance=(TMatrix)NULL, X=(TMatrix)NULL;
  TVector mean=(TVector)NULL, x=(TVector)NULL, theta=(TVector)NULL;

  // Get Sizes
  ntheta=NumberFreeParametersTheta(model);
  nq=NumberFreeParametersQ(model);

  // Input/output file name for metropolis_theta info
  if (!(filename_metropolis=dw_ParseString_String(n_args,args,"af",(char*)NULL)))
    {
      if (!tag)
	{
	  printf("No adaptive filename (-af) or tag (-t) specified\n");
	  return 0;
	}
      fmt="metropolis_theta_%s.prn";
      sprintf(filename_metropolis=(char*)dw_malloc(strlen(fmt) + strlen(tag) - 1),fmt,tag);
      free_filename_metropolis=1;
    }

  // First try: read info from adpative file if -diagonal and -variance are not specified
  if ((dw_FindArgument_String(n_args,args,"diagonal") == -1) && (dw_FindArgument_String(n_args,args,"variance") == -1))
    if (Read_metropolis_theta((FILE*)NULL,filename_metropolis,(char*)NULL,model->metropolis_theta))
      {
	// Cleanup and return
	if (free_filename_metropolis) dw_free(filename_metropolis);
	return READ_METROPOLIS_INFO;
      }
    else
      printf("Unable to open %s.  Attempting calibration\n",filename_metropolis);
  else 
    printf("Starting recalibration\n");

  // Setup calibration parameters
  center_s=dw_ParseFloating_String(n_args,args,"cs",0.20);
  center_a=dw_ParseFloating_String(n_args,args,"ca",0.25);
  period_s=dw_ParseInteger_String(n_args,args,"ps",default_period);
  period_a=dw_ParseInteger_String(n_args,args,"pa",default_period);
  max_period_s=dw_ParseInteger_String(n_args,args,"mps",8*period_s);
  max_period_a=dw_ParseInteger_String(n_args,args,"mpa",16*period_a);

  // Second try: Read parameter draws and calibrate using variance directions if -variance is specified
  if (filename_variance=dw_ParseString_String(n_args,args,"variance",(char*)NULL))
    {
      if (!(posterior_draws=dw_ReadPosteriorDraws((FILE*)NULL,filename_variance,(char*)NULL,ntheta+nq)))
	{
	  printf("Unable to read posterior draws file - %s.\n",filename_variance);
	  return 0;
	}
      InitializeMatrix(variance=CreateMatrix(ntheta,ntheta),0.0);
      X=CreateMatrix(ntheta,ntheta);
      InitializeVector(mean=CreateVector(ntheta),0.0);
      x=CreateVector(ntheta+nq+2);
      theta=CreateVector(ntheta);
      for (i=RowM(posterior_draws)-1; i >= 0; i--)
	{
	  RowVector(x,posterior_draws,i);
	  SubVector(theta,x,2,ntheta);
	  AddVV(mean,mean,theta);
	  OuterProduct(X,theta,theta);
	  AddMM(variance,variance,X);
	}
      ProductVS(mean,mean,1.0/(PRECISION)RowM(posterior_draws));
      ProductMS(variance,variance,1.0/(PRECISION)RowM(posterior_draws));
      SubtractMM(variance,variance,OuterProduct(X,mean,mean));
      FreeMatrix(X);

      // Initializing scale and directions using variance
      Setup_metropolis_theta_variance(model->metropolis_theta,variance);
      if (model->metropolis_theta->n_blocks <= 0)
	Setup_metropolis_theta_blocks_single(model->metropolis_theta);	

      // Calibrating
      begin_time=(int)time((time_t*)NULL);
      ndraws=calibrate_metropolis_theta_two_pass(model,center_s,period_s,max_period_s,center_a,period_a,max_period_a);
      end_time=(int)time((time_t*)NULL);

      // Print Metropolis info
      WriteMetropolisThetaInfo(filename_metropolis,tag,VARIANCE_CALIBRATION | TWO_PASS,center_s,period_s,max_period_s,
			       center_a,period_a,max_period_a,ndraws,end_time-begin_time,model);

      // Clean up and return
      FreeMatrix(posterior_draws);
      FreeMatrix(variance);
      FreeVector(x);
      FreeVector(mean);
      FreeVector(theta);
      if (free_filename_metropolis) dw_free(filename_metropolis);
      return VARIANCE_CALIBRATION | TWO_PASS;
    }

  // Last try: Use diagonal
  // Initializing scale and directions
  Setup_metropolis_theta_diagonal(model->metropolis_theta);
  if (model->metropolis_theta->n_blocks <= 0)
    Setup_metropolis_theta_blocks_single(model->metropolis_theta);

  // Calibrating
  begin_time=(int)time((time_t*)NULL);
  ndraws=calibrate_metropolis_theta_two_pass(model,center_s,period_s,max_period_s,center_a,period_a,max_period_a);
  end_time=(int)time((time_t*)NULL);

  // Print Metropolis info
  WriteMetropolisThetaInfo(filename_metropolis,tag,DIAGONAL_CALIBRATION | TWO_PASS,center_s,period_s,max_period_s,
			   center_a,period_a,max_period_a,ndraws,end_time-begin_time,model);

  // Clean up and return
  if (free_filename_metropolis) dw_free(filename_metropolis);
  return DIAGONAL_CALIBRATION | TWO_PASS;
}



//****************************************************************************//
//**************************** Simulation Main  *****************************//
//****************************************************************************//
/*
   Command line parameters

    General parameters
     -outtag : filename tag

    Adaptive jump ratio parameters
     -cs <real>: target acceptance rate for single variable calibrations (default = 0.20)
     -ca <real> : target acceptance rate for overall calibrations (default = 0.25)
     -ps <integer> : the initial period for the single variable calibrations (default = 20)
     -pa <integer> : the initial period for the overall calibrations (default = 20)
     -mps <integer> : the maximum period for the single variable calibrations (default = 8*ps)
     -mpa <integer> : the maximum period for the overall calibrations (default = 16*mps)
     -diagonal : Forces calibration using two-pass diagonal, even if calibration has alread been performed.
                 This is the default option.
     -variance <filename> : Forces calibration using two-pass variance, even if calibration has already
                            been performed.  Reads draws from filename.
     -af <filename> : file for storing the calibrated metropolis parameters. (default = metropolis_theta_<tag>.prn)
     -database : write to the database instead of a file.

    Simulation parameters
     -ndraws : number of draws to save (default = 1000)
     -burnin : number of burn-in draws (default = 0.1 * ndraws)
     -thin : thinning factor.  Total number of draws made is thin*ndraws + burnin (default = 1)
     -of <filename> : output filename (default = simulation_<tag>.out)
     -random <filename> : Reads previous simulation output and randomly selects a starting value.  
   
   Attempts to setup metropolis parameters from command line for simulating theta. Note if neither -diagonal,
   or -variance are specified, the default behavior is to first look for calibration file and if is
   not present to use the two-pass diagonal method.
*/
void dw_simulate_command_line(int n_args, char **args, TStateModel *model, TVector start_value, int generator_init)
{
  FILE *f_out, *f_start;
  char *tag, *default_filename, *filename, *fmt;
  int j, row, ndraws, burn_in, thin, default_period=20, n_theta=NumberFreeParametersTheta(model), n_q=NumberFreeParametersQ(model);
  TMatrix draws;
  TVector draw;
  PRECISION logposterior, loglikelihood;

  // Initialize generator
  dw_initialize_generator(generator_init);

  // Get tag from command line
  tag=dw_ParseString(n_args,args,'t',(char*)NULL);
  tag=dw_ParseString_String(n_args,args,"ft",tag);
  tag=dw_ParseString_String(n_args,args,"outtag",tag);
  
  // Set maximum posterior and write initial value
  model->max_log_posterior=logposterior=LogPosterior_StatesIntegratedOut(model);
  loglikelihood=LogLikelihood_StatesIntegratedOut(model);
  if (model->max_posterior_theta) ConvertThetaToFreeParameters(model,pElementV(model->max_posterior_theta));
  if (model->max_posterior_q) ConvertQToFreeParameters(model,pElementV(model->max_posterior_q));
  fmt="simulation_startmax_%s.out";
  sprintf(filename=(char*)dw_malloc(strlen(fmt) + strlen(tag) - 1),fmt,tag);
  if (!(f_start=fopen(filename,"wt")))
    printf("Unable to open %s\n",filename);
  else
    {
      fprintf(f_start,"%.14le %.14le",logposterior,loglikelihood);
      if (model->max_posterior_theta) 
	for (j=0; j < n_theta; j++) fprintf(f_start," %.14le",ElementV(model->max_posterior_theta,j));
      if (model->max_posterior_q)
	dw_PrintVector(f_start,model->max_posterior_q," %.14le");
      else
	fprintf(f_start,"\n");
    }
  dw_free(filename);

  // Push starting value into model 
  if (start_value)
    {
      if (n_theta) ConvertFreeParametersToTheta(model,pElementV(start_value));
      if (n_q) ConvertFreeParametersToQ(model,pElementV(start_value)+n_theta);
      logposterior=LogPosterior_StatesIntegratedOut(model);
      loglikelihood=LogLikelihood_StatesIntegratedOut(model);
      if (f_start)
	{
	  fprintf(f_start,"%.14le %.14le",logposterior,loglikelihood);
	  dw_PrintVector(f_start,start_value," %.14le");
	}
      if (logposterior > model->max_log_posterior)
	{
	  model->max_log_posterior=logposterior;
	  if (model->max_posterior_theta) ConvertThetaToFreeParameters(model,pElementV(model->max_posterior_theta));
	  if (model->max_posterior_q) ConvertQToFreeParameters(model,pElementV(model->max_posterior_q));
	}
    }

  // Random starting values
  if (filename=dw_ParseString_String(n_args,args,"random",(char*)NULL))
    if (draws=dw_ReadPosteriorDraws((FILE*)NULL,filename,(char*)NULL,n_theta+n_q))
      {
	row=(int)floor(dw_uniform_rnd()*RowM(draws));
	if (row >= RowM(draws)) row=RowM(draws);
	draw=RowVector((TVector)NULL,draws,row);
	if (n_theta) ConvertFreeParametersToTheta(model,pElementV(draw)+2);
	if (n_q) ConvertFreeParametersToQ(model,pElementV(draw)+2+n_theta);
	printf("Using row %d as random starting value from %s\n",row,filename);
	printf("initial log posterior = %le  -  %le\n",logposterior=LogPosterior_StatesIntegratedOut(model),ElementV(draw,0));
	printf("initial log likelihood = %le  -  %le\n",loglikelihood=LogLikelihood_StatesIntegratedOut(model),ElementV(draw,1));
	ElementV(draw,0)=logposterior;
	ElementV(draw,1)=loglikelihood;
	if (f_start) dw_PrintVector(f_start,draw," %.14le");
	if (logposterior > model->max_log_posterior)
	  {
	    model->max_log_posterior=logposterior;
	    if (model->max_posterior_theta) ConvertThetaToFreeParameters(model,pElementV(model->max_posterior_theta));
	    if (model->max_posterior_q) ConvertQToFreeParameters(model,pElementV(model->max_posterior_q));
	  }
	FreeVector(draw);
	FreeMatrix(draws);
      }
    else
      {
	printf("Unable to open %s\n",filename);
	dw_exit(0);
      }
  else
    {
      printf("initial log posterior = %le\n",logposterior);
      printf("initial log likelihood = %le\n",loglikelihood);
    }

  if (SetUpMetropolisSimulation(n_args,args,tag,model,default_period))              
    {
      // simulation parameters
      ndraws=dw_ParseInteger_String(n_args,args,"ndraws",1000);
      burn_in=dw_ParseInteger_String(n_args,args,"burnin",ndraws/10);
      thin=dw_ParseInteger_String(n_args,args,"thin",1);

      // Starting Simulation
      if (dw_FindArgument_String(n_args,args,"database") == -1)
	{
	  // output filename
	  if (tag)
	    {
	      fmt="simulation_%s.out";
	      sprintf(default_filename=(char*)dw_malloc(strlen(fmt) + strlen(tag) - 1),fmt,tag);
	      filename=dw_ParseString_String(n_args,args,"fo",default_filename);
	      f_out=fopen(filename,"wt");
	      dw_free(default_filename);
	    }
	  else
	    f_out=fopen(filename=dw_ParseString_String(n_args,args,"fo","simulation.out"),"wt");
	  if (!f_out)
	    {
	      printf("Unable to open output file.\n");
	      dw_exit(0);
	    }
	}
      else
	{
	  f_out=(FILE*)NULL;
	  // initialize sdsm
	  //sdsm_init(1001,100,100,1,0,1,1);
	  printf("-database not yet supported\n");
	  dw_exit(0);
	}

      Simulate(burn_in,ndraws,thin,1000,model,f_out);                              

      if (f_start)
	{
	  fprintf(f_start,"%.14le %.14le",model->max_log_posterior,loglikelihood);
	  if (model->max_posterior_theta) 
	    for (j=0; j < n_theta; j++) fprintf(f_start," %.14le",ElementV(model->max_posterior_theta,j));
	  if (model->max_posterior_q)
	    dw_PrintVector(f_start,model->max_posterior_q," %.14le");
	  else
	    fprintf(f_start,"\n");
	  fclose(f_start);
	}

      if (f_out) fclose(f_out);
    }
  else
    {
      printf("Error setting up Metropolis adaptive simulation\n");
      if (f_start) fclose(f_start);
    }
}
