// toIntTest.cpp : Defines the entry point for the console application.
//

#include <fstream>
#include <iostream>
#include <limits>
#include <math.h>

#undef min
#undef max


#if defined( WIN32 )
//#define QUERY_PERFORMANCE_COUNTER
#define RDTSC
#else
#define GET_TIME_OF_DAY
#endif

#ifdef RDTSC
#include <windows.h>

class perf
{
	__int64 r64;

	__forceinline __int64 getCurrentTime()
	{
		__asm
		{
			//
			//	Serialized instruction ensure all previouse
			//	instructions a done befor reading the performance
			//	counter.
			//
//			cpuid
			//
			//	Read the time stamp counter.
			//
			rdtsc
		}
	} 
	
public:
	__forceinline perf()
	{
		::Sleep( 0 );
		__asm cpuid
		r64 = getCurrentTime();
	}
	
	__forceinline double elapsed()
	{
		__int64 now = getCurrentTime();
		return double(now - r64 );
	}

};

__int64 nCPUFrequency;
double dblCPUFrequency;

inline __int64 getCurrentTimeI()
{
	__asm
	{
		rdtsc
	}
}

inline double getCurrentTime()
{
//	return double(getCurrentTimeI());
	//
	//	The time stamp counter.
	//
	union {
		__int64 r64;
		__int32 r32[2];
	} tsc;
	//
	//	Read the time stamp counter.
	//
	__asm
	{
		//
		//	Serialized instruction ensure all previouse
		//	instructions a done befor reading the performance
		//	counter.
		//
//		cpuid
		//
		//	Read the counter.
		// 
		rdtsc
		//
		//
		//
//		mov tsc.r32[0], eax
//		mov tsc.r32[4], edx		
		movd xmm0,eax
		movd xmm1,edx
		pshufd xmm1, xmm0, 0xF7
		
	}
	
	

	//
	//	Get time in seconds.
	//
	return double(tsc.r64);// / dblCPUFrequency;
}

void initGetCurrentTimeLib_hlpr()
{
	//
	//	Use only one fixed CPU
	//
	BOOL b;
	DWORD_PTR proc_affi;
	DWORD_PTR sys_affi;
	DWORD_PTR exclud_affi;
	GetProcessAffinityMask( GetCurrentProcess(), &proc_affi, &sys_affi );
	exclud_affi = proc_affi & ~sys_affi;
	proc_affi = ( exclud_affi ) ? proc_affi : proc_affi;
	int i = 0;
	while (( proc_affi >>= 1 )) ++i;
	proc_affi = 1 << i;
	b = SetProcessAffinityMask( GetCurrentProcess(), proc_affi );
	//
	//	Set the priority of thread high.
	//
	b = SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS );
	b = SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
	//
	//	Get the frequency.
	//
	nCPUFrequency = 2000000000;
//	QueryPerformanceFrequency(
//			reinterpret_cast<LARGE_INTEGER*>( &nCPUFrequency ) );
	//
	//	Frequency counter supported in CPUs of family x86
	//	starting from Pentium 4 or Pentium 3. So for old CPUs
	//	this will not work.
	//
	//	If CPU doesn't support performance counter then just return.
	//
	if ( !nCPUFrequency )
		puts("WARNING: This CPU doesn't support QueryPerformanceFrequency.");
	//
	//	Convert to double.
	//
	dblCPUFrequency = double(nCPUFrequency);
}
#endif


#ifdef QUERY_PERFORMANCE_COUNTER
#include <windows.h>

double dblCPUFrequency;
inline double getCurrentTime()
{
	//
	//	This call must be quite fast. Since, in x86 architectur
	//	it is one instruction. Yet WIN32 API might added some
	//	additional processing.
	//
	//	\todo Vahagn: add our assembly optimised function.
	//
	__int64 nCPUTickCount;
	QueryPerformanceCounter(
		reinterpret_cast<LARGE_INTEGER*>( &nCPUTickCount )
		);
	//
	//	Get time in seconds.
	//
	return double(nCPUTickCount) / dblCPUFrequency;
}

void initGetCurrentTimeLib_hlpr()
{
	//
	//	Use only one fixed CPU
	//
	BOOL b;
	DWORD_PTR proc_affi;
	DWORD_PTR sys_affi;
	DWORD_PTR exclud_affi;
	GetProcessAffinityMask( GetCurrentProcess(), &proc_affi, &sys_affi );
	exclud_affi = proc_affi & ~sys_affi;
	proc_affi = ( exclud_affi ) ? proc_affi : proc_affi;
	int i = 0;
	while (( proc_affi >>= 1 )) ++i;
	proc_affi = 1 << i;
	b = SetProcessAffinityMask( GetCurrentProcess(), proc_affi );
	//
	//	Set the priority of thread high.
	//
	b = SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS );
	b = SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
	//
	//	Get the frequency.
	//
	__int64 nCPUFrequency;
	QueryPerformanceFrequency(
			reinterpret_cast<LARGE_INTEGER*>( &nCPUFrequency )
		);
	//
	//	Frequency counter supported in CPUs of family x86
	//	starting from Pentium 4 or Pentium 3. So for old CPUs
	//	this will not work.
	//
	//	If CPU doesn't support performance counter then just return.
	//
	if ( !nCPUFrequency )
		puts("WARNING: This CPU doesn't support QueryPerformanceFrequency.");
	//
	//	Convert to double.
	//
	dblCPUFrequency = double(nCPUFrequency);
}
#endif

#ifdef GET_TIME_OF_DAY
#include <sys/time.h>

inline double getCurrentTime()
{
	timeval t;
	gettimeofday(&t,0);
	return (double)t.tv_sec + ((double)t.tv_usec/1000000.0);
}

void initGetCurrentTimeLib_hlpr()
{
}

#endif

void initGetCurrentTimeLib()
{
	initGetCurrentTimeLib_hlpr();

#if 0
for ( int j=0; j < 10000; ++j )
{

	//
	//	Calculate the time expectation and dispersion
	//	of getCurrentTime on this CPU.
	//
	const int nProbeCount = 100000;
	double dblTimeExpect = 0.;
	double dblTimeDispersia = 0.;
	for ( int i = nProbeCount; i; --i )
	{
		register double dblTimeBase = getCurrentTime();
		register double dblTimeCurrent = getCurrentTime();
		double dblTimeDelta = dblTimeCurrent - dblTimeBase;
		dblTimeExpect += dblTimeDelta;
		dblTimeDispersia += dblTimeDelta * dblTimeDelta;
	}
	//
	//	finalize.
	//
	dblTimeExpect /= double( nProbeCount );
	dblTimeDispersia = dblTimeDispersia / double( nProbeCount )
		   			- dblTimeExpect * dblTimeExpect;
	printf( "Expectation: %f\n"
		 	"Dispersion: %f\n",
		 	dblTimeExpect,
			sqrt(dblTimeDispersia) );
	puts( "----------------------------------------------------" );
	}
#endif


#if 0
	const int nProbeCount = 1000;
	double* ddd = new double[ nProbeCount ];
	double* p = ddd;
	double m = std::numeric_limits<double>::max();
	for ( int i = nProbeCount; i; --i )
	{
		register double dblTimeBase = getCurrentTime();
		register double dblTimeCurrent = getCurrentTime();
		*p++ = dblTimeCurrent - dblTimeBase;
		m = std::min( m, dblTimeCurrent - dblTimeBase );
//		printf( "%10.1f\n", dblTimeCurrent - dblTimeBase );
	}

	printf( "%10.1f\n", m );

	std::ofstream o;
	o.open( "times.txt" );
	p = ddd;
	for ( int i = nProbeCount; i; --i )
	{
		o << *p++ << std::endl;
	}
	o << std::endl;
	delete [] ddd;
#endif

#if 1
	
	for ( int j = 0; j < 1000; ++j )
	{
		const int nProbeCount = 10000;
		double m = 1e300;
		for ( int i = nProbeCount; i; --i )
		{
			perf pc;
			__asm cpuid
			__asm cpuid
			__asm cpuid
			__asm cpuid
			double c = pc.elapsed();
			m = min( m, c );
		}
		std::cout << m << std::endl;
	}
#endif

}


