// Built by Peter A Noble January 2019 Email: panoble2017@gmail.com
// Copyright 2019

#include <fstream>
#include <string>
#include <iostream>
#include <math.h>
#include <cstdlib>
#include <float.h>
#include <complex>
#include <iomanip>

// g++ sensitivity_analysis.cpp -o sensitivity_analysis
// ./sensitivity_analysis 83 9 weights.txt biases.txt combined_x.txt combined_y.txt out.txt

// the purpose is to determine the sensitivity of different inputs to outputs
// based on input data, weights and biases.

using namespace std;

int main (int argc, char * const argv[]) {
int inputs=0;
inputs=atoi(argv[1]);
int hidden=0;
hidden=atoi(argv[2]);
ifstream in(argv[3]); 
ifstream in2(argv[4]); 
ifstream in3(argv[5]); 
ifstream in4(argv[6]); 
	
ofstream out(argv[7]); 
int standard=100;
int num=10000;

char** code = new char*[num];
	for (int s = 0; s < num; s++)
		{		
		code[s] = new char[standard];
		strcpy(code[s],"test");
		}

double** wgts = new double*[inputs+1];
for (int s = 0; s < inputs+1; s++)		
	{
	wgts[s] = new double[hidden];
	for (int t = 0; t < hidden; t++)
		{
		wgts[s][t] = 0.0;
		}		
	}
	
double* array = new double[hidden];
for (int t = 0; t < hidden; t++)
	{
	array[t]=0.0;			
	}

double* biases= new double[hidden+1];
for (int t = 0; t < (hidden+1); t++)
	{
	biases[t] = 0.0;
	}		

double** var = new double*[inputs+1];
	for (int s = 0; s < inputs+1; s++)
		{
		var[s] = new double[num];
		for (int t = 0; t < num; t++)
			{
			var[s][t]=0.0;
			}
		}

double** matrix = new double*[inputs+1];
	for (int s = 0; s < inputs+1; s++)
		{
		matrix[s] = new double[num];
		for (int t = 0; t < num; t++)
			{
			matrix[s][t]=0.0;
			}
		}

/*
double* var= new double[inputs];
for (int t = 0; t < (inputs); t++)
	{
	var[t] = 0.0;
	}		
*/

int number=0;
int s=0,i=0;
double output=0.0;
double actual=0.0;
double max=1.0;
double min=0.0;
char bs[standard];
int predict=0;
int count=0;
//int total_count=0;
int t1=0;

in >> number;
for (int s = 0; s < hidden; s++) // gets the weights of the inner neurons
	{
	for (int t = 0; t < inputs; t++) 	
		{
		 in >> wgts[t][s];
		}
	}
//cout << wgts[inputs-1][hidden-1] << "\n" << flush; exit(1);


for (int s = 0; s < hidden; s++) // gets the weights of the outer neuron
	{
	in >> wgts[inputs][s];
	}
//cout << wgts[inputs][hidden-1] << "\n" << flush; exit(1);

for (int d = 0; d < (hidden+1); d++)
	{
	in2 >> biases[d]; 
	}

//cout << biases[hidden] << "\n" << flush; exit(1);

// input headers
for (int s = 0; s < inputs; s++)
	{
	in3 >> code[s];
	}
in4 >> bs;
//
		
while(!in3.eof())
	{
	for (int s = 0; s < inputs; s++)
		{
		in3 >> matrix[s][t1];
		}
	in4 >> matrix[inputs][t1];
	t1=t1+1;
	}

//original data
//move data from matrix to var
for (int a = 0; a < t1; a++)
	{
	for (int s = 0; s < inputs+1; s++)
		{	
		var[s][a]=matrix[s][a];
		}
	}

for (int a = 0; a < t1; a++)
	//for (int a = 0; a < 2; a++)
	{
	output=0.0;
	for (int s = 0; s < hidden; s++)
		{		
		for (int t = 0; t < inputs; t++)
			{		
			array[s]=(var[t][a]*wgts[t][s])+array[s];			
			}
		array[s]=double(double(1)/double(1+exp(-1*(array[s]+biases[s]))));
		}
	for (int s = 0; s < hidden; s++)
		{	
		output=((array[s]*wgts[inputs][s])+output);	
		}

		output=(output+biases[hidden]);
		output=(output * (max-min)) + min;	
		predict = (int)round(output);
		actual=int(var[inputs][a]);

		if (predict==actual)
			{
			count=count+1;
			}
		//cout <<  predict << "\t" << actual << "\n";; //exit(1);
		//out << predict << "\t" << actual << "\n";; //exit(1);
	}
	output=double(double(count)/t1);
	cout << "Original\t" << count << "\t" << t1 << "\t" << output << "\n";
	out << "Original\t\t" << count << "\t" << t1 << "\t" << output << "\n";

// sensitivity analysis below	
for (int x = 0; x < inputs; x++)
	{
	for (int y = 0; y < 2; y++)
		{
		output=0.0;
		actual=0.0;
		predict=0;
		count=0;
		
	//move data from matrix to var
		for (int a = 0; a < t1; a++)
			{
			for (int s = 0; s < inputs+1; s++)
				{	
				var[s][a]=matrix[s][a];
				}
			}

	// select one column and assign min or max
		for (int a = 0; a < t1; a++)
			{
			var[x][a]=double(y);
			}
	//
	
		for (int a = 0; a < t1; a++)
		//for (int a = 0; a < 2; a++)
			{
			output=0.0;
			for (int s = 0; s < hidden; s++)
				{		
				for (int t = 0; t < inputs; t++)
					{		
				//cout << var[t] << "\t" << wgts[t][s] << "\t";
					array[s]=(var[t][a]*wgts[t][s])+array[s];			
					}
				array[s]=double(double(1)/double(1+exp(-1*(array[s]+biases[s]))));
		//		array[s]=(array[s]+biases[s]);
				}

		// cout << array[0] << "\t"<< array[1] << "\t"<< array[2] << "\n" << flush; exit(1);
			for (int s = 0; s < hidden; s++)
				{	
				output=((array[s]*wgts[inputs][s])+output);	
				}
		//cout <<  biases[hidden] << "\t"; //exit(1);		
			output=(output+biases[hidden]);
		//output=double(1)/double(1+exp(-1*((output))));	 
			output=(output * (max-min)) + min;	
			predict = (int)round(output);
			actual=int(var[inputs][a]);
	
			if (predict==actual)
				{
				count=count+1;
				}
			//cout <<  predict << "\t" << actual << "\n";; //exit(1);
			//out << predict << "\t" << actual << "\n";; //exit(1);
			}
			output=double(double(count)/t1);
			cout << code[x] << "\t" << y << "\t" << count << "\t" << t1 << "\t" << output << "\n";
			out << code[x] << "\t" << y << "\t" << count << "\t" << t1 << "\t" << output << "\n";
	}
}
  return 0;
}
