/**************************************************************************
 *
 * timing.c -- Program timing routines
 * Copyright (C) 1994  Neil Sharman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************/

#include "sysfuncs.h"
#include "timing.h"


/*
 * Return the time in seconds since 00:00:00 GMT, Jan. 1, 1970 to the
 * best precision possible
 *
 */
double 
RealTime (void)
{
  return ((double) time (NULL));
}



/*
 * Return the amount of system and user CPU used by the process to date to 
 * the best precision possible. If user is non-null then it is initialised to 
 * the user time. If sys is non-null then it is initialised to the system time.
 *
 */
double 
CPUTime (double *user, double *sys)
{

#if defined(HAVE_TIMES)

  struct tms buffer;
  static double clk_tck = 0;
  double u, s;

  times (&buffer);

  if (clk_tck == 0)
    clk_tck = CLK_TCK;

  u = (double) buffer.tms_utime / clk_tck;
  s = (double) buffer.tms_stime / clk_tck;
  if (user)
    *user = u;
  if (sys)
    *sys = s;
  return u + s;

#elif defined(HAVE_GETRUSAGE)

  struct rusage ruse;
  getrusage (RUSAGE_SELF, &ruse);
  if (user)
    *user = (double) ruse.ru_utime.tv_sec +
      (double) ruse.ru_utime.tv_usec / 1000000;
  if (sys)
    *sys = (double) ruse.ru_stime.tv_sec +
      (double) ruse.ru_stime.tv_usec / 1000000;
  return ((double) ruse.ru_utime.tv_sec + ruse.ru_stime.tv_sec +
	((double) ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec) / 1000000);

#else

  /* [RPAP - Feb 97: WIN32 Port] */
#ifdef __WIN32__
  if (user) *user = 0.0;
  if (sys) *sys = 0.0;
  return 0.0;
#else
  -->Can NOT find a system time routine to use ! !!
#endif  /* __WIN32__ */

#endif

}




/*
 * Get the Real and CPU time and store them in a ProgTime structure
 */
void 
GetTime (ProgTime * StartTime)
{
  StartTime->RealTime = RealTime ();
  StartTime->CPUTime = CPUTime (NULL, NULL);
}




/*
 * Display the Real and CPU time elapsed since the StartTime anf FinishTime 
 * structures were initialised. If FinishTime is NULL then FinishTime is
 * Now.
 */
char *
ElapsedTime (ProgTime * StartTime,
	     ProgTime * FinishTime)
{
  static char buf[50];
  double Real, CPU, hour, min, sec;
  if (!FinishTime)
    {
      Real = RealTime () - StartTime->RealTime;
      CPU = CPUTime (NULL, NULL) - StartTime->CPUTime;
    }
  else
    {
      Real = FinishTime->RealTime - StartTime->RealTime;
      CPU = FinishTime->CPUTime - StartTime->CPUTime;
    }
  hour = floor (CPU / 3600);
  min = floor ((CPU - hour * 3600) / 60);
  sec = CPU - hour * 3600 - min * 60;
  sprintf (buf, "%02.0f:%02.0f:%05.2f cpu, %02d:%02d:%02d elapsed.",
	   hour, min, sec,
	   ((int) ceil (Real)) / 3600,
	   (((int) ceil (Real)) % 3600) / 60, ((int) ceil (Real)) % 60);
  return (buf);
}


#define MILLION 1000000

/* ===============================================================================
 * Function: cputime_string
 * Description:
 *      Prints out given cpu time into a string buffer.
 * Input: cpu-time in clock_t or timeval format
 * Output: pointer to internal buffer 
 * =============================================================================== */

#ifdef HAVE_TIMES
char *
cputime_string (clock_t clk)
{
  static char buf[15];
  double CPU, hour, min, sec;

  CPU = (double) clk / (double) CLK_TCK;
  hour = floor (CPU / 3600);
  min = floor ((CPU - hour * 3600) / 60);
  sec = CPU - hour * 3600 - min * 60;
  sprintf (buf, "%02.0f:%02.0f:%05.2f", hour, min, sec);
  return (buf);
}
#else
char *
cputime_string (struct timeval *t)
{
  static char buf[15];
  double CPU, hour, min, sec;

  CPU = (double) t->tv_sec + (double) t->tv_usec / MILLION;
  hour = floor (CPU / 3600);
  min = floor ((CPU - hour * 3600) / 60);
  sec = CPU - hour * 3600 - min * 60;
  sprintf (buf, "%02.0f:%02.0f:%05.2f", hour, min, sec);
  return (buf);
}
#endif



/* ===============================================================================
 * Function: time_normalise
 * Description:
 *      If the micro-second component is negative or over a million,
 *      then must take the over/under flow and add it to the second component.
 * Usage:
 *      Use this routine when you have just been doing straight addition and
 *      subtraction on the micro-second component. For this could lead to a
 *      negative number or over a million.
 *
 * =============================================================================== */

#ifndef HAVE_TIMES

#ifndef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
static 
#endif
void
time_normalise (struct timeval *t)
{
  while (t->tv_usec < 0)
    {
      t->tv_usec += MILLION;
      t->tv_sec--;
    }
  while (t->tv_usec > MILLION)
    {
      t->tv_usec -= MILLION;
      t->tv_sec++;
    }
}
#endif
