/*Subset of procedures for benchmarking.*/
//#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "bench.h"


float CallibrationTime = 110;	//Time used for callibration.


#ifdef __BORLANDC__
  #if defined(__OS2__) || defined(__WIN32__)  
    #define _USE_TIME_H_  
  #else
  #define CLOCKS_PER_SEC 18.0
  double GetInterval(void)
  {
  unsigned int lo,hi;
  asm {
        MOV     AH,0
        INT     1Ah
        MOV     lo,DX
        MOV     hi,CX
      }
  return(1000.0*(65536.0*hi+lo)/18.0);
  }
  #endif
#elif defined(__GNUC__)
  #ifdef __DJGPP__
    #include <dos.h>
    #define CLOCKS_PER_SEC 18.0
    double GetInterval(void)
    {
    union REGS in,out;
     in.h.ah=0;
     int386(0x1A,&in,&out);
    return(1000.0*(65536.0*out.w.cx+out.w.dx)/18.0);
    }
  #else
    #include <sys/time.h>
    double GetInterval(void)
    {
    struct timeval tv;
     gettimeofday(&tv,NULL);
    return(1000*(double)tv.tv_sec+(double)tv.tv_usec/1000.0);
    } 
  #endif     
#elif defined(_MSC_VER)
 #if _MSC_VER<=800
  #define _USE_TIME_H_  
 #else
  #include <windows.h>
  #ifdef _WIN32
    static LARGE_INTEGER pFrequency = {0};

    double GetInterval(void)
    {
      LARGE_INTEGER CurrentTime;

      if(pFrequency.QuadPart==0)
        if(QueryPerformanceFrequency(&pFrequency)==0)
          pFrequency.QuadPart = 1;

      if(pFrequency.QuadPart <= 1) return GetTickCount();

      QueryPerformanceCounter(&CurrentTime);
      return (1000.0*CurrentTime.QuadPart) / pFrequency.QuadPart;
    }
    #define HIGH_RESOLUTION
  #else    
    #include <winreg.h>
    double GetInterval(void)
    {
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft); // pointer to a file time in 100ns intervals  
    return(ft.dwLowDateTime/10000.0 + (ft.dwHighDateTime&0xFFFF)/(double)(0x100000000/10000));
    }
  #endif
 #endif  
#elif defined(__WATCOMC__)
 #define _USE_TIME_H_
#else
double GetInterval(void)
{
  return(0);
}
#endif


#ifdef _USE_TIME_H_
#include <time.h>
double GetInterval(void)
{
  return((1000.0*clock())/CLOCKS_PER_SEC);
}
#endif


#ifdef HIGH_RESOLUTION
  #define MEASURE_TIME	1000
#else
  #define MEASURE_TIME	5000
#endif
long MeasureTime = MEASURE_TIME;	///< Measuring time for AutoMeasure in ms



/*************************************************************************/


/** Returns one loop iteration time in [us]. */
float Measure(void(procedure)(long i),long i)
{
double L1, L2;
  L1 = GetInterval();
  procedure(i);
  L2 = GetInterval();
//return(1000.0*(labs(L1-L2)/18.0)/(float)i);
  return(1000.0l*fabs(L1-L2)/(float)i);
}


/** Returns one loop iteration time in [us]. */
float AutoMeasure(void(procedure)(long i))
{
unsigned long i,i2;
unsigned iter;
double L1, L2;

    //Wait 100ms and sum cycles
  i = 0;
  iter = 4;
  L2 = GetInterval();
  do
    {
    procedure(iter);
    i += iter;
    iter = 10;
    }
  while((GetInterval()-L2) < CallibrationTime);  
  i2 = (unsigned long)((MeasureTime*(float)i)/(GetInterval()-L2));

    //Wait 100ms and sum cycles again
  iter = 15;
  if(i2>100) iter=30;
  if(i2>1000) iter=100;
  if(i2>10000) iter=1000;
  if(i2>100000) iter=10000;

  i = 0;
  L1 = GetInterval();
  do
    {procedure(iter);i+=iter;}

  while(GetInterval()-L1 < CallibrationTime);
  i = (unsigned long)((MeasureTime*(float)i)/(GetInterval()-L1));

  //printf(" %ld; %ld ops proposed, time %g[ms]\n",i,i2, (double)GetInterval()-L1);

  if(i2>i) i=i2;


  L1 = GetInterval();
  procedure(i);
  L2 = GetInterval();

  //printf(" Measurement time:%g %ld ",fabs(L2-L1),i);
  if(L2-L1 < MeasureTime/2)
    {
      if(L1==L2)
	i2 = 9;
      else			/* either timer is stopped or too short iteration time */
	i2 = MeasureTime/fabs(L2-L1);

      procedure(i2*i);
      L2=GetInterval();
      i*=i2+1;
    }
  return(1000.0*fabs(L1-L2)/(float)i);
}
