#include "project.h"
#include <iostream>
using namespace std;
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iomanip>
#include <cstring>
#include <sstream>
#include <fstream>
#include <stdlib.h>
 

double energy(project);
double prob(double,double,double);
project neighbour(project,double[],int);
double temp(int,int);
double randomNum();
int randomGenerateInt(int,int);
double randomGenerateDouble(double,double);
project initproject(int);
void outStats(project);

////general case
//double attrange[2][40]={
//{1,1,1,1,1,1,1,1,3,3,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,0.0344,-0.0912,0.0784,0.0504,-0.0960,0.1104,0.0504,-0.0996,0.0976,-1.5288,0.0896,-0.1188,3.72,0.85,100},
//{6,6,6,5,5,5,5,5,6,6,5,5,6,5,5,5,5,5,5,5,5,5,5,6,6,0.0516,-0.0608,0.1176,0.0756,-0.0640,0.1656,0.0756,-0.0664,0.1464,-1.0192,0.1344,-0.0792,9.18,1.09,125}
//};

//case 1: Ares
double attrange[2][40]={
{1,1,1,3,1,1,1,4,3,3,2,2,2,1,3,1,1,1,1,1,2,1,2,1,1,0.0344,-0.0912,0.0784,0.0504,-0.0960,0.1104,0.0504,-0.0996,0.0976,-1.5288,0.0896,-0.1188,3.72,0.85,100},
{6,6,6,5,5,5,5,5,6,6,5,5,6,5,4,5,5,5,5,5,5,5,4,6,6,0.0516,-0.0608,0.1176,0.0756,-0.0640,0.1656,0.0756,-0.0664,0.1464,-1.0192,0.1344,-0.0792,9.18,1.09,125}
};

////case 2: KC1
//double attrange[2][40]={
//{1,1,1,1,2,1,2,1,3,3,2,2,2,1,2,2,1,2,2,1,2,2,1,5,1,0.0344,-0.0912,0.0784,0.0504,-0.0960,0.1104,0.0504,-0.0996,0.0976,-1.5288,0.0896,-0.1188,3.72,0.85,100},
//{6,6,6,2,5,3,3,4,6,5,5,5,4,5,4,3,5,3,3,5,4,3,3,6,6,0.0516,-0.0608,0.1176,0.0756,-0.0640,0.1656,0.0756,-0.0664,0.1464,-1.0192,0.1344,-0.0792,9.18,1.09,125}
//};


const double cocomoMax = 569279;
const double cocomoMin = 3.44918;
const double coqualmoMax = 82324.8;
const double coqualmoMin = 7.99808;
const double threatMax = 54.6917;
const double threatMin = 0.0;

int main(int argc, char* argv[]) {
	
	double bore = 0.1;
	
	string log ("");
	if (argc==3) {
		bore = atof(argv[2]);
		if (bore >= 1.0) bore = 0.1;
		log.append(argv[1]);
		if (log =="-l") cout << "Log output enabled and bore is " << bore << endl;
		else cout << "Log output disabled and bore is " << bore << endl;
	}
	else if (argc==2) {
		log.append(argv[1]); 
		if (log =="-l") cout << "Log output enabled and bore is " << bore << endl;
		else cout << "Log output disabled and bore is " << bore << endl;
	}	
	else cout << "Log output disabled and bore is " << bore << endl;
	
	srand((unsigned)time(0));
		
	project x;
	
	x=initproject(1);
	
	
////Debug block	
//	outStats(x);

//	for (int i=0; i<25; i++) {
//			x.displayAtt(i);
//	}
	
	double initattset[40];
	
	for (int i=0; i<40; i++)
		initattset[i] = x.attributes[i];
	
	double e,eb,en;
	project s,sb,sn;
	int attNumUndefined=0;
	
	s=x;
	
	for (int i=0; i<25; i++) {
		if (s.attributes[i]==0) {
			s.setattnum(i,randomGenerateInt((int)attrange[0][i],(int)attrange[1][i]));
			attNumUndefined++;
		}
	}
	for (int i=25; i<40; i++) {
		s.setattnum(i,randomGenerateDouble(attrange[0][i],attrange[1][i]));
	}
	
	string htmp (getenv("HOME"));	
	string hmkdir ("");
	
	hmkdir += "mkdir -p " + htmp + "/tmp/xomo-sa/";
	system(hmkdir.c_str());
	
	string hlog (htmp + "/tmp/xomo-sa/xomo-sa.csv");
			
	ofstream outfile (hlog.c_str(), ios::app);
	
	if (log == "-l") {
		outfile.close();
		outfile.open(hlog.c_str(), ios::out);
		if (!outfile) { cout << "Unable to open log file... exiting..." << endl; exit(1); }
		
		for (int i=0; i<40; i++) {
			outfile << s.displayAttName(i) << ",";
		}	
		outfile << "Energy\n";
	}
	else {
		outfile.close();
	}
		
	
	e = energy(s);
	
	const int kmax = 100000;
	const double emax = 0.0; 
	int k = 0;
	
	cout << "Initial Energy: " << e << endl;
	
	sb = s;
	eb = e;
	k = 0;
	
	double minE = 100000;
	double maxE = 0;
	
	while (k < kmax && e > emax) {
		sn = neighbour(s, initattset, (int)ceil((double)attNumUndefined/3));
		en = energy(sn);
		
		if (en < eb) {
			sb = sn;
			eb = en;
			s = sn;
			e = en;
			
			if (en < minE)
				minE = en;
			if (en > maxE)
				maxE = en;
			
			if (log == "-l") {
				for (int i=0; i<40; i++) {
					outfile << sn.attributes[i] << ",";
				}
				outfile << en << "\n";		
			}	
		}
		else if (randomNum() < prob(e, en, temp(k,kmax))) {
			s = sn;
			e = en;
			
			if (en < minE)
				minE = en;
			if (en > maxE)
				maxE = en;
			
			if (log == "-l") {
				for (int i=0; i<40; i++) {
					outfile << sn.attributes[i] << ",";
				}
				outfile << en << "\n";
			}
		}
		else;
		k++;
	}
	
	cout << "Here is the final output for the unset values:" << endl;
	for (int i=0; i<25; i++) {
		if (initattset[i]==0) {
			sb.displayAtt(i);
		}
	}
	for (int i=25; i<40; i++) {
			sb.displayAtt(i);
	}
	cout << "Final Energy: " << energy(sb) << endl;
	cout << "Max Energy: " << maxE << endl << "Min Energy: " << minE << endl;
	
	outfile.close();
	
	double finalattset[40];
	
	for (int i=0; i<40; i++)
		finalattset[i] = sb.attributes[i];
		
	if (log=="-l") {
	
		//class bore and continuous var 10 bins
		
		int numBest = 0;
		int numRest = 0;
		int discreteAttBest [6][25] = {{0},{0},{0},{0},{0},{0}};
		int discreteAttRest [6][25] = {{0},{0},{0},{0},{0},{0}};
		int contAttBest [10][15] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
		int contAttRest [10][15] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
		
		
		ifstream infile (hlog.c_str(), ios::in);
		string descLog (htmp + "/tmp/xomo-sa/xomo-sa-decrete.csv");
		outfile.open (descLog.c_str(), ios::out);
		
		string sbuff("");
		char cbuff [20];
		
		infile >> sbuff;		
		outfile << sbuff;
		
		while(!infile.eof()) {
			
			int Att [40] = {0};
		
			for (int i=0; i<25; i++) {
				infile.getline(cbuff, 15, ',');
				sbuff.clear();
				sbuff.append(cbuff);		
				if (sbuff=="") break;
				
				outfile << sbuff << ",";
				Att[i] = atoi(sbuff.c_str());
			}
			
			for (int i=25; i<40; i++) {
				infile.getline(cbuff, 15, ',');
				sbuff.clear();
				sbuff.append(cbuff);
				if (sbuff=="") break;
				
				double dbuff = atof(sbuff.c_str());
				dbuff = (dbuff-attrange[0][i])/(attrange[1][i]-attrange[0][i]);
				dbuff = ceil(dbuff*10);
						
				outfile << (int)dbuff << ",";
				Att[i] = (int)dbuff;
			}
			
			infile.getline(cbuff,15);
			sbuff.clear();
			sbuff.append(cbuff);
			if (sbuff=="") break;
			
			double dbuff = atof(sbuff.c_str());
			dbuff = (dbuff-minE)/(maxE-minE);	
			
			if (dbuff<=bore) {
				outfile << "1\n"; 
				numBest++;
				for(int i=0; i<25; i++) discreteAttBest [Att[i]-1][i]++;
				for(int i=25; i<40; i++) contAttBest [Att[i]-1][i-25]++;
			}
			else {
				outfile << "0\n";
				numRest++;
				for(int i=0; i<25; i++) discreteAttRest [Att[i]-1][i]++;
				for(int i=25; i<40; i++) contAttRest [Att[i]-1][i-25]++;
			}	
		}
		
		outfile.close();
		
		//end dicretization
		
		//scoring of the attributes
		double discreteAttScore [6][25] = {{0},{0},{0},{0},{0},{0}};
		double contAttScore [10][15] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
		
		for(int i=0; i<25; i++) {
			for (int j=0; j<6; j++) {
				double LBest = ((double)discreteAttBest [j][i]/(double)numBest)*((double)numBest/(double)(numBest+numRest));
				double LRest = ((double)discreteAttRest [j][i]/(double)numRest)*((double)numRest/(double)(numBest+numRest));
				if ((LBest+LRest)==0) discreteAttScore [j][i] = 0;
				else discreteAttScore [j][i] = pow(LBest,2)/(LBest+LRest);
			}
		}
		
		for(int i=0; i<15; i++) {
			for (int j=0; j<10; j++) {
				double LBest = ((double)contAttBest [j][i]/(double)numBest)*((double)numBest/(double)(numBest+numRest));
				double LRest = ((double)contAttRest [j][i]/(double)numRest)*((double)numRest/(double)(numBest+numRest));
				if ((LBest+LRest)==0) contAttScore [j][i] = 0;
				else contAttScore [j][i] = pow(LBest,2)/(LBest+LRest);
			}
		}
		
		//end scoring
		
		
		// Simulations
		
		string simLog (htmp + "/tmp/xomo-sa/simlog.csv");
		outfile.open (simLog.c_str(), ios::out);
		
		int thrownAtt[6][25] = {{0},{0},{0},{0},{0},{0}};	
		
		cout << "Attribute,value,score,avgE,sdE,avgEffort,sdEffort,avgDefects,sdDefects,avgThreat,sdThreat\n";

		outfile << "Attribute,value,score,avgE,sdE,avgEffort,sdEffort,avgDefects,sdDefects,avgThreat,sdThreat\n";
		
		for (int k=0; k<(attNumUndefined*6); k++) {
		
			double minScore = 100000;
			int minAtt;
			int minAttVal;
			
				
			for (int i=0; i<25; i++) {
				for (int j=0; j<6; j++) {
					if (initattset[i] == 0 && thrownAtt[j][i] == 0 && discreteAttScore [j][i] < minScore) {
						minScore = discreteAttScore [j][i];
						minAtt=i;
						minAttVal=j;
					}
				}
			}
			
			thrownAtt[minAttVal][minAtt] = 1;
			
			double avgE = 0;
			double sdE = 0;
			
			double avgEffort = 0;
			double sdEffort = 0;
			
			double avgDefects = 0;
			double sdDefects = 0;
			
			double avgThreat = 0;
			double sdThreat = 0;
			
			for (int simn=0; simn<100; simn++) {
				project sim = sb;
				for (int i=0; i<25; i++) {
					for (int j=0; j<6; j++) {
						if (thrownAtt[j][i]==1) {
							if(thrownAtt[0][i]==1 && thrownAtt[1][i]==1 && thrownAtt[2][i]==1 && thrownAtt[3][i]==1 && thrownAtt[4][i]==1 && thrownAtt[5][i]==1) {
								sim.setattnum(i,randomGenerateInt((int)attrange[0][i],(int)attrange[1][i]));
							}
							else {
								int simrnum;
								while (true) {
									simrnum = randomGenerateInt((int)attrange[0][i],(int)attrange[1][i]);
									if (thrownAtt[simrnum-1][i]!=1) break;
								}
								sim.setattnum(i,simrnum);							
							}					
						}
					}
				}
				for (int l=25; l<40; l++) {
						sim.setattnum(l,randomGenerateDouble(attrange[0][l],attrange[1][l]));
				}
				
				double tmpE = energy(sim);
				double tmpEffort = sim.effort();
				double tmpDefects = sim.defects();
				double tmpThreat = sim.threat();
				
				avgE += tmpE;	
				sdE += tmpE*tmpE;
				
				avgEffort += tmpEffort;	
				sdEffort += tmpEffort*tmpEffort;
				
				avgDefects += tmpDefects;	
				sdDefects += tmpDefects*tmpDefects;
				
				avgThreat += tmpThreat;	
				sdThreat += tmpThreat*tmpThreat;		
			}
			
			
			avgE = avgE/100;
			sdE = sqrt(fabs((sdE/100)-(avgE*avgE)));
			
			avgEffort = avgEffort/100;
			sdEffort = sqrt(fabs((sdEffort/100)-(avgEffort*avgEffort)));
			
			avgDefects = avgDefects/100;
			sdDefects = sqrt(fabs((sdDefects/100)-(avgDefects*avgDefects)));
			
			avgThreat = avgThreat/100;
			sdThreat = sqrt(fabs((sdThreat/100)-(avgThreat*avgThreat)));
			
			
			if (discreteAttScore [minAttVal][minAtt] != 0) {
				cout << x.displayAttName(minAtt) << ","
					<< (minAttVal+1) << "," << discreteAttScore [minAttVal][minAtt]
					<< "," << avgE << "," << sdE << "," << avgEffort 
					<< "," << sdEffort << "," << avgDefects << "," << sdDefects 
					<< "," << avgThreat << "," << sdThreat << "\n";

				outfile << x.displayAttName(minAtt) << ","
					<< (minAttVal+1) << "," << discreteAttScore [minAttVal][minAtt]
					<< "," << avgE << "," << sdE << "," << avgEffort 
					<< "," << sdEffort << "," << avgDefects << "," << sdDefects 
					<< "," << avgThreat << "," << sdThreat << "\n";
			}
		}
		
		outfile.close();
	
	}
	
	return 0; 
}

double energy(project x) {
	double E = (x.effort()-cocomoMin)/(cocomoMax-cocomoMin);
	double D = (x.defects()-coqualmoMin)/(coqualmoMax-coqualmoMin);
	double T = (x.threat()-threatMin)/(threatMax-threatMin);	
	
	return sqrt(pow(E,2)+pow(D,2)+pow(T,2));
}

double prob(double e, double en, double T) {
	return (double)exp((e-en)/T);
}

project neighbour(project s, double initattset[], int numtochange) {
	
	int numdone=0;
	
	while (numdone<numtochange) {
		int i=randomGenerateInt(0,39);
		if (i<25 && initattset[i]==0) {
			s.setattnum(i,randomGenerateInt((int)attrange[0][i],(int)attrange[1][i]));
			numdone++;
		}
		else if (i>=25) {
			s.setattnum(i,randomGenerateDouble(attrange[0][i],attrange[1][i]));
			numdone++;
		}
	}	
	return s;
}

double temp(int k, int kmax) {
	return (double)exp(-100*k/kmax);
}

int randomGenerateInt (int min, int max) {
	return (int)(rand()%(max-min+1) + min);
}

double randomGenerateDouble (double min, double max) {
	return (double)(rand()%((int)(10000*max-10000*min+1)) + 10000*min)/10000.0;
}

double randomNum() {
	return (double)(rand()%101)/100;
}

project initproject (int p) {
	project init;
	
	switch (p) {
		case 1:
		//case1: Ares
	//	init.sprec (0);
		init.sflex (3);
		init.sresl (4);
		init.steam (3);
	//	init.spmat (0);
		
		init.stime (3);
		init.sstor (3);
		init.sdata (4);
		init.spvol (3);
		init.sruse (4);
		init.srely (5);
	//	init.sdocu (0);
		init.sacap (4);
		init.spcap (3);
		init.spcon (3);
		init.sapex (4);
	//	init.sltex (0);
		init.stool (5);
	//	init.ssced (0);
		init.scplx (4);
		init.ssite (6);
		init.splex (4);
		
	//	init.sautomated_analysis (0);
	//	init.speer_reviews (0);
	//	init.sexecution_testing_and_tools (0);
		break;
		
		case 2:	
		//case2: KC1
	//	init.sprec (0);
	//	init.sflex (0);
	//	init.sresl (0);
	//	init.steam (0);
	//	init.spmat (0);
		
	//	init.stime (0);
	//	init.sstor (0);
		init.sdata (3);
		init.spvol (2);
	//	init.sruse (0);
		init.srely (5);
	//	init.sdocu (0);
	//	init.sacap (0);
		init.spcap (3);
	//	init.spcon (0);
	//	init.sapex (0);
	//	init.sltex (0);
	//	init.stool (0);
	//	init.ssced (0);
	//	init.scplx (0);
		init.ssite (3);
		init.splex (3);
		
	//	init.sautomated_analysis (0);
	//	init.speer_reviews (0);
	//	init.sexecution_testing_and_tools (0);
		break;
		
		
		//used for min/max calculations
		case 3: 
		//CocomoMax
		init.sprec (0);
		init.sflex (0);
		init.sresl (0);
		init.steam (0);
		init.spmat (0);
		
		init.stime (6);
		init.sstor (6);
		init.sdata (6);
		init.spvol (6);
		init.sruse (6);
		init.srely (6);
		init.sdocu (6);
		init.sacap (0);
		init.spcap (0);
		init.spcon (0);
		init.sapex (0);
		init.sltex (0);
		init.stool (0);
		init.ssced (6);
		init.scplx (6);
		init.ssite (0);
		init.splex (0);
		
		init.sautomated_analysis (0);
		init.speer_reviews (0);
		init.sexecution_testing_and_tools (0);
		break;
		
		case 4: 
		//CocomoMin
		init.sprec (6);
		init.sflex (6);
		init.sresl (6);
		init.steam (6);
		init.spmat (6);
		
		init.stime (0);
		init.sstor (0);
		init.sdata (0);
		init.spvol (0);
		init.sruse (0);
		init.srely (0);
		init.sdocu (0);
		init.sacap (6);
		init.spcap (6);
		init.spcon (6);
		init.sapex (6);
		init.sltex (6);
		init.stool (6);
		init.ssced (0);
		init.scplx (0);
		init.ssite (6);
		init.splex (6);
		
		init.sautomated_analysis (6);
		init.speer_reviews (6);
		init.sexecution_testing_and_tools (6); 
		break;
		
		case 5: 
		//CoqualmoMax
		init.sprec (0);
		init.sflex (0);
		init.sresl (0);
		init.steam (0);
		init.spmat (0);
		
		init.stime (6);
		init.sstor (6);
		init.sdata (6);
		init.spvol (6);
		init.sruse (6);
		init.srely (0);
		init.sdocu (0);
		init.sacap (0);
		init.spcap (0);
		init.spcon (0);
		init.sapex (0);
		init.sltex (0);
		init.stool (0);
		init.ssced (0);
		init.scplx (6);
		init.ssite (0);
		init.splex (0);
		
		init.sautomated_analysis (0);
		init.speer_reviews (0);
		init.sexecution_testing_and_tools (0); 
		break;
		
		case 6: 
		//CoqualmoMin
		init.sprec (6);
		init.sflex (6);
		init.sresl (6);
		init.steam (6);
		init.spmat (6);
		
		init.stime (0);
		init.sstor (0);
		init.sdata (0);
		init.spvol (0);
		init.sruse (0);
		init.srely (6);
		init.sdocu (6);
		init.sacap (6);
		init.spcap (6);
		init.spcon (6);
		init.sapex (6);
		init.sltex (6);
		init.stool (6);
		init.ssced (6);
		init.scplx (0);
		init.ssite (6);
		init.splex (6);
		
		init.sautomated_analysis (6);
		init.speer_reviews (6);
		init.sexecution_testing_and_tools (6); 
		break;
		
		case 7: 
		//ThreatMax
		init.sprec (0);
		init.sflex (0);
		init.sresl (0);
		init.steam (0);
		init.spmat (0);
		
		init.stime (6);
		init.sstor (6);
		init.sdata (0);
		init.spvol (6);
		init.sruse (6);
		init.srely (6);
		init.sdocu (0);
		init.sacap (0);
		init.spcap (0);
		init.spcon (0);
		init.sapex (0);
		init.sltex (0);
		init.stool (0);
		init.ssced (0);
		init.scplx (6);
		init.ssite (0);
		init.splex (0);
		
		init.sautomated_analysis (0);
		init.speer_reviews (0);
		init.sexecution_testing_and_tools (0);
		break;
		
		case 8:
		//ThreatMin
		init.sprec (6);
		init.sflex (6);
		init.sresl (6);
		init.steam (6);
		init.spmat (6);
		
		init.stime (0);
		init.sstor (0);
		init.sdata (6);
		init.spvol (0);
		init.sruse (0);
		init.srely (0);
		init.sdocu (6);
		init.sacap (6);
		init.spcap (6);
		init.spcon (6);
		init.sapex (6);
		init.sltex (6);
		init.stool (6);
		init.ssced (6);
		init.scplx (0);
		init.ssite (6);
		init.splex (6);
		
		init.sautomated_analysis (6);
		init.speer_reviews (6);
		init.sexecution_testing_and_tools (6);
		break;
	}
	
	return init;
}

void outStats(project x) {
	cout << "\nCocomoII: " << x.effort() << endl
	<< "Coqualmo: " << x.defects() << endl 
	<< "Threat: " << x.threat() << endl;
}
