//#  eulerN -- program for statistical estimation of Euler characteristic of a 3-dimensional body
//#  given as a sufficiently dense "volumetric" point sampling
//#
//#  Copyright (c) 2005-2007 Bradford Jones, Konstantin Rybnikov, Karen Daniels, Daniel Klain
//#  Release 2: 1 June 2007 
//#
//#  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, or (at your option) any
//#  later version: http://www.gnu.org/licenses/gpl.txt.
//#
//#  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.
//#-----------------------------------------------------------------------------

#include <iostream>
#include <vector>
#include <math.h>
#include <fstream>
#include <algorithm>
#include <string>


/* Files included for inclusion / exclusion   */
#include "tokenizer.h"
#include "math_vector.h"
#include "EulerIE.h"

/* Files included for sweep algorithm    */
#include "EulerSweep.h"

/* Files included for 2d Reeb graph algorithm */
#include "Euler2dreeb.h"

using namespace std;

vector<d3point> read3dfile(const char *filename)
{
	vector<d3point> vectorPoints;
	ifstream inputFile(filename);
	char string[256], *token;
	char seps[] = " ,";

	if (!inputFile)
	{
		cout << "Error: could not open inputfile" << endl;
		exit(0);
	}
	while (inputFile.getline(string, 256))
	{
		token = strtok(string, seps);
		double xcoor = atof(token);
		token = strtok(NULL, seps);
		double ycoor = atof(token);
		token = strtok(NULL, seps);
		double zcoor = atof(token);
		vectorPoints.push_back(d3point(xcoor, ycoor, zcoor));
	}
	return vectorPoints;
}
//reads in 2dimensional graph from file
vector<d2point> read2dfile(const char *filename)
{
	vector<d2point> vectorPoints;
	ifstream inputFile(filename);
	char string[256], *token;
	char seps[] = " ,";

	if (!inputFile)
	{
		cout << "Error: could not open inputfile" << endl;
		exit(0);
	}
	while (inputFile.getline(string, 256))
	{
		token = strtok(string, seps);
		double xcoor = atof(token);
		token = strtok(NULL, seps);
		double ycoor = atof(token);
		vectorPoints.push_back(d2point(xcoor, ycoor));
	}
	return vectorPoints;
}
vector<double> read1dfile(const char *filename)
{
	vector<double> vectorPoints;
	ifstream inputFile(filename);
	char string[256], *token;
	char seps[] = " ,";

	if (!inputFile)
	{
		cout << "Error: could not open inputfile" << endl;
		exit(0);
	}
	while (inputFile.getline(string, 256))
	{
		token = strtok(string, seps);
		double coor = atof(token);
		vectorPoints.push_back(coor);
	}
	return vectorPoints;
}
double squaredDistance(d3point a, d3point b)
{
	return ( (a.xcoord() - b.xcoord())*(a.xcoord() - b.xcoord()) +
			 (a.ycoord() - b.ycoord())*(a.ycoord() - b.ycoord()) +
			 (a.zcoord() - b.zcoord())*(a.zcoord() - b.zcoord()) );
}
double squaredDistance(d2point a, d2point b)
{
	return ( (a.xcoord() - b.xcoord())*(a.xcoord() - b.xcoord()) +
			 (a.ycoord() - b.ycoord())*(a.ycoord() - b.ycoord()) );
}
double medianDistanceToNearestNeighbour(vector<d3point> &pointcloud)
{
	if (pointcloud.size() < 2)
		return 0;

	vector<double> squaredDistances;
	double min;
	vector<double> temp;
	for (int i = 0; i < pointcloud.size(); i++)
	{
		for (int j = 0; j < pointcloud.size(); j++)
		{
			if (j != i)
			{
				temp.push_back(squaredDistance(pointcloud[i], pointcloud[j]));
			}
		}
		min = temp[0];
		for (int j = 0; j < temp.size(); j++)
		{
			if (temp[j] < min)
				min = temp[j];
		}
		squaredDistances.push_back(min);
		temp.clear();
	}
	sort(squaredDistances.begin(), squaredDistances.end());

	if (squaredDistances.size() % 2 == 0)
		return (( sqrt(squaredDistances[squaredDistances.size()/2 -1]) + sqrt(squaredDistances[(squaredDistances.size()/2)]))/2);
	else
		return sqrt(squaredDistances[(squaredDistances.size()/2)]);
}
double medianDistanceToNearestNeighbour(vector<d2point> &pointcloud)
{
	if (pointcloud.size() < 2)
		return 0;

	vector<double> squaredDistances;
	double min;
	vector<double> temp;
	for (int i = 0; i < pointcloud.size(); i++)
	{
		for (int j = 0; j < pointcloud.size(); j++)
		{
			if (j != i)
			{
				temp.push_back(squaredDistance(pointcloud[i], pointcloud[j]));
			}
		}
		min = temp[0];
		for (int j = 0; j < temp.size(); j++)
		{
			if (temp[j] < min)
				min = temp[j];
		}
		squaredDistances.push_back(min);
		temp.clear();
	}
	sort(squaredDistances.begin(), squaredDistances.end());

	if (squaredDistances.size() % 2 == 0)
		return (( sqrt(squaredDistances[squaredDistances.size()/2 -1]) + sqrt(squaredDistances[(squaredDistances.size()/2)]))/2);
	else
		return sqrt(squaredDistances[(squaredDistances.size()/2)]);
}
double medianDistanceToNearestNeighbour(vector<double> &pointcloud)
{
	vector<double> temp = pointcloud;
	sort(temp.begin(), temp.end());
	vector<double> distances;

	double a;
	double b;

	distances.push_back(temp[1] - temp[0]);
	for (int i = 1; i < temp.size() -1; i++)
	{
		a = temp[i] - temp[i-1];
		b = temp[i+1] - temp[i];
		if (a < b)
			distances.push_back(a);
		else
			distances.push_back(b);
	}
	distances.push_back(temp[temp.size() -1] - temp[temp.size() - 2]);

	sort(distances.begin(), distances.end());
	if (distances.size() % 2 == 0)
		return ((distances[distances.size()/2 -1] + distances[(distances.size()/2)])/2);
	else
		return distances[(distances.size()/2)];
}

// this will create a new default_parameters.cfg file
void generateDefaultsFile()
{
	ofstream outputFile("default_parameters.cfg");
	outputFile << "overlap2d .5" << endl;
	outputFile << "overlap3d .5" << endl;
	outputFile << "numdirections2d 10" << endl;
	outputFile << "numdirections3d 10" << endl;
	outputFile << "d2algorithm 0" << endl;
	outputFile << "2dcustomdir 0" << endl;
	outputFile << "3dcustomdir 0" << endl;
	outputFile << "T 4000000000" << endl;
	outputFile << "tau 0" << endl;
	outputFile << "factor 10" << endl;
	outputFile << "lambda 0" << endl;
	outputFile << "hard_approach 1" << endl;
	outputFile.close();
}
struct TestPiece
{
	vector<string> code;
};
bool verifyTestCode(vector<TestPiece> &testPieces, vector<int> &linenumbers)
{
	tokenizer a;

	for (int i = 0; i < testPieces.size(); i++)
	{
		//cout << "checking test #" << i << endl;
		//process testpiece
		bool stripwidthDefined = false;
		bool filenameDefined = false;
		for (int j = 0; j < testPieces[i].code.size(); j++)
		{
			a.tokenize(testPieces[i].code[j], "= 	");
			int linenumber = linenumbers[i] + j;

			if (a.size() == 0)
			{
				continue;
			}
			else if (a[0] == "filename")
			{
				if (a.size() > 1)
				{
					ifstream temp(a[1].c_str());
					if (!temp)
					{
						cout << "Error at line #"<< linenumber<< ": unable to open file: " << a[1] << endl;
						return false;
					}
					else
					{
						filenameDefined = true;
					}
				}
				else
				{
					cout << "Error at line #"<< linenumber<< ": expected a file name."<< endl;
					return false;
				}
			}
			else if (a[0] == "stripwidth" || a[0] == "stripwidths")
			{
				tokenizer b;
				b.tokenize(testPieces[i].code[j], " ()<>,	");
				if (b.size() != 4 && b.size() != 2)
				{
					cout << "Error at line #"<< linenumber<< ": Stripwidths werent defined properly. Proper syntax:"<< endl;
					cout << "	stripwidth (s1, s2, s3)" << endl;
					cout << "	where s1 is stripwidth for dimension 1, s2 is for dimension 2, and s3 is for dimension 3" << endl;
				}
				stripwidthDefined = true;

			}
			else if(a[0] == "numdirections3d")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting an integer after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "factor")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "lambda")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "factor")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "hard_approach")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting an integer after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "tau")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "T")
			{
				if (a.size() == 1)
				{
					cout << testPieces[i].code[j] << endl;
					//cout a.
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "overlap2d")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "overlap3d")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting a double after " << a[0] << endl;
					return false;
				}
			}
			else if(a[0] == "numdirections2d")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting an integer after " << a[0] << endl;
					return false;
				}

			}
			else if(a[0] == "d2algorithm")
			{
				if (a.size() == 1)
				{
					cout << "Error at line #"<< linenumber<< ": incorrect syntax, expecting an int after " << a[0] << endl;
					cout << "	0 - inclusion/exclusion" << endl;
					cout << "	1 - reeb graph" << endl;
					cout << "	2 - sweep" << endl;
					return false;
				}
				int function = atoi(a[1].c_str());
				//cout << function << endl;
				if (function < 0 || function > 2)
				{
					cout <<"Error at line #"<< linenumber<< ": " << a[1] << " is an incorrect value for d2algorithm." << endl;
					cout <<"	Possible values are: " << endl;
					cout << "	0 - inclusion/exclusion" << endl;
					cout << "	1 - sweep" << endl;
					cout << "	2 - reeb graph" << endl;
					return false;
				}

			}
			else if(a[0] == "2dcustomdir")
			{
				if (a.size() > 1)
				{
					if (a[1] != "0")
					{
						ifstream temp(a[1].c_str());
						if (!temp)
						{
							cout << "Error at line #"<< linenumber<< ": unable to open file: " << a[1] << endl;
							return false;
						}
					}
				}
			}
			else if(a[0] == "3dcustomdir")
			{
				if (a.size() > 1)
				{
					if (a[1] != "0")
					{
						ifstream temp(a[1].c_str());
						if (!temp)
						{
							cout << "Error at line #"<< linenumber<< ": unable to open file: " << a[1] << endl;
							return false;
						}
					}
				}

			}
			else
			{
				cout << "Error at line #"<< linenumber<< ": unrecognized command " << a[0] << endl;
				return false;
			}
		}

		if (stripwidthDefined == false || filenameDefined == false)
		{
			cout << "Error in test case " << i +1 << " parameters stripwidth and filename MUST be defined in each test case." << endl;
			return false;
		}
	}
}
void processScriptFile(string scriptfile)
{

	// these parameters can be overridden, but have default values specified in default_parameters.cfg
	double defaultoverlap2d;
	double defaultoverlap3d;
	int defaultnumdirections3d;
	int defaultnumdirections2d;
	int defaultd2algorithm;
	string defaultd2customdir;
	string defaultd3customdir;
	double defaultfactor;
	double defaultlambda;
	double defaultT;
	double defaulttau;
	bool defaulthard_approach;
	vector<math_vector> defaultd2customdirections;
	vector<math_vector> defaultd3customdirections;


	/*/////////////////////////////////////////////////////////////////////////////////////*/
	/*// Read in the defaults file
	/*/////////////////////////////////////////////////////////////////////////////////////*/

	ifstream defaultstest("default_parameters.cfg");
	if (!defaultstest)
	{
			cout << "Error: could not open default_parameters.cfg" << endl;
			cout << "generating default_parameters.cfg..." << endl;
			generateDefaultsFile();
			cout << "default_parameters.cfg created successfully." << endl;
	}
	defaultstest.close();
	ifstream defaults("default_parameters.cfg");

	char buffer[256];
	tokenizer a;
	while (! defaults.eof() )
	{

		defaults.getline(buffer,256);
		a.tokenize(buffer, " =	");
		if (buffer[0] == '/' && buffer[1] == '/')
			continue;
		if (a.size() == 0)
			continue;
		else if(a[0] == "numdirections3d")
			defaultnumdirections3d = atoi(a[1].c_str());
		else if(a[0] == "hard_approach")
			defaulthard_approach = (bool) atoi(a[1].c_str());
		else if(a[0] == "lambda")
			defaultlambda = atof(a[1].c_str());
		else if(a[0] == "factor")
			defaultfactor = atof(a[1].c_str());
		else if(a[0] == "tau")
			defaulttau = atof(a[1].c_str());
		else if(a[0] == "T")
			defaultT = atof(a[1].c_str());
		else if(a[0] == "overlap3d")
			defaultoverlap3d = atof(a[1].c_str());
		else if(a[0] == "overlap2d")
			defaultoverlap2d = atof(a[1].c_str());
		else if(a[0] == "numdirections2d")
			defaultnumdirections2d = atoi(a[1].c_str());
		else if(a[0] == "d2algorithm")
			defaultd2algorithm = atoi(a[1].c_str());
		else if(a[0] == "2dcustomdir")
		{
			if (a[1] != "0") // 0 means random direction
			{
				ifstream d2directions(a[1].c_str());
				if (!d2directions)
				{
					cout << "Error: could not open d2 directions file. Could not find " << a[1] << endl;
					exit(0);
				}
				vector<d2point> temp = read2dfile(a[1].c_str());
				for (int i = 0; i < temp.size(); i++)
					defaultd2customdirections.push_back(temp[i].to_vector());

				d2directions.close();
			}
		}
		else if(a[0] == "3dcustomdir")
		{
			if (a[1] != "0")
			{
				ifstream d3directions(a[1].c_str());
				if (!d3directions)
				{
					cout << "Error: could not open d3 directions file. Could not find " << a[1] << endl;
					exit(0);
				}
				vector<d3point> tempb = read3dfile(a[1].c_str());
				for (int i = 0; i < tempb.size(); i++)
					defaultd3customdirections.push_back(tempb[i].to_vector());

				d3directions.close();
			}
		}
	}
	defaults.close();


	/*/////////////////////////////////////////////////////////////////////////////////////*/
	/*// Read in the script file and prepare it
	/*/////////////////////////////////////////////////////////////////////////////////////*/
	vector<TestPiece> testPieces;
	vector<int> linenumbers; //holds linenumbers of the begining of each testpiece
	ifstream sfile(scriptfile.c_str());
	if (!sfile)
	{
		cout << "Error: could not open " << scriptfile.c_str() << endl;
		exit(0);
	}
	cout << "1) READING " << scriptfile << endl;
	int linenumber = 1;
	int testCounter = 0;
	bool startEncountered = false;
	while (! sfile.eof() )
	{
		sfile.getline(buffer,256);
		//cout << buffer << endl;
		a.tokenize(buffer, " =	");

		if (a.size() == 0)
		{
			if (startEncountered == true)
				testPieces[testCounter].code.push_back(" ");
		}
		else if ( (a[0])[0] == '/' && (a[0])[1] == '/')
		{
			if (startEncountered == true)
				testPieces[testCounter].code.push_back(" ");
		}
		else if (a[0] == "start" || a[0] == "START")
		{
			if (startEncountered == true)
			{
				cout << "Error at line #" << linenumber << ": expected an END here" << endl;
				exit(0);
			}
			testPieces.push_back(TestPiece());
			linenumbers.push_back(linenumber +1);
			startEncountered = true;
			//testPieces[testCounter].code.push_back(buffer);

		}
		else if (a[0] == "END" || a[0] == "end")
		{
			if (startEncountered == false)
			{
				cout << "Error at line #" << linenumber << ": no matching START for END statement" << endl;
				exit(0);
			}
			startEncountered = false;
			//testPieces[testCounter].code.push_back(buffer);
			testCounter++;
		}
		else
		{
			if (startEncountered == false)
			{
				cout << "Error at line #" << linenumber << ": token " << a[0] << " is outside of a START END block. All commands must be enclosed by a START END block." << endl;
				exit(0);
			}
			testPieces[testCounter].code.push_back(buffer);
		}

		linenumber++;
	}
	if (testCounter == 0)
	{
		cout << "No tests in " << scriptfile << endl;
		exit(0);
	}

	/*cout << "printing buffers: " << endl;
	for (int i = 0; i < testPieces.size(); i++)
	{
		cout << "Test piece #" << i << endl;

		for (int j = 0; j < testPieces[i].code.size(); j++)
		{
			cout << "STRING: " << testPieces[i].code[j] << endl;
			if (testPieces[i].code[j] == "	")
				cout << "hehhe" << endl;
		}
	} */

	

	/*/////////////////////////////////////////////////////////////////////////////////////*/
	/*// execute the script file
	/*/////////////////////////////////////////////////////////////////////////////////////*/
	cout << "3) EXECUTING, logging results to testresults.txt"  << endl;


	ofstream testresults("testresults.txt");
	for (int i = 0; i < testPieces.size(); i++)
	{
		// these parameters must be specified for each test case
		//double stripwidth;
		vector<double> stripwidth(3);
		string filename;

		// set parameters to their default values
		double overlap2d = defaultoverlap2d;
		double overlap3d = defaultoverlap3d;
		int numdirections3d = defaultnumdirections3d;
		int numdirections2d = defaultnumdirections2d;
		int d2algorithm = defaultd2algorithm;
		string d2customdir = defaultd2customdir;
		string d3customdir = defaultd3customdir;
		double T = defaultT;
		double tau = defaulttau;
		bool hard_approach = defaulthard_approach;
		vector<math_vector> d2customdirections = defaultd2customdirections;
		vector<math_vector> d3customdirections = defaultd3customdirections;
		double factor = defaultfactor;
		double lambda = defaultlambda;

		// read test case and override parameters
		for (int j = 0; j < testPieces[i].code.size(); j++)
		{
			a.tokenize(testPieces[i].code[j], " =	");
			if (a[0] == "filename")
				filename = a[1];
			else if(a[0] == "lambda")
				lambda = atof(a[1].c_str());
			else if(a[0] == "factor")
				factor = atof(a[1].c_str());
			else if (a[0] == "stripwidth" || a[0] == "stripwidths")
			{
				tokenizer b;
				b.tokenize(testPieces[i].code[j], " ()<>,	");
				if (b.size() == 4)
				{
					stripwidth[0] = atof(b[1].c_str());
					stripwidth[1] = atof(b[2].c_str());
					stripwidth[2] = atof(b[3].c_str());
				}
				else if (b.size() == 2)
				{
					double d = atof(b[1].c_str());

					stripwidth[0] = d;
					stripwidth[1] = d;
					stripwidth[2] = d;
				}
				else
				{
					cout << "runtime error with stripwidths. this shouldn't happen?" << endl;
				}

			}
			else if(a[0] == "numdirections3d")
				numdirections3d = atoi(a[1].c_str());
			else if(a[0] == "hard_approach")
				hard_approach = (bool) atoi(a[1].c_str());
			else if(a[0] == "tau")
				tau = atof(a[1].c_str());
			else if(a[0] == "T")
				T = atof(a[1].c_str());
			else if(a[0] == "overlap3d")
				overlap3d = atof(a[1].c_str());
			else if(a[0] == "overlap2d")
				overlap2d = atof(a[1].c_str());
			else if(a[0] == "numdirections2d")
				numdirections2d = atoi(a[1].c_str());
			else if(a[0] == "d2algorithm")
				d2algorithm = atoi(a[1].c_str());
			else if(a[0] == "2dcustomdir")
			{
				if (a[1] != "0") // 0 means random direction
				{
					ifstream d2directions(a[1].c_str());
					if (!d2directions)
					{
						cout << "Error: could not open d2 directions file. Could not find " << a[1] << endl;
						exit(0);
					}
					vector<d2point> temp = read2dfile(a[1].c_str());
					for (int i = 0; i < temp.size(); i++)
						d2customdirections.push_back(temp[i].to_vector());

					d2directions.close();
				}
			}
			else if(a[0] == "3dcustomdir")
			{
				if (a[1] != "0")
				{
					ifstream d3directions(a[1].c_str());
					if (!d3directions)
					{
						cout << "Error: could not open d3 directions file. Could not find " << a[1] << endl;
						exit(0);
					}
					vector<d3point> tempb = read3dfile(a[1].c_str());
					for (int i = 0; i < tempb.size(); i++)
						d3customdirections.push_back(tempb[i].to_vector());

					d3directions.close();
				}
			}
		}

		//execute test case
		cout << "EXECUTING TEST #" << i+1 << endl;
		cout << "------------------" << endl;
		testresults << "EXECUTING TEST #" << i+1 << endl;
		testresults << "------------------" << endl;
		//retrieve dimension from input
		int dimension;
		ifstream inputfile(filename.c_str());
		if (!inputfile)
		{
			cout << "Error: could not open " << filename << endl;
			exit(0);
		}
		inputfile.getline(buffer, 256);
		a.tokenize(buffer, " ");
		dimension = a.size();
		inputfile.close();
		int answer;

		vector<double> d1pointcloud;
		vector<d2point> d2pointcloud;
		vector<d3point> d3pointcloud;
		time_t begin;
		time_t end;

		// set up sweeping algorithm and initialize it with tau, T, and hard
		EulerSweep sweeper(hard_approach, tau, T);

		// set up inclusion/exclusion
		EulerIE eulerIE(overlap2d, overlap3d);

		d2euler function; /// the d2euler typedef is in EulerIE.h

		cout << "Processing " << dimension << "d data in " << filename << endl;
		testresults << "Processing " << dimension << "d data in " << filename << endl;
		if (dimension > 1)
		{
			if (d2algorithm == 0)
			{
				cout << "- Using 2d function: inclusion/exclusion." << endl;
				cout << "- Using 2doverlap = " << EulerIE::getOverlap2d() << endl;
				testresults << "- Using 2d function: inclusion/exclusion." << endl;
				testresults << "- Using 2doverlap = " << EulerIE::getOverlap2d() << endl;
				function = EulerIE::euler2d;
			}
			else if (d2algorithm == 1)
			{
				cout << "- Using 2d function: sweeping algorithm." << endl;
				testresults << "- Using 2d function: sweeping algorithm." << endl;
				function = EulerSweep::euler2d;
				cout << "	* using tau = " << tau << endl;
				cout << "	* using T = " << T << endl;
				cout << "	* hard approach = " << hard_approach << endl;
				testresults << "	* using tau = " << tau << endl;
				testresults << "	* using T = " << T << endl;
				testresults << "	* hard approach = " << hard_approach << endl;
			}
			else if (d2algorithm == 2)
			{
				cout << "- Using 2d function: 2d reeb graph." << endl;
				testresults << "- Using 2d function: 2d reeb graph." << endl;

				function = Euler2dreeb;
			}
			else
			{
				cout << endl;
				cout << "Error: unsupported 2d function" << endl;
				testresults << endl;
				testresults << "Error: unsupported 2d function" << endl;
				exit(0);
			}
		}
		if (dimension == 1)
		{
			d1pointcloud = read1dfile(filename.c_str());
			if (stripwidth[0] == 0 && stripwidth[1] == 0 && stripwidth[2] == 0)
			{
				cout << "- Calculating median distance to nearest neighbour" << endl;
				testresults << "- Calculating median distance to nearest neighbour" << endl;
				stripwidth[0] = factor * medianDistanceToNearestNeighbour(d1pointcloud);
				cout << "- Setting stripwidth to " << stripwidth[0] << endl;
				testresults << "- Setting stripwidth to " << stripwidth[0] << endl;
			}


				begin = time(NULL);

			cout << "- Using stripwidth(1d) = " << stripwidth[0] << endl;
			testresults << "- Using stripwidth(1d) = " << stripwidth[0] << endl;
			answer = EulerIE::euler1d(d1pointcloud, stripwidth[0]);

				end = time(NULL);
				cout << "process took " << end - begin << " seconds to complete" << endl;
				testresults << "process took " << end - begin << " seconds to complete" << endl;

			cout << "E.C. 1d = " << answer << endl;
			testresults << "E.C. 1d = " << answer << endl;
		}
		else if (dimension == 2)
		{
			d2pointcloud = read2dfile(filename.c_str());
			if (stripwidth[0] == 0 && stripwidth[1] == 0 && stripwidth[2] == 0 && lambda == 0)
			{
				cout << "- Stripwidth and lambda are 0. Calculating median distance to nearest neighbour" << endl;
				testresults << "- Stripwidth and lambda are 0. Calculating median distance to nearest neighbour" << endl;
				stripwidth[1] = factor * medianDistanceToNearestNeighbour(d2pointcloud);
				stripwidth[0] = stripwidth[1];
				cout << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				cout << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;
				testresults << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				testresults << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;

			}
			else if (stripwidth[0] == 0 && stripwidth[1] == 0 && stripwidth[2] == 0 && lambda != 0)
			{
				cout << "- Stripwidth is 0, but lambda is non-zero. Using lambda to approximate distance to nearest neighbor." << endl;
				testresults << "- Stripwidth is 0, but lambda is non-zero. Using lambda to approximate distance to nearest neighbor." << endl;
				stripwidth[1] = factor * ( 1 / (sqrt (lambda * M_PI))) * (.88368353); // this is the formula for expected distance to nearest neighbor in 2d
				stripwidth[0] = stripwidth[1];
				cout << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				cout << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;
				testresults << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				testresults << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;
			}

			if  (d2customdirections.size() == 0)
			{
				cout << "- Using " << numdirections2d << " random directions in 2d."<<endl;
				testresults << "- Using " << numdirections2d << " random directions in 2d."<<endl;
			}
			else
			{
				cout << "- Using " << d2customdirections.size() << " pre-defined directions in 2d" << endl;
				testresults << "- Using " << d2customdirections.size() << " pre-defined directions in 2d" << endl;
			}

			cout << "- Using stripwidth(2d) = " << stripwidth[1] << endl;
			cout << "- Using stripwidth(1d) = " << stripwidth[0] << endl;
			testresults << "- Using stripwidth(2d) = " << stripwidth[1] << endl;
			testresults << "- Using stripwidth(1d) = " << stripwidth[0] << endl;



				begin = time(NULL);

			answer = function(d2pointcloud, stripwidth, numdirections2d, d2customdirections);



				end = time(NULL);
				cout << "process took " << end - begin << " seconds to complete" << endl;
				testresults << "process took " << end - begin << " seconds to complete" << endl;

			cout << "E.C. 2d = " << answer << endl;
			testresults << "E.C. 2d = " << answer << endl;

		}
		else if (dimension == 3)
		{

			cout << "- Using 3doverlap = " << EulerIE::getOverlap3d() << endl;
			testresults << "- Using 3doverlap = " << EulerIE::getOverlap3d() << endl;

			d3pointcloud = read3dfile(filename.c_str());
			if (stripwidth[0] == 0 && stripwidth[1] == 0 && stripwidth[2] == 0 && lambda == 0)
			{
				cout << "- Stripwidth and lambda are 0. Calculating median distance to nearest neighbour" << endl;
				testresults << "- Stripwidth and lambda are 0. Calculating median distance to nearest neighbour" << endl;
				stripwidth[2] = factor * medianDistanceToNearestNeighbour(d3pointcloud);
				stripwidth[1] = stripwidth[2];
				stripwidth[0] = stripwidth[2];
				cout << "- Setting stripwidth(3d) to " << stripwidth[2] << endl;
				cout << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				cout << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;

				testresults << "- Setting stripwidth(3d) to " << stripwidth[2] << endl;
				testresults << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				testresults << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;

			}
			else if (stripwidth[0] == 0 && stripwidth[1] == 0 && stripwidth[2] == 0 && lambda != 0)
			{
				cout << "- Stripwidth is 0, but lambda is non-zero. Using lambda to approximate distance to nearest neighbor." << endl;
				testresults << "- Stripwidth is 0, but lambda is non-zero. Using lambda to approximate distance to nearest neighbor." << endl;
				stripwidth[2] = factor * pow(((4*lambda*M_PI)/3), (-1* (1/3))) * (.89232059); // this is the formula for expected distance to nearest neighbor in 3d
				stripwidth[1] = stripwidth[2];
				stripwidth[0] = stripwidth[2];
				cout << "- Setting stripwidth(3d) to " << stripwidth[2] << endl;
				cout << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				cout << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;
				testresults << "- Setting stripwidth(3d) to " << stripwidth[2] << endl;
				testresults << "- Setting stripwidth(2d) to " << stripwidth[1] << endl;
				testresults << "- Setting stripwidth(1d) to " << stripwidth[0] << endl;
			}
			if (d2customdirections.size() == 0)
			{
				cout << "- Using " << numdirections2d << " random directions in 2d" <<endl;
				testresults << "- Using " << numdirections2d << " random directions in 2d" <<endl;
			}
			else
			{
				cout << "- Using " << d2customdirections.size() << " pre-defined directions in 2d" << endl;
				testresults << "- Using " << d2customdirections.size() << " pre-defined directions in 2d" << endl;
			}


			if (d3customdirections.size() == 0)
			{
				cout << "- Using " << numdirections3d << " random directions in 3d" <<endl;
				testresults << "- Using " << numdirections3d << " random directions in 3d" <<endl;
			}
			else
			{
				cout << "- Using " << d3customdirections.size() << " pre-defined directions in 3d" << endl;
				testresults << "- Using " << d3customdirections.size() << " pre-defined directions in 3d" << endl;
			}

			cout << "- Using stripwidth(3d) = " << stripwidth[2] << endl;
			cout << "- Using stripwidth(2d) = " << stripwidth[1] << endl;
			cout << "- Using stripwidth(1d) = " << stripwidth[0] << endl;
			testresults << "- Using stripwidth(3d) = " << stripwidth[2] << endl;
			testresults << "- Using stripwidth(2d) = " << stripwidth[1] << endl;
			testresults << "- Using stripwidth(1d) = " << stripwidth[0] << endl;


				begin = time(NULL);

			answer = EulerIE::euler3d(d3pointcloud, function, stripwidth, numdirections3d, numdirections2d, d3customdirections, d2customdirections);

				end = time(NULL);
				cout << "process took " << end - begin << " seconds to complete" << endl;
				testresults << "process took " << end - begin << " seconds to complete" << endl;

			cout << "E.C. 3d = " << answer << endl;
			testresults << "E.C. 3d = " << answer << endl;
		}
		else
		{
			cout << "Error: unsupported dimension" << endl;
			testresults << "Error: unsupported dimension" << endl;
		}
	}
	testresults.close();
}




//////
/// test module for euler 1d,2d and 3d
//////
int main(int argc, char *argv[])
{
	if (argc == 2)
	{
		string scriptfilename = argv[1];
		cout << "processing script file " << scriptfilename << endl;

		processScriptFile(scriptfilename);

		cout << "finished processing " << scriptfilename << endl;
	}
	else if (argc == 1)
	{
		cout << "no script file given as argument, attempting to use test_configuration.cfg" << endl;
		ifstream eulerconfig("test_configuration.cfg");
		if (!eulerconfig)
		{
			cout << "Couldn't open test_configuration.cfg" << endl;
			cout << "exiting..." << endl;
		}
		else
		{
			eulerconfig.close();
			cout << "processing script file test_configuration.cfg" << endl;
			processScriptFile("test_configuration.cfg");
			cout << "finished processing test_configuration.cfg" << endl;
		}
	}
	else
	{
		cout << "Too many arguments" << endl;
	}
}
