
//..
//..FMA3 Module ..
//..developed by GTS..
//..
//..Intel(R) Corporation (C) 2016
//..

//..includes
#include "Include/Main.h"
#include <immintrin.h>
#include <iostream>
#include <vector>
#include <bitset>
#include <array>
#include <string>
//#include <conio.h>
#include <memory>


#ifdef __WIN_OS__
#include <Windows.h>
#endif
#ifdef __LIN_OS__
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <ivec.h>
#include <fvec.h>
#include <dvec.h>
#include <math.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dlfcn.h>

#endif


#ifdef __LIN_OS__

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));
}

#endif

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

#ifdef __WIN_OS__

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

bool isAVX_OS_Supported;
bool FMA_HWare_Supported;
int AVX_Max_Supp;

void Run_FMA3()
{
	std::string sResult = "Fail --- FMA3 Testing complete ---";

#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");
	pCheckFMA3 pCFMA3 = (pCheckFMA3)GetProcAddress(hInstanceLoadDLL, "DUdll_bCheckFMA3");

	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();
		std::string sDLLversion = pODV();
#ifdef __WIN_32__
		cout << "..DetectUtils DLL Version - " << cDLLversion << endl;
		strPFOut.append("\nDetectUtils DLL Version - " + sDLLversion + "\n");
#elif defined __WIN_64__
		cout << "..DetectUtils64 DLL Version - " << cDLLversion << endl;
		strPFOut.append("\nDetectUtils64 DLL Version - " + sDLLversion + "\n");
#endif
	}


	//Output AVX Features Detection Section to Screen
	std::cout << std::endl << " --- CPU Features Detection --- " << std::endl;
	strPFOut.append("\n  --- CPU Features Detection --- \n");


	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
	{
		isAVX_OS_Supported = 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();
	}

	if (!pCFMA3)
	{
#ifdef __WIN_32__
		cout << ".." << endl << "..could not load checkFMA3 from DetectUtils dll " << endl << ".." << endl;
#elif defined __WIN_64__
		cout << ".." << endl << "..could not load checkFMA3 from DetectUtils64 dll " << endl << ".." << endl;
#endif
		exit(1);
	}
	else
	{
		//get bool returned for FMA3 check from DLL
		FMA_HWare_Supported = pCFMA3();
	}


	//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");
	if ((error = dlerror()) != NULL)
	{
		fputs(error, stderr);
		exit(1);
	}
	#ifdef __LIN_32__
	std::cout << "..DetectUtils.so Version.. --> " << trim(OutputDLLVersion()) << endl;
	strPFOut.append("\n..DetectUtils.so Version.. --> " + trim(OutputDLLVersion()) + "\n");
	#elif defined __LIN_64__
	std::cout << "..DetectUtils64.so Version.. --> " << trim(OutputDLLVersion()) << endl;
	strPFOut.append("\n..DetectUtils64.so Version.. --> " + trim(OutputDLLVersion()) + "\n");
	#endif	

	//Output AVX Features Detection Section to Screen
	std::cout << std::endl << " --- CPU Features Detection --- " << std::endl;
	strPFOut.append("\n  --- CPU Features Detection --- \n");

	//Is AVX supported by OS
	bISFeature_OS_Supported = (bool(*)(void))dlsym(handle, "bISFeature_OS_Supported");
	if ((error = dlerror()) != NULL)
	{
		fputs(error, stderr);
		exit(1);
	}
	isAVX_OS_Supported = bISFeature_OS_Supported();

	//Max AVX Supported
	max_avx_supported = (int(*)(void))dlsym(handle, "max_avx_supported");
	if ((error = dlerror()) != NULL)
	{
		fputs(error, stderr);
		exit(1);
	}
	AVX_Max_Supp = max_avx_supported();

    //FMA3
    bCheckFMA3 = (bool (*)(void))dlsym(handle, "bCheckFMA3");
    if (( error = dlerror() ) != NULL)  
    {
        fputs(error, stderr);
        exit(1);
    }
    FMA_HWare_Supported = bCheckFMA3();

	dlclose(handle);

#endif

//Set Testing Result Flags
	FMA_TestResult_Flag = true;

	//Output CPU Features Detection Section to Screen
	std::cout << (FMA_HWare_Supported ? "..FMA3 is supported by this CPU..\n" : "..FMA3 is NOT supported by this CPU..\n");
	std::cout << (isAVX_OS_Supported ? "..FMA3 is supported by this Operating System..\n" : "..FMA3 is NOT supported by this Operating System..\n");

	//Output CPU Features Detection Section to Test Results File	
	strPFOut.append(FMA_HWare_Supported? "..FMA3 is supported by this CPU..\n" : "..FMA3 is NOT supported by this CPU..\n");
	strPFOut.append(isAVX_OS_Supported ? "..FMA3 is supported by this Operating System..\n" : "..FMA3 is NOT supported by this Operating System..\n");

	if (isAVX_OS_Supported)
	{
		if (FMA_HWare_Supported)
		{
			//Need to check if Feature is supported by Operating System
			std::cout << "\nTesting FMA3....\n";
			strPFOut.append("\n"); 
			if (testfma())
			{
				FMA_TestResult_Flag = true;
				strPFOut.append("FMA3 Test Result --- PASS\n");
				std::cout << "FMA3 Test Result --- PASS" << std::endl;
			}
			else
			{
				FMA_TestResult_Flag = false;
				strPFOut.append("FMA3 Test Result --- FAIL\n");
				std::cout << "FMA3 Test Result --- FAIL" << std::endl;
			}
		}
		else
		{
			std::cout << "FMA3 Test - Not Tested" << std::endl;
			strPFOut.append("FMA3 Test - Not Tested\n");
			strPFOut.append("\n");
			FMA_TestResult_Flag = true;
		}
	}
	else
	{
		std::cout << "\nFMA3 Test Result --- Not Tested" << std::endl << std::endl;
		strPFOut.append("\nFMA3 Test Result --- Not Tested\n\n");
		FMA_TestResult_Flag = true;

		std::cout << "Minimum Operating System requirements are " << std::endl;
#ifdef __WIN_OS__
		std::cout << "Windows 10,8.1,8,7 SP1, Windows Server 2008 R2 SP1" << std::endl;
#endif
#ifdef __LIN_OS__
		std::cout << "Linux kernel version 2.6.30" << std::endl << std::endl;
#endif
		strPFOut.append("Minimum Operating System requirements are \n");
#ifdef __WIN_OS__
		strPFOut.append("Windows 10,8.1,8,7 SP1, Windows Server 2008 R2 SP1\n");
#endif
#ifdef __LIN_OS__
		strPFOut.append("Linux kernel version 2.6.30\n\n");
#endif
	}


	if (FMA_TestResult_Flag)
	{
		igPassFailStatus = 0;
	}
	else
	{
		igPassFailStatus = 1;
	}

}

float fma_calc(float a, float b, float c)
{
	float i;
	float sum = 0;
	sum += fma(a, b, c);
	return sum;
}

float c_calc(float a, float b, float c)
{
	float i;
	float sum = 0;
	sum += (a * b + c);
	return sum;
}







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

void Init(void)
{
	
	tStartTime = std::time(nullptr);	
	if (iDebugFlag)
	{
		std::string sTemp = std::asctime(std::localtime(&tStartTime));
		std::cout << "tStartTime = " << sTemp << std::endl;
	}

	// Logic needs to be done to determine if this test is a pass or fail.  
	//If indeterminate, it means that the test was interrupted
	igPassFailStatus = ReturnValueDef::Success;  // Success
	//igPassFailStatus = 1;  // Fail
	//igPassFailStatus = 2;  // Indeterminate 
	iColorText = 0;
	iSec = 0;
	iMin = 0;
	iHour = 0;
	iOptionValid = 0;
	iRun = 1;
	iArgIndex = 0;
	iPrintVersionFlag = 1;
	iPrintSuccessFlag = 1;


}

void PrintVersion(void)
{
	std::cout << std::endl << "..." << std::endl << "Version: " << sgGoldModuleVersion << 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 = ReturnValueDef::Indeterminate;

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

}

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

void WriteResultsFile(int iPassFailStatus)  
{

	// pass fail logic using iPassFailStatus  with 0=pass, 1=fail, 2=indeterminate

	// Write Results File
	td.WriteToFile(sgGoldModuleResultsFile, "FMA3 Test");
	std::string sTemp = "Module Version: " + sgGoldModuleVersion;
	td.WriteToFile(sgGoldModuleResultsFile, sTemp);
	sTemp = "Start Time: ";
	sTemp.append(std::asctime(std::localtime(&tStartTime)));
	td.WriteToFileNoEndL(sgGoldModuleResultsFile, sTemp);


	if (iPassFailStatus == 0)
	{
		igPassFailStatus = 0;  // Success
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - PASS");

	}
	else if (iPassFailStatus == 1) //Fail
	{
		igPassFailStatus = 1;  // Fail
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - FAIL");
	}

	else if (iPassFailStatus == 2) //Indeterminate
	{
		igPassFailStatus = 2;  // Indeterminate 
		td.WriteToFile(sgGoldModuleResultsFile, "Test Result - INDETERMINATE");
	}

	td.WriteToFile(sgGoldModuleResultsFile, strPFOut);

	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 HelpUseage(void)
{
	//std::cout << "Help or Usage ... version info ... copyright info ..." << std::endl;
	std::cout << std::endl << "Test Information:" << std::endl;
	std::cout << " FMA3 Test Module detects and tests the following Intel Processor Features" << std::endl;
	std::cout << " " << std::endl;
	std::cout << " FMA3 - Fused Multiply Add Instruction" << std::endl;
	std::cout << " " << std::endl;
	std::cout << " Some features may be dependant on your Operating System" << std::endl;
	std::cout << " Operating Systems that support FMA3:" << std::endl;
	std::cout << " Windows 10, 8, 7 SP1, Windows Server 2008 R2 SP1," << std::endl;
	std::cout << " Linux since kernel version 2.6.30" << std::endl;
	std::cout << " " << 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;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -h" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -h" << std::endl << std::endl;
#endif
	std::cout << " -s           = Time in seconds to perform the test" << std::endl;
	std::cout << "                default is 5 seconds." << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -s 10" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -s 10" << std::endl << std::endl;
#endif
	std::cout << " -m           = Time in minutess to perform the test" << std::endl;
	std::cout << "                default is 5 seconds." << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -m 5" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -m 5" << std::endl << std::endl;
#endif
	std::cout << " -hrs          = Time in hours to perform the test" << std::endl;
	std::cout << "                default is 5 seconds." << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -hrs 1" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -hrs 1" << std::endl << std::endl;
#endif
	std::cout << " -resultName  = The resultName switch provides a way to name " << std::endl;
	std::cout << "                the results file as desired." << std::endl;
	std::cout << "                Example :" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -resultName fma3_results_0001.txt " << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -resultName fma3_results_0001.txt " << std::endl << std::endl;
#endif
	std::cout << " -c           = This option will display text in various colors" << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -c" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -c" << std::endl << std::endl;
#endif
	std::cout << " -info        = Information switch that publishes parallel information" << std::endl;
	std::cout << "                using the following scheme:" << std::endl;
	std::cout << "               \"parallel:yes|socket:yes|core:yes\" " << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -info" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -info" << std::endl << std::endl;
#endif
	std::cout << " -pa          = pause app option" << std::endl;
	std::cout << "                Example:" << std::endl;
#ifdef __WIN_OS__
	std::cout << "                fma3.exe -pa" << std::endl << std::endl;
#endif
#ifdef __LIN_OS__
	std::cout << "                ./fma3 -pa" << std::endl << std::endl;
#endif
#ifdef __WIN_32__
	std::cout << "Dependencies:" << std::endl;
	std::cout << "      libmmd.dll " << std::endl;
	std::cout << "      svml_dispmd.dll " << std::endl;
	std::cout << "      libiomp5md.dll " << std::endl;
	std::cout << "      C:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool\\DetectUtils.dll " << std::endl;
#endif
#ifdef __WIN_64__
	std::cout << "Dependencies:" << std::endl;
	std::cout << "      libmmd.dll " << std::endl;
	std::cout << "      svml_dispmd.dll " << std::endl;
	std::cout << "      libiomp5md.dll " << std::endl;
	std::cout << "      C:\\Program Files\\Intel Corporation\\Intel Processor Diagnostic Tool 64bit\\DetectUtils64.dll " << std::endl;
#endif
#ifdef __LIN_32__
	std::cout << "Dependencies:" << std::endl;
	std::cout << "      libDetectUtils.so.1.1 " << std::endl;
#endif
#ifdef __LIN_64__
	std::cout << "Dependencies:" << std::endl;
	std::cout << "      libDetectUtils64.so.1.1 " << std::endl;
#endif
	std::cout << std::endl << " Copyright (C) 2017, 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;
}



void handleArgs(int argc, char *argv[])
{

	// 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("-stress");
	sArgs_v.push_back("-hrs");
	sArgs_v.push_back("-m");
	sArgs_v.push_back("-s");
	//sArgs_v.push_back("-d");
	//sArgs_v.push_back("-p");
	//sArgs_v.push_back("-plx");
	//sArgs_v.push_back("-plt");
	sArgs_v.push_back("-pa");
	sArgs_v.push_back("-c");
	//sArgs_v.push_back("-r");

	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"))
					{
						iRun = 0;
						std::cout << std::endl << " --- FMA3 Test --- ";
						PrintVersion();
						HelpUseage();
						iPrintVersionFlag = 0;
						iPrintSuccessFlag = 0;
					}
					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("-stress"))
					{
						// Hours
						if (isOptionValid(iArg, argc, argv, VarType::vtINT))
						{
							iStress = CustomAsciiToInteger(argv[++iArg]);
							//cout << "iStress - " << iStress << endl;
						}
						else
						{
							iRun = 0;
							iOptionValid = 0;
						}

					}

					if (sArgs_v[iVec] == std::string("-hrs"))
					{
						// Hours
						if (isOptionValid(iArg, argc, argv, VarType::vtINT))
						{
							iHour = CustomAsciiToInteger(argv[++iArg]);
							iFMAtimer = iHour * 60 * 60;
						}
						else
						{
							iRun = 0;
							iOptionValid = 0;
						}

					}
					if (sArgs_v[iVec] == std::string("-m"))
					{
						// Minutes
						if (isOptionValid(iArg, argc, argv, VarType::vtINT))
						{
							iMin = CustomAsciiToInteger(argv[++iArg]);
							iFMAtimer = iMin * 60;
						}
						else
						{
							iRun = 0;
							iOptionValid = 0;
						}
					}
					if (sArgs_v[iVec] == std::string("-s"))
					{
						// Seconds
						if (isOptionValid(iArg, argc, argv, VarType::vtINT))
						{
							iSec = CustomAsciiToInteger(argv[++iArg]);
							iFMAtimer = iSec;
						}
						else
						{
							iRun = 0;
							iOptionValid = 0;
						}
					}
					if (sArgs_v[iVec] == std::string("-d"))
					{
						// debug
						iDebugFlag = 1;
					}
					if (sArgs_v[iVec] == std::string("-p"))
					{
						std::cout << std::endl << "Entering print opt" << std::endl;
						igPrintConfig = 1;
					}
					if (sArgs_v[iVec] == std::string("-pa"))
					{
						std::cout << std::endl << "Entering pause app opt" << std::endl;
						igPauseApp = 1;
					}
					if (sArgs_v[iVec] == std::string("-c"))
					{
						std::cout << std::endl << "Entering color text opt" << std::endl;
#ifdef __WIN_OS__
						HANDLE hCommandWindowTextColor = GetStdHandle(STD_OUTPUT_HANDLE);
						SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_GREEN | FOREGROUND_INTENSITY));
						//std::cout << "Some green text ..." << std::endl << std::endl;
						SetConsoleTextAttribute(hCommandWindowTextColor, (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN));
#endif
#ifdef __LIN_OS__
						std::cout << "\033[1;32m";
						//std::cout << std::endl << ".." << std::endl << "Some green text ..." << std::endl << std::endl;
						std::cout << "\033[0m";
#endif
					}


					// Erase current vector so the option doesn't repeat
					sArgs_v.erase(sArgs_v.begin() + (iVec));
				}

			}
			if (iDebugFlag)
			{
				std::cout << std::endl << "iOptionValid = " << iOptionValid << std::endl;
			}
			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("FMA3 Module Success!", TextColor::Green);
	}
	if (igPassFailStatus == ReturnValueDef::Fail)
	{
		PrintColorMsg("FMA3 Module Fail!", TextColor::Red);
	}
	if (igPassFailStatus == ReturnValueDef::Indeterminate)
	{
		PrintColorMsg("FMA3 Module Interrupted!", TextColor::Yellow);
	}
	if (igPassFailStatus == ReturnValueDef::InvalidArgs)
	{
		PrintColorMsg("FMA3 Module has invalid arguments!", TextColor::Red);
	}
	if (igPassFailStatus == ReturnValueDef::ConfigMismatch)
	{
		PrintColorMsg("FMA3 Module has a configuration mismatch!", TextColor::Yellow);
	}	
	if (igPassFailStatus != ReturnValueDef::Success)
	{
		cout << "Return Status = " << igPassFailStatus << endl;
	}

}

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

bool testfma()
{
	int OMPnum = 0;

	const unsigned int loopsize = 19000000;
	const unsigned int loopstart = iStress * 1000;

	float *a = 0;
	float *b = 0;
	float *c = 0;
	
	long dif = 0;
	time_t start, end;

	time(&start);

	while (dif <= iFMAtimer)
	{
		//Test Here
		
		a = new float[loopsize];
		b = new float[loopsize];
		c = new float[loopsize];
		float d = 0;
		float e = 0;
		float f = 0;

		#pragma omp parallel for schedule(dynamic) reduction(+:OMPnum)
		for (int i = loopstart; i < loopsize; i++)
		{
			OMPnum++;
			a[i] = i*exp(exp(exp(i)));
			b[i] = i*exp(exp(exp(i)));
			c[i] = i*exp(exp(exp(i)));
			d = a[i];
			e = b[i];
			f = c[i];
			//Calculate using Fused Multiply Add (d*e+f)
			float Calc_FMA = fma_calc(d, e, f);
			//Calculate using c++ (d*e+f)
			float Calc_c = c_calc(d, e, f);
			//Compare FMA to C++ calculation
			if (Calc_FMA != Calc_c)
			{
				FMA_TestResult_Flag = false;
			}
		}
		delete[] a;
		delete[] b;
		delete[] c;
		time(&end);
		dif = difftime(end, start);
	}

	if (FMA_TestResult_Flag)
	{
		return true;
	}
	else {
		return false;
	}
}


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

	#if defined __WIN_32__ 
	sgGoldModuleVersion = "1.0.19.1.32b.W";
	#endif

	// .L linux
#if defined __LIN_64__
	sgGoldModuleVersion = "1.0.18.64b.L";
#endif
#if defined __LIN_32__
	sgGoldModuleVersion = "1.0.18.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);

	// Print out version
	if (iPrintVersionFlag != 0)
	{
		std::cout << std::endl << " --- FMA3 Test --- ";
		PrintVersion();
	}


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

	if (iRun)
	{
		if (iStress <= 1)
		{
			iStress = 1;
		}
		if (iStress >= 10)
		{
			iStress = 10;
		}

		Run_FMA3();
		WriteResultsFile(igPassFailStatus);
	}

	if (iPrintSuccessFlag != 0)
	{
		PrintSuccess();
	}

	if (igPauseApp)
	{
		PauseWQuit();
	}

	return igPassFailStatus;
}
