
//..
//..Gold Module - Example template ..
//..developed by GTS..
//..
//..Intel(R) Corporation (C) 2015
//..

//..includes
#include "Include/Main.h"


//..defines
#ifdef __WIN_OS__
#include <Windows.h>
#endif
#ifdef __LIN_OS__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <dlfcn.h>
#endif

#ifdef __LIN_OS__
std::string(*OutputDLLVersion)(void);
bool(*bISFeature_OS_Supported)(void);
int(*max_avx_supported)(void);
#endif

#ifdef __WIN_OS__
typedef char *(*pOutputDLLVersion)(void);
typedef bool(*pISFeature_OS_Supported)(void);
typedef int(*pMaxAVX_Support)(void);
#endif

bool sStopOnError = false;
std::vector<size_t> sieve_of_eratosthenes(size_t max);
double OpPerSec = (double)0;
string sPNTimer;
long lQtyErr = 0;
int displayOnly = 0;  //nc
long CycleErr = 0;
const int sievesize = 1000000;
int primes((sievesize >= 2) ? 1 : 0);  // two is a prime, so count it;
double primesnumber = (double)0;
int RangeNumberProgress = 0;
int AVXnumber = 0;
bool AVXOSsupported = false;
int AVX_Max_Supp;
std::string sDLLversion;
std::string sDLLname;

string trim(const string& str)
{
	size_t first = str.find_first_not_of(' ');
	if (string::npos == first)
	{
		return str;
	}
	size_t last = str.find_last_not_of(' ');
	return str.substr(first, (last - first + 1));
}

class Sieve
{
private:
	std::vector<bool> sieve;
public:
	Sieve(size_t size);
	bool is_composite(size_t k);
	void set_composite(size_t k);
};

inline Sieve::Sieve(size_t size)
	: sieve((size + 1) / 2)
{ }

inline bool Sieve::is_composite(size_t k)
{
	assert(k >= 3 && (k % 2) == 1);
	return sieve[(k - 3) / 2];
}

inline void Sieve::set_composite(size_t k)
{
	assert(k >= 3 && (k % 2) == 1);
	sieve[(k - 3) / 2] = true;
}


void WriteResultsFile(int iPassFailStatus)
{
	//..clean up result files ... Clean file before another one is written
	//CleanUp();
	// pass fail logic using iPassFailStatus  with 0=pass, 1=fail, 2=indeterminate
	// CPUFreqResultStatus 0=within lower and higher tolerance, 1=lower that lower tolerance, 2=higher than higher tolerance, 3=DisplayOnly
	// Write Results File
	td.WriteToFile(sgGoldModuleResultsFile, "Prime Number Generation Test");
	std::string sTemp = "Module Version: " + sgTestModuleVersion;
	td.WriteToFile(sgGoldModuleResultsFile, sTemp);
	sTemp = "Start Time: ";
	sTemp.append(std::asctime(std::localtime(&tStartTime)));
	td.WriteToFileNoEndL(sgGoldModuleResultsFile, sTemp);
	sTemp = sDLLname + " Version - " + sDLLversion;
	td.WriteToFile(sgGoldModuleResultsFile, sTemp);
	if (AVXOSsupported)
	{
		td.WriteToFile(sgGoldModuleResultsFile, "AVX is supported in your OS");

		if (AVXnumber == 1)
		{
			td.WriteToFile(sgGoldModuleResultsFile, "Max AVX supported = AVX");
		}
		else if (AVXnumber == 2)
		{
			td.WriteToFile(sgGoldModuleResultsFile, "Max AVX supported = AVX2");
		}
		else if (AVXnumber == 5)
		{
			td.WriteToFile(sgGoldModuleResultsFile, "Max AVX supported = AVX512");
		}
		else
			td.WriteToFile(sgGoldModuleResultsFile, "AVX, AVX2 or AVX512 not supported");
	}
	else
	{
		td.WriteToFile(sgGoldModuleResultsFile, "AVX is NOT supported in your OS");
	}

	if (displayOnly) //No Compare Option from argument -nc
	{
		igPassFailStatus = ReturnValueDef::NoCompare;
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - No Compare Option Used");
		std::string sTemp = "Operation Per Second: ";
		sTemp.append(UtilConvert(OpPerSec));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
		sTemp = "Error: ";
		sTemp.append(UtilConvert(lQtyErr));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
	}
	else if (iPassFailStatus == 0)
	{
		igPassFailStatus = ReturnValueDef::Success;
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - PASS");
		std::string sTemp = "Operation Per Second: ";
		sTemp.append(UtilConvert(OpPerSec));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
		sTemp = "Error: ";
		sTemp.append(UtilConvert(lQtyErr));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
		td.WriteToFile(sgGoldModuleResultsFile, "--- Prime Number Generation Test Passed!!!---");
	}
	else if (iPassFailStatus == 1) //Fail
	{
		igPassFailStatus = ReturnValueDef::Fail;
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - FAIL");
		std::string sTemp = "Operation Per Second: ";
		sTemp.append(UtilConvert(OpPerSec));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
		sTemp = "Error: ";
		sTemp.append(UtilConvert(lQtyErr));
		td.WriteToFile(sgGoldModuleResultsFile, sTemp);
		td.WriteToFile(sgGoldModuleResultsFile, "--- Prime Number Generation Test Failed!!!---");
	}

	tEndTime = std::time(nullptr);
	sTemp = "End Time: ";
	sTemp.append(std::asctime(std::localtime(&tEndTime)));
	td.WriteToFileNoEndL(sgGoldModuleResultsFile, sTemp);

	double seconds = difftime(tEndTime, tStartTime);
	sTemp = "Total Time: ";
	sTemp.append(" seconds: ");
	sTemp.append(UtilConvert(seconds));


	td.WriteToFileNoEndL(sgGoldModuleResultsFile, sTemp);
}

void ReadLocalTextConfig(void)
{
	TextData tdLocal;
	std::list<std::string> lLineArr;
	tdLocal.ReadFile("local_text_cfg.txt", lLineArr);
	for (std::list<string>::iterator li = lLineArr.begin(); li != lLineArr.end(); ++li)
	{
		std::cout << ' ' << *li << endl;
	}

}

void ReadLocalConfig(void)
{
	XMLParser localXMLObj;
	try
	{

		//Local config
		localXMLObj.LoadXMLFile("LocalConfig.xml");

		// Set up the root element
		localXMLObj.SetRootElem("LocalModuleConfig");

		string localStrItem = localXMLObj.ReadNodeKeyAsString("@LocalConfigItem", "");
		cout << "LocalConfigItem = " << localStrItem << endl;

	}
	catch (...)
	{
		std::string sException;
		sException.append("\n-------------------------------\n");
		sException.append("Cannot locate config file: 'LocalConfig.xml'! \n");
		sException.append("Application terminated! \n");
		sException.append("\n-------------------------------\n");
		throw(sException);
	}
	return;
}

// ---------------------------------------------------------------------------
//  PopulateData -- A routine to populate configuration variables
// ---------------------------------------------------------------------------

void PopulateData(XMLParser& xd, GlobalConfig& globalConfig)
{
	try
	{
		//Global config
		// Set up the root element
		xd.SetRootElem("GlobalModuleConfig");

		globalConfig.setGlobalConfigItem(xd.ReadNodeKeyAsString("@GlobalConfigItem", ""));
	}
	catch (...)
	{
		std::string sException;
		sException.append("\n-------------------------------\n");
		sException.append("Cannot locate config file: 'GlobalConfig.xml'! \n");
		sException.append("Application terminated! \n");
		sException.append("\n-------------------------------\n");
		throw(sException);
	}
	return;
}

void Init(void)
{

	tStartTime = std::time(nullptr);
	//xd.LoadXMLFile("GlobalConfig.xml");

	igPassFailStatus = ReturnValueDef::Success;  // Success
	iColorText = 0;
	iOptionValid = 0;
	iRun = 1;
	iArgIndex = 0;
	iPrintVersionFlag = 1;
	iPrintSuccessFlag = 1;
}

void PrintVersion(void)
{
	std::cout << std::endl << "--- Prime Number Generation Test ---" << std::endl << "..." << std::endl << "Version: " << sgTestModuleVersion << std::endl << "..." << std::endl << std::endl;
}

void SignalFun(int iSigNum)
{
	// If you want to print out the signal, do the following
	cout << "Signal is:" << iSigNum << endl;

	// Depending on your routine, you may want to have some sort of exit message ... however you may also want to comment these out
	//cout << endl << "Do the soft clean up here" << endl;

	// Do cleanup and close up stuff here 
	//CleanUp();

	// Exit the program with one of the accepted error levels. 
	//0 = Success
	//1 = Fail
	//2 = Indeterminate 
	igPassFailStatus = 2;

	// if you get to this logic, most likely your answer should always be a 2
	//cout << "Error is: " << igPassFailStatus << " Indeterminate!" << endl;
	std::string sTemp = "Error is: " + UtilConvert(igPassFailStatus) + " Indeterminate!";
	PrintColorMsg(sTemp, TextColor::Yellow);
	exit(igPassFailStatus);

}

void CleanUp()
{
	td.RemoveFile(sgGoldModuleResultsFile);
}

void HelpUseage(void)
{
	//std::cout << "Help or Useage ... version info ... copyright info ..." << std::endl;
	std::cout << "Test Information:" << std::endl;
	std::cout << " Math_PrimeNum.exe checks how fast the CPU can search for prime number." << std::endl << std::endl;
	std::cout << "Possible options are as follows:" << std::endl;
	std::cout << " -h           = Help or Usage (this message)" << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -h" << std::endl << std::endl;
	std::cout << " -info        = Information switch publishes parallel information" << std::endl;
	std::cout << "                using the following scheme:" << std::endl;
	std::cout << "                \"parallel:yes/no|socket:yes/no|core:yes/no\"" << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -info" << std::endl << std::endl;
	std::cout << " -resultName  = The resultName switch provides a way to name " << std::endl;
	std::cout << "                the results file as desired." << std::endl;
	std::cout << "                This is for the convenience of the control program. " << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -resultName Math_PrimeNum_results_01.txt " << std::endl << std::endl;
	std::cout << " -c           = If present, this option will display text in various colors" << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -c" << std::endl << std::endl;
	std::cout << " -nc          = Skip the result, will display only." << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -nc " << std::endl << std::endl;
	std::cout << " -errstop     = Stop on error, default is continue on error." << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -errstop " << std::endl << std::endl;
	std::cout << " -s [d]       = Time in seconds to perform the test" << std::endl;
	std::cout << "                default is 2 seconds." << std::endl;
	std::cout << "                Example:" << std::endl;
	std::cout << "                Math_PrimeNum.exe -s 5 " << std::endl << std::endl;
	std::cout << "Dependencies:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "               libiomp5md.dll" << std::endl;
	std::cout << "               svml_dispmd.dll" << std::endl;
#ifdef __WIN_64__
	std::cout << "               C:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool 64bit\\DetectUtils64.dll" << std::endl;
#endif
#ifdef __WIN_32__
	std::cout << "               C:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool\\DetectUtils.dll" << std::endl;
#endif
#endif
#ifdef __LIN_OS__
	std::cout << "               libiomp5.so" << std::endl;
#ifdef __LIN_64__
	std::cout << "               libDetectUtils64.so.1.1" << std::endl;
#endif
#ifdef __LIN_32__
	std::cout << "               libDetectUtils.so.1.1" << std::endl;
#endif
#endif
	std::cout << std::endl << "Copyright (C) 2015, Intel Corporation" << std::endl;

}

void PauseWQuit(void)
{
	// Pause code
	std::cout << "Please press 'q' <ENTER> to quit: ";
	std::string PauseStr;
	std::cin >> PauseStr;
}

void PrintColorMsg(std::string sMsg, TextColor iColor)
{
	if (iColorText == 0)
	{
		std::cout << std::endl << sMsg << std::endl;
	}
	else
	{
		if (iColor == TextColor::Red)
		{
#ifdef __WIN_OS__
			HANDLE hCommandWindowTextColor = GetStdHandle(STD_OUTPUT_HANDLE);
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_INTENSITY));
			std::cout << std::endl << sMsg << std::endl;
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN));
#endif
#ifdef __LIN_OS__
			std::cout << "\033[0;31m";
			std::cout << std::endl << sMsg << std::endl;
			std::cout << "\033[0m";
#endif
		}
		if (iColor == TextColor::Green)
		{

#ifdef __WIN_OS__
			HANDLE hCommandWindowTextColor = GetStdHandle(STD_OUTPUT_HANDLE);
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_GREEN | FOREGROUND_INTENSITY));
			std::cout << std::endl << sMsg << std::endl;
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN));
#endif
#ifdef __LIN_OS__
			std::cout << "\033[0;32m";
			std::cout << std::endl << sMsg << std::endl;
			std::cout << "\033[0m";
#endif
		}
		if (iColor == TextColor::Yellow)
		{
#ifdef __WIN_OS__
			HANDLE hCommandWindowTextColor = GetStdHandle(STD_OUTPUT_HANDLE);
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY));
			std::cout << std::endl << sMsg << std::endl;
			SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN));
#endif
#ifdef __LIN_OS__
			std::cout << "\033[0;33m";
			std::cout << std::endl << sMsg << std::endl;
			std::cout << "\033[0m";
#endif
		}

	}
}

bool is_integer(const std::string & s) {
	return std::regex_match(s, std::regex("[0-9]+"));
}

bool isOptionValid(int iArgLocal, int argcLocal, char *argvLocal[], VarType vt)
{
	// Start out as Valid
	bool iRetVal = true;
	std::string sMsg = "";
	std::string sTemp = argvLocal[iArgLocal];

	if (((iArgLocal + 1) < argcLocal) && !(argvLocal[iArgLocal + 1] == NULL) && !(argvLocal[iArgLocal + 1][0] == '\0'))
	{
		if (vt == VarType::vtINT)
		{
			if (!is_integer(argvLocal[iArgLocal + 1]))
			{
				iRetVal = false;
				sMsg = "Wrong argument type format for argument: '" + sTemp + "'!";
			}
		}
	}
	else
	{
		// invalid
		iRetVal = false;
		sMsg = "No argument for '" + sTemp + "' detected!";
	}
	// if there was an invalid argument
	if (iRetVal == false)
	{
		PrintColorMsg(sMsg, TextColor::Red);
	}
	return iRetVal;
}

long ConvertToLong(const std::string& s)
{
	std::istringstream i(s);
	long x = 0l;
	if (!(i >> x))
		return 0;
	return x;
}

bool IsPrime(int p) {
	bool a = true;
	for (double i = 2; i <= sqrt(p); i++)        //check untill the square root
	{
		int numberI = static_cast<int>(p);
		int iI = static_cast<int>(i);
		if (numberI%iI == 0)            // if it is divisible it is non prime 
		{
			a = false;
			break;
		}
	}
	return a;
}

void primeFactors(int number)
{
	while (number % 2 == 0)
	{
		number = number / 2;
	}
  
	for (int i = 3; i <= sqrt(number); i = i + 2)
	{
		while (number%i == 0)
		{
			number = number / i;
		}
	}

	if (number > 2)
	{
		if (IsPrime(number))
		{
			primesnumber += 1;
		}
		else // Non Prime Number
		{
			CycleErr += 1;
		}
	}
}

int RemainderFunction(int a, int b)
{
	__m256i r0 = _mm256_set1_epi32(a);
	__m256i r1 = _mm256_set1_epi32(b);

	__m256i ReDivide = _mm256_div_epi32(r0, r1);
	int* reDiv = (int*)&ReDivide;

	__m256i ReMultiply = _mm256_mul_epi32(ReDivide, r1);
	int* reMul = (int*)&ReMultiply;

	__m256d r2 = _mm256_set1_pd(9);
	__m256d sqrtResult = _mm256_sqrt_pd(r2);
	double* reSqrt = (double*)&sqrtResult;

	__m256i Remainder = _mm256_sub_epi32(r0, ReMultiply);
	int* re = (int*)&Remainder;
	//printf("Remainder: %d\n", re[0]);
	return re[0];

}

void AVX2PrimeNum(int RangeNumber)
{
	/// int = 4,294,967,295  // 8 ints
	int iR0 = 1000000;
	int iR1 = 0;
	if (RangeNumber == 3)
	{
		RangeNumberProgress += 1;
		iR1 += (50000000 * RangeNumberProgress);
		if (RangeNumberProgress < 15)
		{
			iR0 = iR1 + 500000;
		}
		else
		{
			iR0 = iR1 + 300000;
		}
		if (RangeNumberProgress == 40)
		{
			RangeNumberProgress = 0;
		}
	}
	else if (RangeNumber == 2) {
		iR0 = 10000000;
		iR1 = 8000000;
	}
#pragma omp parallel for schedule(dynamic) reduction(+:primes)
	for (int p = iR1; p <= iR0; p++) {
		primes++;
		__m256i r0 = _mm256_set1_epi32(p);
		__m256d r2 = _mm256_set1_pd(p);
		__m256d sqrtResult = _mm256_sqrt_pd(r2);
		double* reSqrt = (double*)&sqrtResult;
		bool ISaPrime = true;

		for (double i = 2; i <= reSqrt[0]; i++) 
		{
			__m256i r1 = _mm256_set1_epi32((int)i);
			__m256i ReDivide = _mm256_div_epi32(r0, r1);
			int* reDiv = (int*)&ReDivide;

			__m256i ReMultiply = _mm256_mul_epi32(ReDivide, r1);
			int* reMul = (int*)&ReMultiply;

			__m256i Remainder = _mm256_sub_epi32(r0, ReMultiply);
			int* re = (int*)&Remainder;

			if (re[0] == 0)            // if it is divisible it is non prime 
			{
				ISaPrime = false;
				break;
			}
		}
		if (ISaPrime)
		{
			primesnumber += 1;
			if (p > 2)
			{
				if (!IsPrime(p)) // Non Prime Number
				{
					CycleErr += 1;
				}
			}

		}
	}

}
void AVX5PrimeNum(int RangeNumber)
{
	/// int = 4,294,967,295  // 8 ints
	int iR0 = 1000000;
	int iR1 = 0;

	if (RangeNumber == 3)
	{
		RangeNumberProgress += 1;
		iR1 += (50000000 * RangeNumberProgress);
		if (RangeNumberProgress < 15)
		{
			iR0 = iR1 + 500000;
		}
		else
		{
			iR0 = iR1 + 300000;
		}
		if (RangeNumberProgress == 40)
		{
			RangeNumberProgress = 0;
		}
	}
	else if (RangeNumber == 2) {
		iR0 = 10000000;
		iR1 = 8000000;
	}

#pragma omp parallel for schedule(dynamic) reduction(+:primes)
	for (int p = iR1; p <= iR0; p++) {
		primes++;
		__m512i r0 = _mm512_set1_epi32(p);
		__m512d r2 = _mm512_set1_pd(p);
		__m512d sqrtResult = _mm512_sqrt_pd(r2);
		double* reSqrt = (double*)&sqrtResult;
		bool ISaPrime = true;

		for (double i = 2; i <= reSqrt[0]; i++)        
		{
			__m512i r1 = _mm512_set1_epi32((int)i);
			__m512i ReDivide = _mm512_div_epi32(r0, r1);
			int* reDiv = (int*)&ReDivide;

			__m512i ReMultiply = _mm512_mul_epi32(ReDivide, r1);
			int* reMul = (int*)&ReMultiply;

			__m512i Remainder = _mm512_sub_epi32(r0, ReMultiply);
			int* re = (int*)&Remainder;

			if (re[0] == 0)            // not prime 
			{
				ISaPrime = false;
				break;
			}
		}
		if (ISaPrime)
		{
			primesnumber += 1;
			if (p > 2)
			{
				if (!IsPrime(p)) // Non Prime Number
				{
					CycleErr += 1;
				}
			}

		}

	}

}
void AVX1PrimeNum(int RangeNumber)
{
	/// int = 4,294,967,295  // 8 ints
	int iR0 = 1000000;
	int iR1 = 0;

	if (RangeNumber == 3)
	{
		RangeNumberProgress += 1;
		iR1 += (50000000 * RangeNumberProgress);
		if (RangeNumberProgress < 15)
		{
			iR0 = iR1 + 500000;
		}
		else
		{
			iR0 = iR1 + 300000;
		}
		if (RangeNumberProgress == 40)
		{
			RangeNumberProgress = 0;
		}
	}
	else if (RangeNumber == 2) {
		iR0 = 10000000;
		iR1 = 8000000;
	}
#pragma omp parallel for schedule(dynamic) reduction(+:primes)
	for (int p = iR1; p <= iR0; p++)
	{
		primes++;
		__m256i r0 = _mm256_set1_epi32(p);
		__m256d r2 = _mm256_set1_pd(p);
		__m256d sqrtResult = _mm256_sqrt_pd(r2);
		double* reSqrt = (double*)&sqrtResult;
		bool ISaPrime = true;

		for (double i = 2; i <= reSqrt[0]; i++)       
		{
			__m256d dr0 = _mm256_cvtepi32_pd(_mm256_castsi256_si128(r0));
			double* Redr0 = (double*)&dr0;
			__m256d r1 = _mm256_set1_pd(i);
			double* Rer1 = (double*)&r1;
			__m256d ReDivide = _mm256_div_pd(dr0, r1);
			double* reDiv = (double*)&ReDivide;
			__m128i epi32ReDivides = _mm256_cvtpd_epi32(ReDivide);
			__m256d newReDivide = _mm256_cvtepi32_pd(epi32ReDivides);
			double* newreDiv = (double*)&newReDivide;
			__m256d ReMultiply = _mm256_mul_pd(newReDivide, r1);
			double* reMul = (double*)&ReMultiply;
			__m256d Remainder = _mm256_sub_pd(dr0, ReMultiply);
			double* re = (double*)&Remainder;

			if (re[0] == 0)            // Non Prime Number
			{
				ISaPrime = false;
				break;
			}
		}
		if (ISaPrime)
		{
			primesnumber += 1;
			if (p > 2)
			{
				if (!IsPrime(p)) // Non Prime Number
				{
					CycleErr += 1;
				}
			}

		}
	}
}

long RunMathPrimeNum(long runTime, bool StopOnErr)
{
	time_t start;
	time_t end;
	long timeBalance = runTime;
	long dif = 0;
	long dif2 = 0;
	long timeUpdateOPS = 0;
	double TotalGenerated = 0;
	long QtyCycle = 1;
	time(&start);
	
#ifdef __WIN_OS__
	HINSTANCE hInstanceLoadDLL;
#ifdef __WIN_32__
	if (!(hInstanceLoadDLL = LoadLibraryA("c:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool\\DetectUtils.dll")))
#elif defined __WIN_64__
	if (!(hInstanceLoadDLL = LoadLibraryA("c:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool 64bit\\DetectUtils64.dll")))
#endif
	{
#ifdef __WIN_32__
		cout << ".." << endl << "..could not load DetectUtils.dll " << endl << ".." << endl;
#elif defined __WIN_64__
		cout << ".." << endl << "..could not load DetectUtils64.dll " << endl << ".." << endl;
#endif
		exit(1);
	}

#endif

#ifdef __WIN_OS__

	pOutputDLLVersion pODV = (pOutputDLLVersion)GetProcAddress(hInstanceLoadDLL, "DUdll_OutputDLLVersion");
	pISFeature_OS_Supported pISFOSS = (pISFeature_OS_Supported)GetProcAddress(hInstanceLoadDLL, "DUdll_bISFeature_OS_Supported");
	pMaxAVX_Support pMaxAVX = (pMaxAVX_Support)GetProcAddress(hInstanceLoadDLL, "DUdll_Max_AVX_Supported");
	
	if (!pODV)
	{
#ifdef __WIN_32__
		cout << ".." << endl << "..could not load OutputDLLVersion from DetectUtils.dll " << endl << ".." << endl;
#elif defined __WIN_64__
		cout << ".." << endl << "..could not load OutputDLLVersion from DetectUtils64.dll " << endl << ".." << endl;
#endif
		exit(1);
	}
	else
	{
		//output DLL version
		char* cDLLversion = pODV();
		sDLLversion = pODV();
#ifdef __WIN_32__
		cout << "..DetectUtils DLL Version - " << cDLLversion << endl;
		sDLLname = "DetectUtils DLL";
#elif defined __WIN_64__
		cout << "..DetectUtils64 DLL Version - " << cDLLversion << endl;
		sDLLname = "DetectUtils64 DLL";
#endif
	}

	if (!pISFOSS)
	{
#ifdef __WIN_32__
		cout << ".." << endl << "..could not load ISFeature_OS_Supported from DetectUtils dll " << endl << ".." << endl;
#elif defined __WIN_64__
		cout << ".." << endl << "..could not load ISFeature_OS_Supported from DetectUtils64 dll " << endl << ".." << endl;
#endif
		exit(1);
	}
	else
	{
		AVXOSsupported = pISFOSS();
	}

	if (!pMaxAVX)
	{
#ifdef __WIN_32__
		cout << ".." << endl << "..could not load Max_AVX_Supported from DetectUtils dll " << endl << ".." << endl;
#elif defined __WIN_64__
		cout << ".." << endl << "..could not load Max_AVX_Supported from DetectUtils64 dll " << endl << ".." << endl;
#endif
		exit(1);
	}
	else
	{
		AVX_Max_Supp = pMaxAVX();
	}
		//unload DLL
	FreeLibrary(hInstanceLoadDLL);

#endif

#ifdef __LIN_OS__

	void *handle;
	char *error;
	int x, y, z;

#ifdef __LIN_32__
	handle = dlopen("./libDetectUtils.so.1.1", RTLD_LAZY);
#elif defined __LIN_64__
	handle = dlopen("./libDetectUtils64.so.1.1", RTLD_LAZY);
#endif
	if (!handle) {
	#ifdef __LIN_32__
		cout << ".." << endl << "..could not load libDetectUtils shared library " << endl << ".." << endl;
	#elif defined __LIN_64__
		cout << ".." << endl << "..could not load libDetectUtils64 shared library " << endl << ".." << endl;
	#endif
		fputs(dlerror(), stderr);
		exit(1);
	}

	//DLLVersion
	OutputDLLVersion = (std::string(*)(void))dlsym(handle, "OutputDLLVersion");
	sDLLversion = OutputDLLVersion();
	if ((error = dlerror()) != NULL)
	{
	#ifdef __LIN_32__
		cout << ".." << endl << "..could not load OutputDLLVersion from libDetectUtils shared library " << endl << ".." << endl;
	#elif defined __LIN_64__
		cout << ".." << endl << "..could not load OutputDLLVersion from libDetectUtils64 shared library" << endl << ".." << endl;
	#endif
		fputs(error, stderr);
		exit(1);
	}
#ifdef __LIN_32__
	cout << ".libDetectUtils Shared Library Version - " << trim(sDLLversion) << endl;
	sDLLname = "libDetectUtils Shared Library";
#elif defined __LIN_64__
	cout << ".libDetectUtils64 Shared Library Version - " << trim(sDLLversion) << endl;
	sDLLname = "libDetectUtils64 Shared Library";
#endif
	
	//Is AVX supported by OS
	bISFeature_OS_Supported = (bool(*)(void))dlsym(handle, "bISFeature_OS_Supported");
	if ((error = dlerror()) != NULL)
	{
	#ifdef __LIN_32__
		cout << ".." << endl << "..could not load bISFeature_OS_Supported from libDetectUtils Shared Library" << endl << ".." << endl;
	#elif defined __LIN_64__
		cout << ".." << endl << "..could not load bISFeature_OS_Supported from libDetectUtils64 Shared Library" << endl << ".." << endl;
	#endif
		fputs(error, stderr);
		exit(1);
	}
	AVXOSsupported = bISFeature_OS_Supported();
	//Max AVX Supported
	max_avx_supported = (int(*)(void))dlsym(handle, "max_avx_supported");
	if ((error = dlerror()) != NULL)
	{
	#ifdef __LIN_32__
		cout << ".." << endl << "..could not load Max_AVX_Supported from libDetectUtils Shared Library " << endl << ".." << endl;
	#elif defined __LIN_64__
		cout << ".." << endl << "..could not load Max_AVX_Supported from libDetectUtils64 Shared Library " << endl << ".." << endl;
	#endif
		fputs(error, stderr);
		exit(1);
	}
	AVX_Max_Supp = max_avx_supported();		

	dlclose(handle);

#endif
	
	if (AVXOSsupported == true)
	{
		std::cout << "AVX is supported in your OS" << std::endl;
		AVXOSsupported = true;
		if (AVX_Max_Supp == AVXLevelSupport::AVX_Supported)
		{
			AVXnumber = 1;
			std::cout << "Max AVX supported = AVX" << std::endl << std::endl;
		}
		else if (AVX_Max_Supp == AVXLevelSupport::AVX2_Supported)
		{
			AVXnumber = 2;
			std::cout << "Max AVX supported = AVX2" << std::endl << std::endl;
		}
		else if (AVX_Max_Supp == AVXLevelSupport::AVX512_Supported)
		{
			AVXnumber = 5;
			std::cout << "Max AVX supported = AVX512" << std::endl << std::endl;
		}
		else
			std::cout << "AVX, AVX2 or AVX512 not supported" << std::endl << std::endl;
	}
	
	else
	{
		std::cout << "AVX is NOT supported in your OS" << std::endl << std::endl;
		AVXOSsupported = false;

	}


	std::cout << "Ops Per Sec       CycleRun       Error       Time(sec)\n";
	for (long lQtyCycle = 1;; lQtyCycle++)
	{
		if (!AVXOSsupported)
		{
#pragma omp parallel for schedule(dynamic) reduction(+:primes)
			for (int p = 2; p <= sievesize; p++) {
				primes++;
				primeFactors(p);
			}
		}
		else
		{
			if (AVXnumber == 1)
			{
				if (timeBalance > 30) AVX1PrimeNum(3);
				else if (timeBalance > 5) AVX1PrimeNum(2);
				else  AVX1PrimeNum(1);

			}

			else if (AVXnumber == 2)
			{
				if (timeBalance > 30) AVX2PrimeNum(3);
				else if (timeBalance > 5) AVX2PrimeNum(2);
				else  AVX2PrimeNum(1);

			}
			else if (AVXnumber == 5)
			{
				if (timeBalance > 30) AVX5PrimeNum(3);
				else if (timeBalance > 5) AVX5PrimeNum(2);
				else  AVX5PrimeNum(1);
			}
			else
			{
#pragma omp parallel for schedule(dynamic) reduction(+:primes)
				for (int p = 2; p <= sievesize; p++) {
					primes++;
					primeFactors(p);
				}
			}

		}
		time(&end);
		dif = difftime(end, start);
		timeBalance = runTime - dif;
		timeUpdateOPS = dif - dif2;
		if (timeUpdateOPS >= 1)
		{
			dif2 = dif;
			OpPerSec = primesnumber / timeUpdateOPS;
			primesnumber = 0;

			TotalGenerated = 0;
			std::cout << "\r";
			std::cout.precision(7);
			//std::cout << OpPerSec << "              " << lQtyCycle << "              " << lQtyCycle << "            " << CycleErr << "              " << dif;
			std::cout << OpPerSec << "             " << lQtyCycle << "             "<< CycleErr << "            " << dif;
			std::cout << "    " << "\b" << "    " << std::flush;
		}

		if (dif > (long)1 && dif >= runTime)
		{
			break;
		}
		else if (CycleErr >= 1 && StopOnErr)
		{
			break;
		}
	}
	return CycleErr;
}

void handleArgs(int argc, char *argv[])
{
	//int iHelpSelected = 0;
	// Optional args
	// Make sure the options can only be run one time
	// Load up all the possible args
	std::vector <string> sArgs_v;
	sArgs_v.push_back("-h");
	sArgs_v.push_back("-info");
	sArgs_v.push_back("-resultName");
	sArgs_v.push_back("-s");
	sArgs_v.push_back("-errstop");
	sArgs_v.push_back("-nc");
	sArgs_v.push_back("-c");

	if (argc > 1)
	{
		for (int iArg = 1; iArg < argc; iArg++)
		{
			for (int iVec = 0; iVec < sArgs_v.size(); iVec++)
			{
				if (argv[iArg] == sArgs_v[iVec])
				{
					// Only the options that need to be processed before all the other ones need to be in this list
					if (sArgs_v[iVec] == std::string("-c"))
					{
						iColorText = 1;
					}
				}
			}
		}
		for (int iArg = 1; iArg < argc; iArg++)
		{
			iArgIndex = iArg;
			iOptionValid = 0;
			for (int iVec = 0; iVec < sArgs_v.size(); iVec++)
			{
				if (argv[iArg] == sArgs_v[iVec])
				{
					iOptionValid = 1;
					if (sArgs_v[iVec] == std::string("-h"))
					{
						// Print out version
						if (iPrintVersionFlag != 0)
						{
							PrintVersion();
						}
						/*iHelpSelected = 1;*/
						iRun = 0;
						HelpUseage();
					}
					if (sArgs_v[iVec] == std::string("-info"))
					{
						iRun = 0;
						iPrintVersionFlag = 0;
						iPrintSuccessFlag = 0;
						std::cout << "\"parallel:yes|socket:yes|core:yes\"" << std::endl;
					}
					if (sArgs_v[iVec] == std::string("-resultName"))
					{
						sgGoldModuleResultsFile = argv[++iArg];
					}
					if (sArgs_v[iVec] == std::string("-s"))
					{
						if (isOptionValid(iArg, argc, argv, VarType::vtINT))
						{
							sPNTimer = argv[iArg + 1];
							iArg++;

						}
						else
						{
							iRun = 0;
							iOptionValid = 0;
						}
					}
					if (sArgs_v[iVec] == std::string("-errstop"))
					{
						// Higher CPU Freq tolerance
						// std::cout << std::endl << "Entering high tolerance opt" << std::endl;
						sStopOnError = true;
					}
					if (sArgs_v[iVec] == std::string("-nc"))
					{
						// No Compare between expected and detected, display only
						// std::cout << std::endl << "Entering no compare opt" << std::endl;
						displayOnly = 1;
					}
					// Erase current vector so the option doesn't repeat
					sArgs_v.erase(sArgs_v.begin() + (iVec));
				}
			}
			if (iOptionValid == 0)
			{
				break;
			}
		}
		if (iOptionValid == 0)
		{
			std::string sArgTemp = argv[iArgIndex];
			std::string sTempMsg = "Option " + sArgTemp + " invalid!";
			PrintColorMsg(sTempMsg, TextColor::Red);
			iRun = 0;
			igPassFailStatus = ReturnValueDef::InvalidArgs;
			HelpUseage();
		}
	}
	else
	{
		// Default message if no arguments are used  ... it may be the useage message or it may be OK that no args are used
		//std::cout << "No args used!!" << std::endl;
	}
}

void PrintSuccess(void)
{
	if (igPassFailStatus == ReturnValueDef::Success)
	{
		//PrintColorMsg("Math_PrimeNum Success!", TextColor::Green);
	}
	if (igPassFailStatus == ReturnValueDef::Fail)
	{
		PrintColorMsg("Math_PrimeNum Fail!", TextColor::Red);
	}
	if (igPassFailStatus == ReturnValueDef::Indeterminate)
	{
		PrintColorMsg("Math_PrimeNum Interrupted!", TextColor::Yellow);
	}
	if (igPassFailStatus == ReturnValueDef::InvalidArgs)
	{
		PrintColorMsg("Math_PrimeNum has invalid arguments!", TextColor::Red);
	}
	if (igPassFailStatus == ReturnValueDef::ConfigMismatch)
	{
		PrintColorMsg("Math_PrimeNum has a configuration mismatch!", TextColor::Yellow);
	}
	if (igPassFailStatus != ReturnValueDef::Success)
	{
		cout << "Return Status = " << igPassFailStatus << endl;
	}

}

//..main
int main(int argc, char *argv[])
{
	// .W // windows 
#if defined __WIN_64__ 
	sgTestModuleVersion = "1.0.20.64b.W";
#endif

#if defined __WIN_32__ 
	sgTestModuleVersion = "1.0.20.32b.W";
#endif

	// .L linux
#if defined __LIN_64__
	sgTestModuleVersion = "1.0.19.64b.L";
#endif
#if defined __LIN_32__
	sgTestModuleVersion = "1.0.19.32b.L";
#endif


	// Need to include signal for Softkill functions
	// If this thread is sent a signal SIGINT, we need to send it to the signal function 
	signal(SIGINT, SignalFun);


	// Parse both text local config style and xml style

	Init();
	handleArgs(argc, argv);


	//..clean up result files ... Clean file before another one is written
	CleanUp();

	if (iRun)
	{
		// Print out version
		if (iPrintVersionFlag != 0)
		{
			PrintVersion();
		}

		//std::cout << " --- Prime Number Generation Test --- " << std::endl << std::endl;

		if (displayOnly)
		{
			PrintColorMsg("No Compare Option Used.\n", TextColor::Yellow);
		}

		
		// Fill XML the object
		if (sPNTimer.size())
		{
			long PrimeNumberTimer = ConvertToLong(sPNTimer);
			lQtyErr = RunMathPrimeNum(PrimeNumberTimer, sStopOnError);
		}
		else
		{
			//Default is 2 sec
			lQtyErr = RunMathPrimeNum(2, sStopOnError);
		}


		std::cout << std::endl << std::endl << "Operation Per Second --> " << OpPerSec;
		std::cout << std::endl << "Error --> " << lQtyErr << std::endl;

		if (lQtyErr == 0)
		{
			//std::cout << "Prime Number Generation Test Pass!!!" << std::endl;
			PrintColorMsg("Prime Number Generation Test Passed!!!", TextColor::Green);
			WriteResultsFile(0);
		}
		else
		{
			//std::cout << "Prime Number Generation Test Fail!!!" << std::endl;
			PrintColorMsg("Prime Number Generation Test Failed!!!", TextColor::Red);
			WriteResultsFile(1);
		}
		/*if (igPauseApp)
		{
		PauseWQuit();
		}*/
	}
	if (iPrintSuccessFlag != 0)
	{
		PrintSuccess();
	}
	// Returns 0=pass, 1=fail, 2=indeterminate 
	return igPassFailStatus;
}
