/*   
*   timer.h Copyright (C) 2004-2010 Paolo Medici
*
*   This library is free software; you can redistribute it and/or modify it under the terms of the 
*    GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of 
*    the License, or (at your option) any later version.
*
*   This library 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 Lesser General Public 
*    License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License along with this library; if not, 
*   write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

#ifndef _H_TIMER_
#define _H_TIMER_

#ifdef WIN32

/// Win32 GetTime
class Counter {
	double time_factor;    // Time Scaling Factor
public:
	Counter() {
	LONGLONG perf_cnt;
	
	if (QueryPerformanceFrequency((LARGE_INTEGER *) &perf_cnt)) 
		{
	     time_factor=1.0/perf_cnt; 
		} 
	}

	/// Return time in second 
	inline double GetTime()
	{
	LONGLONG time;

	QueryPerformanceCounter((LARGE_INTEGER *) &time);
	return (time * time_factor);
	}
};

#else

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

/// *NIX GetTime
class Counter {
public:
	Counter() 
	{ 
	}

	/// Return time in second 
	inline double GetTime() const
	{
	struct timespec ts;
	clock_gettime(CLOCK_REALTIME, &ts);
	return (ts.tv_sec*1.0 + ts.tv_nsec * 0.000000001);
	}
};


#endif

/// Timer class measure elapsed time from Start
class Timer: public Counter {
	double user_time;
public:
	Timer() : Counter()
	{
	user_time = Counter::GetTime();
	}

	/// Reset time
	inline void Start()
	{
	user_time = Counter::GetTime();
	}

	/// Return Time elapsed from start
	inline double GetTime() const
	{
	return Counter::GetTime() - user_time;
	}

};

/// Statistic class measure average, variance, min, max times between Start and Stop
class Statistic: public Counter {
    int count;
    double min_time, max_time, sum_time, sum2_time;
    double start_time;
  public:
    void Reset() { count = 0; min_time = max_time = sum2_time = sum_time = 0.0; }
    Statistic() { Reset(); }
    
    /// Begin time measure
    void Start()
    {
      start_time = Counter::GetTime();
    }
    
    /// Stop Time Measure
    void Stop()
    {
      double dt = Counter::GetTime() - start_time; 
      sum_time += dt;
      sum2_time += dt*dt;
      if(count == 0)
      {
	min_time = max_time = dt;
      }
      else
      {
	if(dt<min_time)
	  min_time = dt;
	if(dt>max_time)
	  max_time = dt;
      }
      count ++;
    }
    
    /// Return average time
    double Avg() const { return sum_time / count; }
    /// Return variance
    double Var() const { return sum2_time / count - (sum_time / count) * (sum_time / count); }
    /// Return min
    double Min() const { return min_time; }
    /// Return max
    double Max() const { return max_time; }
    
};


#endif