/*
 * C-callable "portable" timer-routines.
 *
 * Copyright (c) Sami Saarinen, CSC, Finland. 9-JAN-93, 12-FEB-93
 * E-mail: sbs@csc.fi
 *
 * Usage: 

 In order to get wall clock, use following skeleton:

  extern double walltime(double *oldtime, double *pad1,
                         double *newtime, double *pad2);
  double time1, time2;
  
  initim(); ! Not actually required 
  time1 = walltime(NULL,NULL,NULL,NULL);

  ... do calculations;

  time2 = walltime(&time1,NULL,NULL,NULL);
  
  Or: (void) walltime(&time1,NULL,&time2,NULL);
 

 In order to get cputimes, use following skeleton:

  extern double cputimes(double *olduser, double *oldsys, 
                         double *newuser, double *newsys);

  double user1, sys1, user2, sys2;
  double total_cputime_spent;

  initim(); ! Not actually required 
  cputimes(NULL, NULL, &user1, &sys1);

  ... do calculations;

  total_cputime_spent = cputimes(&user1, &sys1, &user2, &sys2);

  Or:  total_cputime_spent = user2 + sys2;


 Note that you can also omit the system-time (or user-time)
 replacing corresponding output parameter with NULL:

 total_user_cputime_spent = cputimes(&user1, NULL,  &user2, NULL);

 Or: total_user_cputime_spent = user2; 

 In order to facilitate switching between cputimes and walltime,
 you can redefine in your application the calls to cputimes (or walltime)
 with walltime (or cputimes) using preprocessor: -Dcputimes=walltime 
 (or -Dwalltime=cputimes) during application compile (not times.c compile!)
 

 * Compilation & linking:

 - Sun Sparcstation (with GNU-cc and SYSV-libraries):
 
 % gcc -O2 main.c times.c -L/usr/5lib

 - Convex C-series:

 % cc -O1 -D__convex__ main.c times.c
   or simply:
 % cc main.c times.c

 - Cray XMP/YMP (with standard cc):

 % cc -DCRAY main.c times.c
   or simply:
 % cc main.c times.c

 - Silicon Graphics:

 % cc -O2 -prototypes main.c times.c

 - HP 9000/700-series (with GNU-cc):

 % gcc -O2 main.c times.c

 or (with HP's cc):

 % cc -Aa -DHPPA +O2 main.c times.c

 - DEC ALPHA AXP/OSF1:

 % cc -O2 -DALPHA main.c times.c

*/

#include <unistd.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/param.h>

/* === Start of comment ===

#ifdef __convex__
#include <sys/time.h>
#endif

#ifdef CRAY
#include <time.h>
#endif

#if !defined(HZ) && !defined(CLK_TCK)
#error Neither 'HZ' nor 'CLK_TCK' was defined.
#endif

#ifndef HZ
#define HZ CLK_TCK
#endif
clock_t times (struct tms *buffer);

  === End of comment === */
  

static double clock_ticks = 0.0;
static int initdone = 0;

void initime()
{
  clock_ticks = sysconf(_SC_CLK_TCK);
  initdone = 1;
}


double walltime(double *oldtime, double *pad1,
		double *newtime, double *pad2)
{
  double total = 0.0;
  struct tms buffer;

  if (!initdone) initime();
  total = (double)
    ( times(&buffer) - (oldtime ? (*oldtime) : 0.0) * clock_ticks )
      / clock_ticks;

  if (newtime) *newtime = total;
  if (pad2) *pad2 = 0.0;

  return total;
}


double cputimes(double *olduser, double *oldsys,
		double *newuser, double *newsys)
{
  double total = 0.0;

  if (!initdone) initime();

  if (newuser || newsys) { /* Do only if non-NULL pointers exist */
    struct tms buffer;
    (void) times(&buffer);
    if (newuser) {
      *newuser = 
	(buffer.tms_utime + buffer.tms_cutime - 
	 (olduser ? (*olduser) : 0.0) * clock_ticks) / clock_ticks;
      total += *newuser;
    }
    if (newsys)  {
      *newsys  = 
	(buffer.tms_stime + buffer.tms_cstime - 
	 (oldsys ? (*oldsys) : 0.0) * clock_ticks) / clock_ticks;
      total += *newsys;
    }
  }

  return total;
}

#ifdef CRAY
#define waltim_ WALTIM
#endif

#if defined(HPPA) || defined(RS6K)
#define waltim_ waltim
#endif
  
double waltim_(double *newtime)
{
  double tmp;

  tmp = walltime(NULL,NULL,NULL,NULL);

  if (newtime) *newtime = tmp;

  return tmp;
}
