/*
  Copyright (c) 2011 Paul Richmond, University of Sheffield , UK; 
  all rights reserved unless otherwise stated.

  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.

  In addition to the regulations of the GNU General Public License,
  publications and communications based in parts on this program or on
  parts of this program are required to cite the article 
  "Democratic population decisions result in robust policy-gradient 
  learning: a parametric study with GPU simulations" by Paul Richmond, 
  Lars Buesing, Michele Giugliano and Eleni Vasilaki, PLoS ONE Neuroscience, 
  Under Review.. 

  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., 59 Temple Place, Suite 330, Boston,
  MA 02111-1307 USA
*/
/*
This CUDA source file contains the function definitions used for creating data 
plots and generating GPU plot scripts.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#include <time.h>
#include <float.h>
#include <cutil_inline.h>

#include "output.cuh"

template<DYNAMICS dynamics>
void printConfigurationToFile(FILE* stream, int config_no)
{
	time_t rawtime;
	struct tm * timeinfo;
			
	//output configuration info
	time ( &rawtime );
	timeinfo = localtime ( &rawtime );
	fprintf(stream, "# Output from GPU model: %s", asctime(timeinfo));
	fprintf(stream, "# Configuration number (%i)\n", config_no);
	if (dynamics == DYN_SYS)
		fprintf(stream, "# Simulation using Dynamical Lateral Connections (%i)\n", config_no);
	else //NO_DYN_SYS
		fprintf(stream, "# Simulation NOT using Dynamical Lateral Connections (%i)\n", config_no);
	if (moving_target)
		fprintf(stream, "# Random moving target between trials\n");
	else
		fprintf(stream, "# Static moving target (%f) between trials\n", static_target);
	if (REFACTORY)
		fprintf(stream, "# Refactory period for spiking neurons\n");
	else
		fprintf(stream, "# No refactory period for spiking neurons\n");

	fprintf(stream, "# \n");
	fprintf(stream, "# Global Values ************************\n");
	fprintf(stream, "# N = %i \n", N);
	fprintf(stream, "# M = %i \n", M);
	fprintf(stream, "# T = %i \n", T);

	fprintf(stream, "# Learning Step Values ************************\n");
	fprintf(stream, "# no_intval = %i \n", no_intval);
	fprintf(stream, "# analysis_trials = %i \n", analysis_trials);
	fprintf(stream, "# gradiant_analysis_trials = %i \n", gradiant_analsyis_trials);
	fprintf(stream, "# learn_trials_dyn_sys = %i \n", learn_trials_dyn);
	fprintf(stream, "# learn_trials_no_dyn_sys = %i \n", learn_trials_no_dyn);
	fprintf(stream, "# trials_per_config = %i \n", trials_per_config);

	fprintf(stream, "# Simulation Parameters ****************\n");
	fprintf(stream, "# sig_p = %f \n", sig_p[config_no]);
	fprintf(stream, "# rsc = %f \n", rsc[config_no]);
	fprintf(stream, "# w_E = %f \n", w_E[config_no]);
	if (reward_func == GAUSSIAN){
		fprintf(stream, "# GAUSSIAN REWARD FUNCTION\n");
		fprintf(stream, "# sigma_R = %f \n", sigma_R[config_no]);
	}else{	//BOX
		fprintf(stream, "# BOX REWARD FUNCTION\n");
		fprintf(stream, "# sigma_R_box = %f \n", sigma_R_box[config_no]);
	}
	fprintf(stream, "# beta = %f \n", h_beta[config_no]);
	fprintf(stream, "# xsc = %f \n", h_xsc[config_no]);
	fprintf(stream, "# w0 = %f \n", h_w0[config_no]);
	fprintf(stream, "# u0 = %f \n", h_u0[config_no]);
	fprintf(stream, "# eta = %f \n", h_eta[config_no]);
	fprintf(stream, "# baseline = %f \n", h_baseline[config_no]);
	fprintf(stream, "# \n\n\n");

	fprintf(stream, "# Spiking Neuron Parameters ****************\n");
	fprintf(stream, "# dt = %f \n", dt);
	fprintf(stream, "# tau = %f \n", h_tau[config_no]);
	fprintf(stream, "# lambda = %f \n", h_lambda[config_no]);
	fprintf(stream, "# urest = %f \n", h_urest[config_no]);
	fprintf(stream, "# threshold = %f \n", h_threshold[config_no]);
	fprintf(stream, "# du = %f \n", h_du[config_no]);
	fprintf(stream, "# rho0 = %f \n", h_rho0[config_no]);
	fprintf(stream, "# ref = %f \n", h_ref[config_no]);

}

template<DYNAMICS dynamics>
void saveLearnCurveData(float* total_analysis_rewards)
{
	for (int f = 0; f< ind_configs; f++)
	{
		char filename[128];
		FILE* ar;
		if (dynamics == DYN_SYS)
			sprintf(filename, "%s/%s_average_reward(dyn_sys).dat", config_output_dir, config_output_prefix[f]);
		else //NO_DYN_SYS
			sprintf(filename, "%s/%s_average_reward(no_dyn_sys).dat", config_output_dir, config_output_prefix[f]);
		ar = fopen(filename, "w");
		if (ar == NULL){
			printf("Error: Can't open output file %s!\n", filename);
			exit(0);
		}

		printConfigurationToFile<dynamics>(ar, f);

		fprintf(ar, "# Average Reward Data ******************\n");
		fprintf(ar, "# Step\tMEAN_REWARD\tSTD DEVIATION\n");
		
		float average_total = 0.0f;
		float sd_total = 0.0f;
		//calculate averages
		for (int m=0; m<no_intval; m++)
		{
			float average_reward = 0;
			float std_dev = 0;

			//For each step of each config calculate the average accross the ind trials
			for(int i=0; i<trials_per_config; i++)
			{
				float* no_intval_reward = &total_analysis_rewards[m*ind_trials];
				float reward = no_intval_reward[f+(i*ind_configs)]/(float)analysis_trials;
				average_reward += reward;
			}
			average_reward /= (float)trials_per_config;

			//For each step of each config calculate the SD accross the ind trials
			for(int i=0; i<trials_per_config; i++)
			{
				float* no_intval_reward = &total_analysis_rewards[m*ind_trials];
				float reward = no_intval_reward[f+(i*ind_configs)]/(float)analysis_trials;
				std_dev += (average_reward - reward)*(average_reward - reward);
			}
			std_dev /= (float)trials_per_config;
			std_dev = sqrtf(std_dev);


			//print to data file with error bars
			fprintf(ar, "%i\t%f\t%f\n", m, average_reward, std_dev);

			average_total += average_reward;
			sd_total += std_dev;
		}

		average_total /= no_intval;
		sd_total /= no_intval;
		fprintf(ar, "# average over steps\n");
		fprintf(ar, "# %f\t%f\n", average_total, sd_total);

		//close the file
		fclose(ar);
	}
}

template<DYNAMICS dynamics>
void saveErrorCurveData(float* total_reward_errors)
{
	for (int f = 0; f< ind_configs; f++)
	{
		char filename[128];
		FILE* ar;
		if (dynamics == DYN_SYS)
			sprintf(filename, "%s/%s_reward_error(dyn_sys).dat", config_output_dir, config_output_prefix[f]);
		else //NO_DYN_SYS
			sprintf(filename, "%s/%s_reward_error(no_dyn_sys).dat", config_output_dir, config_output_prefix[f]);
		ar = fopen(filename, "w");
		if (ar == NULL){
			printf("Error: Can't open output file %s!\n", filename);
			exit(0);
		}

		printConfigurationToFile<dynamics>(ar, f);

		fprintf(ar, "# Reward Error Data ******************\n");
		fprintf(ar, "# Step\tMEAN_REWARD\tSTD DEVIATION\n");
		
		float error_total = 0.0f;
		float sd_total = 0.0f;
		//calculate averages
		for (int m=0; m<no_intval; m++)
		{
			float average_error = 0;
			float std_dev = 0;

			//For each step of each config calculate the average accross the ind trials
			for(int i=0; i<trials_per_config; i++)
			{
				float* no_intval_reward = &total_reward_errors[m*ind_trials];
				float error = no_intval_reward[f+(i*ind_configs)]/(float)analysis_trials;
				average_error += error;
			}
			average_error /= (float)trials_per_config;

			//For each step of each config calculate the SD accross the ind trials
			for(int i=0; i<trials_per_config; i++)
			{
				float* no_intval_reward = &total_reward_errors[m*ind_trials];
				float error = no_intval_reward[f+(i*ind_configs)]/(float)analysis_trials;
				std_dev += (average_error - error)*(average_error - error);
			}
			std_dev /= (float)trials_per_config;
			std_dev = sqrtf(std_dev);


			//print to data file with error bars
			fprintf(ar, "%i\t%f\t%f\n", m, average_error, std_dev);

			error_total += average_error;
			sd_total += std_dev;
		}

		error_total /= no_intval;
		sd_total /= no_intval;
		fprintf(ar, "# average over steps\n");
		fprintf(ar, "# %f\t%f\n", error_total, sd_total);

		//close the file
		fclose(ar);
	}
}

void createLearnCurveGraph(bool dyn_sys, bool no_dyn_sys)
{
	for (int f = 0; f< ind_configs; f++)
	{
		char filename[128];
		char dyn_sys_filename[128];
		char no_dyn_sys_filename[128];
		time_t rawtime;
		struct tm * timeinfo;
		FILE* ar;

		//file names
		sprintf(filename, "%s/%s_reward_error.plt", config_output_dir, config_output_prefix[f]);
		ar = fopen(filename, "w");
		if (ar == NULL){
			printf("Error: Can't open output file %s!\n", filename);
			exit(0);
		}

		//files used for graph input
		sprintf(dyn_sys_filename, "%s_reward_error(dyn_sys).dat", config_output_prefix[f]);
		sprintf(no_dyn_sys_filename, "%s_reward_error(no_dyn_sys).dat", config_output_prefix[f]);

		//output file header information
		time ( &rawtime );
		timeinfo = localtime ( &rawtime );
		fprintf(ar, "# Output from GPU model: %s", asctime(timeinfo));
		fprintf(ar, "# GNUPlot Script");
		fprintf(ar, "set title 'Average Reward Error Plot for %s'\n", config_output_prefix[f]);
		fprintf(ar, "set xlabel 'learning steps'\n");
		fprintf(ar, "set ylabel 'reward error'\n");

		//create plot
		if (dyn_sys && no_dyn_sys){
			fprintf(ar, "plot '%s' with lines, '%s'  with yerrorbars title 'SD accross ind trials', '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials'",	dyn_sys_filename, dyn_sys_filename, no_dyn_sys_filename, no_dyn_sys_filename);
		}
		else
		{
			if(dyn_sys)
				fprintf(ar, "plot '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials",	dyn_sys_filename, dyn_sys_filename, no_dyn_sys_filename, no_dyn_sys_filename);
			if(no_dyn_sys)
				fprintf(ar, "plot '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials", no_dyn_sys_filename, no_dyn_sys_filename);
		}
		fclose(ar);
	}

}


void createErrorCurveGraph(bool dyn_sys, bool no_dyn_sys)
{
	for (int f = 0; f< ind_configs; f++)
	{
		char filename[128];
		char dyn_sys_filename[128];
		char no_dyn_sys_filename[128];
		time_t rawtime;
		struct tm * timeinfo;
		FILE* ar;

		//file names
		sprintf(filename, "%s/%s_average_reward.plt", config_output_dir, config_output_prefix[f]);
		ar = fopen(filename, "w");
		if (ar == NULL){
			printf("Error: Can't open output file %s!\n", filename);
			exit(0);
		}

		//files used for graph input
		sprintf(dyn_sys_filename, "%s_average_reward(dyn_sys).dat", config_output_prefix[f]);
		sprintf(no_dyn_sys_filename, "%s_average_reward(no_dyn_sys).dat", config_output_prefix[f]);

		//output file header information
		time ( &rawtime );
		timeinfo = localtime ( &rawtime );
		fprintf(ar, "# Output from GPU model: %s", asctime(timeinfo));
		fprintf(ar, "# GNUPlot Script");
		fprintf(ar, "set title 'Average Reward Plot for %s'\n", config_output_prefix[f]);
		fprintf(ar, "set xlabel 'learning steps'\n");
		fprintf(ar, "set ylabel 'average reward'\n");

		//create plot
		if (dyn_sys && no_dyn_sys){
			fprintf(ar, "plot '%s' with lines, '%s'  with yerrorbars title 'SD accross ind trials', '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials'",	dyn_sys_filename, dyn_sys_filename, no_dyn_sys_filename, no_dyn_sys_filename);
		}
		else
		{
			if(dyn_sys)
				fprintf(ar, "plot '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials",	dyn_sys_filename, dyn_sys_filename, no_dyn_sys_filename, no_dyn_sys_filename);
			if(no_dyn_sys)
				fprintf(ar, "plot '%s' with lines, '%s' with yerrorbars title 'SD accross ind trials", no_dyn_sys_filename, no_dyn_sys_filename);
		}
		fclose(ar);
	}

}

template<DYNAMICS dynamics>
void saveGraphAnalysisData(float* h_sumGrad, float* h_sumDeltaW, int step)
{
	char step_str[128] = "";
	if (step > 0)
	{
		sprintf(step_str, "_step_%i", step);
	}
	for (int f = 0; f< ind_configs; f++)
	{
		//create and open files for writing mean gradiant and mean deltaW
		char mG_filename[128];
		char dW_filename[128];
		FILE* mG;
		FILE* dW;
		if (dynamics == DYN_SYS){
			sprintf(mG_filename, "%s/%s_gradiant(dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
			sprintf(dW_filename, "%s/%s_deltaW(dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
		}
		else{ //NO_DYN_SYS
			sprintf(mG_filename, "%s/%s_gradiant(no_dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
			sprintf(dW_filename, "%s/%s_deltaW(no_dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
		}
		mG = fopen(mG_filename, "w");
		if (mG == NULL){
			printf("Error: Can't open output file %s!\n", mG_filename);
			exit(0);
		}
		dW = fopen(dW_filename, "w");
		if (dW == NULL){
			printf("Error: Can't open output file %s!\n", dW_filename);
			exit(0);
		}

		//output file header information
		printConfigurationToFile<dynamics>(mG, f);
		printConfigurationToFile<dynamics>(dW, f);
		
		//loop through action cells
		for (int n=0; n<N; n++)
		{
			float avr_grad = 0.0f;	
			float avr_deltaW = 0.0f;	
			//average for each neuron connection (xM) and over the indipendant trials
			for(int i=0; i<trials_per_config; i++)
			{
				//offset for each indipendant trial
				int trial_offset = (f+(i*ind_configs))*M*N;
				for (int m=0; m<M; m++)
				{
					avr_grad += h_sumGrad[(n*M) + m + trial_offset];
					avr_deltaW += h_sumDeltaW[(n*M) + m + trial_offset];
				}
			}
			//average
			avr_grad /= M*trials_per_config;
			avr_deltaW /= M*trials_per_config;
			
			//print to data files
			fprintf(mG, "%i\t%f\n", n, avr_grad);
			fprintf(dW, "%i\t%f\n", n, avr_deltaW);
		}

		//close the files
		fclose(mG);
		fclose(dW);
	}
}

void createAnalysisGraphs(bool dyn_sys, bool no_dyn_sys, int step)
{
	char step_str[128] = "";
	if (step > 0)
	{
		sprintf(step_str, "_step_%i", step);
	}

	for (int f = 0; f< ind_configs; f++)
	{
		char mG_filename[128];
		char dW_filename[128];
		char dyn_sys_mG_filename[128];
		char no_dyn_sys_mG_filename[128];
		char dyn_sys_dW_filename[128];
		char no_dyn_sys_dW_filename[128];
		time_t rawtime;
		struct tm * timeinfo;
		FILE* mG;
		FILE* dW;

		//file names
		sprintf(mG_filename, "%s/%s_gradiant%s.plt", config_output_dir, config_output_prefix[f], step_str);
		sprintf(dW_filename, "%s/%s_deltaW%s.plt", config_output_dir, config_output_prefix[f], step_str);
		mG = fopen(mG_filename, "w");
		if (mG == NULL){
			printf("Error: Can't open output file %s!\n", mG_filename);
			exit(0);
		}
		dW = fopen(dW_filename, "w");
		if (dW == NULL){
			printf("Error: Can't open output file %s!\n", dW_filename);
			exit(0);
		}

		//files used for graph input
		sprintf(dyn_sys_mG_filename, "%s_gradiant(dyn_sys)%s.dat", config_output_prefix[f], step_str);
		sprintf(no_dyn_sys_mG_filename, "%s_gradiant(no_dyn_sys)%s.dat", config_output_prefix[f], step_str);
		sprintf(dyn_sys_dW_filename, "%s_deltaW(dyn_sys)%s.dat", config_output_prefix[f], step_str);
		sprintf(no_dyn_sys_dW_filename, "%s_deltaW(no_dyn_sys)%s.dat", config_output_prefix[f], step_str);

		//get time info
		time ( &rawtime );
		timeinfo = localtime ( &rawtime );

		//output file header infor for gradiant mean
		fprintf(mG, "# Output from GPU model: %s", asctime(timeinfo));
		fprintf(mG, "# GNUPlot Script");
		fprintf(mG, "# %i gradiant anyalysis trials", gradiant_analsyis_trials);
		fprintf(mG, "# %i indipendant trials per configuration", trials_per_config);
		fprintf(mG, "# Step %i of %i", step, no_intval);
		fprintf(mG, "set title 'Action Cells Mean Gradiant (before learning)%s'\n", config_output_prefix[f]);
		fprintf(mG, "set xlabel 'Action Cell Index (aligned by decision)'\n");
		fprintf(mG, "set ylabel 'mean gradiant'\n");

		//output file header infor for delta W
		fprintf(dW, "# Output from GPU model: %s", asctime(timeinfo));
		fprintf(dW, "# GNUPlot Script");
		fprintf(dW, "# %i gradiant anyalysis trials", gradiant_analsyis_trials);
		fprintf(dW, "# %i indipendant trials per configuration", trials_per_config);
		fprintf(dW, "set title 'Action Cells Delta W (before learning)%s'\n", config_output_prefix[f]);
		fprintf(dW, "set xlabel 'Action Cell Index (aligned by target angle and offset 180*)'\n");
		fprintf(dW, "set ylabel 'delta W'\n");

		//create plot
		if (dyn_sys && no_dyn_sys){
			fprintf(mG, "plot '%s' with lines, '%s' with lines",	dyn_sys_mG_filename, no_dyn_sys_mG_filename);
			fprintf(dW, "plot '%s' with lines, '%s' with lines",	dyn_sys_dW_filename, no_dyn_sys_dW_filename);
		}
		else
		{
			if(dyn_sys){
				fprintf(mG, "plot '%s' with lines", dyn_sys_mG_filename);
				fprintf(dW, "plot '%s' with lines", dyn_sys_dW_filename);
			}
			if(no_dyn_sys){
				fprintf(mG, "plot '%s' with lines", no_dyn_sys_mG_filename);
				fprintf(dW, "plot '%s' with lines", no_dyn_sys_dW_filename);
			}
		}

		//close files
		fclose(mG);
		fclose(dW);
	}

}


template<DYNAMICS dynamics>
void saveWeightData(float* h_W, int step)
{
char step_str[128] = "";
	if (step > 0)
	{
		sprintf(step_str, "_step_%i", step);
	}
	for (int f = 0; f< ind_configs; f++)
	{
		//create and open files for writing mean gradiant and mean deltaW
		char w_filename[128];
		FILE* w;
		if (dynamics == DYN_SYS){
			sprintf(w_filename, "%s/%s_W(dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
		}
		else{ //NO_DYN_SYS
			sprintf(w_filename, "%s/%s_W(no_dyn_sys)%s.dat", config_output_dir, config_output_prefix[f], step_str);
		}
		w = fopen(w_filename, "w");
		if (w == NULL){
			printf("Error: Can't open output file %s!\n", w_filename);
			exit(0);
		}

		//output file header information
		printConfigurationToFile<dynamics>(w, f);

		
		//loop through action cells
		for (int n=0; n<N; n++)
		{
			for (int m=0; m<M; m++)
			{
				float avr_w = 0;
				for(int i=0; i<trials_per_config; i++)
				{
					//offset for each indipendant trial
					int trial_offset = (f+(i*ind_configs))*M*N;

					avr_w += h_W[(n*M) + m + trial_offset];
				}
				avr_w /= trials_per_config;
				fprintf(w, "%i\t%i\t%f\n", n, m, avr_w);
			}
			fprintf(w, "\n");
		}

		//close the files
		fclose(w);
	}

}

void createWeightGraphs(bool dyn_sys, bool no_dyn_sys, int step)
{
	char step_str[128] = "";
	if (step > 0)
	{
		sprintf(step_str, "_step_%i", step);
	}

	for (int f = 0; f< ind_configs; f++)
	{
		char dyn_sys_w_plot_filename[128];
		char dyn_sys_w_filename[128];
		char no_dyn_sys_w_plot_filename[128];
		char no_dyn_sys_w_filename[128];
		time_t rawtime;
		struct tm * timeinfo;
		FILE* w_dyn_sys;
		FILE* w_no_dyn_sys;

		//get time info
		time ( &rawtime );
		timeinfo = localtime ( &rawtime );

		if(dyn_sys){

			//file names
			sprintf(dyn_sys_w_plot_filename, "%s/%s_W(dyn_sys)%s.plt", config_output_dir, config_output_prefix[f], step_str);
			w_dyn_sys = fopen(dyn_sys_w_plot_filename, "w");
			if (w_dyn_sys == NULL){
				printf("Error: Can't open output file %s!\n", dyn_sys_w_plot_filename);
				exit(0);
			}

			//files used for graph input
			sprintf(dyn_sys_w_filename, "%s_W(dyn_sys)%s.dat", config_output_prefix[f], step_str);

			//output file header infor for gradiant mean
			fprintf(w_dyn_sys, "# Output from GPU model: %s", asctime(timeinfo));
			fprintf(w_dyn_sys, "# GNUPlot Script");
			fprintf(w_dyn_sys, "# %i gradiant anyalysis trials", gradiant_analsyis_trials);
			fprintf(w_dyn_sys, "# %i indipendant trials per configuration", trials_per_config);
			fprintf(w_dyn_sys, "# Step %i of %i", step, no_intval);
			fprintf(w_dyn_sys, "set title 'Action Cell Weight: Step %i %s'\n", step, config_output_prefix[f]);
			fprintf(w_dyn_sys, "set xlabel 'N'\n");
			fprintf(w_dyn_sys, "set ylabel 'M'\n");	

			fprintf(w_dyn_sys, "plot '%s' with image", dyn_sys_w_filename);

			fclose(w_dyn_sys);

		}

		
		if(no_dyn_sys){
			sprintf(no_dyn_sys_w_plot_filename, "%s/%s_W(no_dyn_sys)%s.plt", config_output_dir, config_output_prefix[f], step_str);
			w_no_dyn_sys = fopen(no_dyn_sys_w_plot_filename, "w");
			if (w_no_dyn_sys == NULL){
				printf("Error: Can't open output file %s!\n", no_dyn_sys_w_plot_filename);
				exit(0);
			}

			sprintf(no_dyn_sys_w_filename, "%s_W(no_dyn_sys)%s.dat", config_output_prefix[f], step_str);

			fprintf(w_no_dyn_sys, "# Output from GPU model: %s", asctime(timeinfo));
			fprintf(w_no_dyn_sys, "# GNUPlot Script");
			fprintf(w_no_dyn_sys, "# %i gradiant anyalysis trials", gradiant_analsyis_trials);
			fprintf(w_no_dyn_sys, "# %i indipendant trials per configuration", trials_per_config);
			fprintf(w_no_dyn_sys, "# Step %i of %i", step, no_intval);
			fprintf(w_no_dyn_sys, "set title 'Action Cell Weight: Step %i %s'\n", step, config_output_prefix[f]);
			fprintf(w_no_dyn_sys, "set xlabel 'N'\n");
			fprintf(w_no_dyn_sys, "set ylabel 'M'\n");

			fprintf(w_no_dyn_sys, "plot '%s' with image", no_dyn_sys_w_filename);

			fclose(w_no_dyn_sys);
		}
	}

}

//template prototypes
template void saveLearnCurveData<NO_DYN_SYS>(float* total_analysis_rewards);
template void saveLearnCurveData<DYN_SYS>(float* total_analysis_rewards);

template void saveErrorCurveData<NO_DYN_SYS>(float* total_reward_errors);
template void saveErrorCurveData<DYN_SYS>(float* total_reward_errors);

template void saveGraphAnalysisData<NO_DYN_SYS>(float* h_sumGrad, float* h_sumDeltaW, int step);
template void saveGraphAnalysisData<DYN_SYS>(float* h_sumGrad, float* h_sumDeltaW, int step);

template void saveWeightData<NO_DYN_SYS>(float* h_W, int step);
template void saveWeightData<DYN_SYS>(float* h_W, int step);