/*
 * testing and timing an implementation of the DES and crypt(3)
 *
 * compile-time option:
 *	ALONE	define this if you DON'T load the des library
 *	NOSTDCRYPT	define this if you don't have a system version
 *
 * Arguments:
 * -c	test & time crypt(3) [short version if no -u, standard version if -u]
 * -d	test & time the DES
 * -f	force timing even if the test fails
 * -h	if verbose flag not given, print a header
 * -n	no timings; only test
 * -o	print ONLY the non-verbose header
 * -r<number>
 *	repeat the timings <number> times
 * -s	test/time the system crypt(3) or encrypt(3)
 * -t<number>
 *	run each timing for <number> seconds
 * -u	use UNIX interface rather than DESZIP interface
 * -v   verbose; print long form of output
 */
/****************************************************************
 * Copyright notice.						*
 * This software is copyrighted (c) 1991 by Matt Bishop and the *
 * Trustees of Dartmouth College.  All rights reserved.		*
 *								*
 * Author:	Matt Bishop					*
 * Address:	Department of Mathematics and Computer Science	*
 *		Dartmouth College				*
 *		Hanover, NH  03755-1831				*
 *		USA						*
 * telephone:	+1 603 646 3267					*
 * fax:		+1 603 646 1312					*
 * internet:	Matt.Bishop@dartmouth.edu			*
 * usenet:	...!decvax!dartvax!Matt.Bishop			*
 ****************************************************************/
 
#ifndef lint
static char *version = "Version GAMMA 6/31/91 Matt.Bishop@dartmouth.edu";
#endif

#include <stdio.h>
#include "stdlib.h"

/*
 * The DES works with strings of 64 bits, so we define this type
 * to make things simple.  We assume "unsigned char" is 8 bits; a
 * dangerous assumption, but ...
 */
typedef unsigned char Uchar;
#ifndef ALONE
#	include "des.h"
#else
	/* these don't matter as the code that depends on */
	/* them is never invoiked when ALONE is defined   */
typedef unsigned long Unit;
typedef Unit A96[4];
typedef A96 *pA96;
#endif

/*
 * the types
 */
#define TST_HDRONLY	-1	/* just print header */
#define	TST_DES		0	/* DES, deszip interface */
#define	TST_UDES	1	/* DES, UNIX interface */
#define	TST_CRYPT	2	/* crypt(3), deszip interface */
#define	TST_UCRYPT	3	/* crypt(3), UNIX interface */
#define	TST_PCRYPT	4	/* crypt(3), precompute interface */
#define	TST_SDES	5	/* DES, UNIX library copy */
#define	TST_SCRYPT	6	/* crypt(3), UNIX library copy */

/*
 * the useful variables
 */
int unixcrypt = 0;		/* use UNIX interfaces */
int what = TST_DES;		/* what to test */
int force = 0;			/* time even if test fails */
int verbose = 0;		/* be quiet */
int header = 0;			/* don't print header if being quiet */
int repcount = 5;		/* repeat timings this much */
int timcount = 10;		/* time for this many seconds */
int go_on = 1;			/* 0 when time to stop */
int intermed = 0;		/* print out intermediate timings */
int notimes = 0;		/* just test, don't time */
extern char *optarg;		/* for processing arguments to options */
char passwd[] = "pastafazool";	/* password for timing encryption */
char salt[] = "Mb";		/* salt for timing encryption */
char msgbuf[64];		/* message buffer for encryption timing */

/*
 * forward declarations
 */
int dodes();			/* run the DES test (and timings) */
int docrypt();			/* run the crypt(3) test (and timings) */
void prheader();		/* print a nice header */
void prinfo();			/* print info about what is being profiled */
void bitdiff();			/* report problem bits */
void expand(), compress();	/* map UNIX format to/from mine */
void time_start(), tick();	/* start the clock */
void time_end(), tock();	/* stop the clock */
void time_average();		/* compute average time of runs */
void time_print();		/* print average time of runs */

/*
 * the library hook
 */
pA96 dzscrypt(), dzucrypt();
char *crypt(), *dzcrypt();
void setkey(), encrypt();

/* ARGSUSED 2 */
void main(argc, argv)
int argc;
char *argv[];
{
	register int i;			/* counter in a for loop */
	int ntests;			/* number of rounds run */
	int failed;			/* number of rounds that failed */
	int rep;			/* repetitions */
	register char *r1;		/* register pointer for timing */
	register char *r2;		/* register pointer for timing */

	/*
	 * if an argument is provided, see what it is
	 */
	while((i = getopt(argc, argv, "cdfhinor:st:uv")) != EOF){
		switch(i){
		case 'c':		/* test crypt(3) */
			what = TST_CRYPT;
			break;
		case 'd':		/* test the DES */
			what = TST_DES;
			break;
		case 'f':		/* force timing if the test fails */
			force = 1;
			break;
		case 'h':		/* print header */
			header = 1;
			break;
		case 'i':		/* show intermediate timings */
			intermed = 1;
			break;
		case 'n':		/* only test */
			notimes = 1;
			break;
		case 'o':		/* print header only */
			header = 1;
			what = TST_HDRONLY;
			prinfo(-1, -1, -1);
			/* NEVER RETURNS */
		case 'r':		/* number of times to time */
			if ((repcount = atoi(optarg)) < 0){
				fprintf(stderr, "-r: needs nonnegative count\n");
				exit(1);
			}
			break;
		case 's':		/* library interface */
			unixcrypt = 2;
			break;
		case 't':		/* timing interval */
			if ((timcount = atoi(optarg)) < 0){
				fprintf(stderr, "-t: needs nonnegative count\n");
				exit(1);
			}
			break;
		case 'u':		/* UNIX interface */
			unixcrypt = 1;
			break;
		case 'v':		/* be wordy */
			verbose = 1;
			break;
		default:		/* ??? */
			fprintf(stderr, "Usage: %s [ -u ]\n", argv[0]);
			exit(1);
		}
	}
	if (unixcrypt == 1)
		switch(what){
		case TST_DES:	what = TST_UDES;	break;
		case TST_CRYPT:	what = TST_UCRYPT;	break;
		}
	else if (unixcrypt == 2)
		switch(what){
		case TST_DES:	what = TST_SDES;	break;
		case TST_CRYPT:	what = TST_SCRYPT;	break;
		}
#ifdef ALONE
	if (what != TST_SDES && what != TST_SCRYPT){
		fprintf(stderr, "%s: only UNIX DES and UNIX crypt supported\n",
					argv[0]);
		exit(1);
	}
#endif
#ifdef NOSTDCRYPT
	if (what == TST_SDES || what == TST_SCRYPT){
		fprintf(stderr, "%s: UNIX DES and UNIX crypt not supported\n",
					argv[0]);
		exit(1);
	}
#endif

	/*
	 * print header
	 */
	 if (verbose)
		prheader();

	/*
	 * assume it will do everything correctly
	 */
	failed = 0;

	/*
	 * do the encryption and count the number of errors
	 */
	ntests = 0;
	do{
		switch(what){
		case TST_DES:			/* do next DES test */
			i = dodes(ntests, 0);
			break;
		case TST_UDES:			/* do next DES test (UNIX) */
			i = dodes(ntests, 1);
			break;
		case TST_SDES:			/* do next DES test (std lib) */
			i = dodes(ntests, 2);
			break;
		case TST_CRYPT:			/* do next crypt test */
			i = docrypt(ntests, 0);
			break;
		case TST_UCRYPT:		/* do next crypt test (UNIX) */
			i = docrypt(ntests, 1);
			break;
		case TST_SCRYPT:		/* do next crypt test (std lib) */
			i = docrypt(ntests, 2);
			break;
		}
		switch(i){
		case 1:	ntests++; failed++; break;
		case 0:	ntests++; break;
		}
	} while(i != -1);

	/*
	 * announce the results
	 */
	if (verbose)
		printf("testing summary: %d tests, %d successes, %d failures\n",
					ntests, ntests - failed, failed);
	else
		prinfo(ntests, ntests - failed, failed);

	/*
	 * if things failed, don't bother timing
	 */
	if (notimes || (failed > 0 && !force)){
		if (!verbose)
			putchar('\n');
		exit(failed);
	}

	/*
	 * do the runs and count the number of encryptions
	 */
	for(rep = 0; rep < repcount; rep++){
		i = 0;
		go_on = 1;
		switch(what){
		case TST_DES:			/* do DES timing */
			r1 = msgbuf;
			time_start(timcount);
			for( ; go_on; i++)
				des_run(r1);
			time_end(i);
			break;
		case TST_UDES:			/* do DES timing (UNIX) */
			r1 = msgbuf;
			time_start(timcount);
			for( ; go_on; i++)
				dzencrypt(r1, 0);
			time_end(i);
			break;
		case TST_SDES:			/* do DES timing (std lib) */
			r1 = msgbuf;
			time_start(timcount);
			for( ; go_on; i++)
				encrypt(r1, 0);
			time_end(i);
			break;
		case TST_CRYPT:			/* do crypt timing */
			r1 = passwd;
			r2 = salt;
			time_start(timcount);
			for( ; go_on; i++)
				(void) dzscrypt(r1, r2);
			time_end(i);
			break;
		case TST_UCRYPT:		/* do crypt timing (UNIX) */
			r1 = passwd;
			r2 = salt;
			time_start(timcount);
			for( ; go_on; i++)
				(void) dzcrypt(r1, r2);
			time_end(i);
			break;
		case TST_SCRYPT:		/* do crypt timing (std lib) */
			r1 = passwd;
			r2 = salt;
			time_start(timcount);
			for( ; go_on; i++)
				(void) crypt(r1, r2);
			time_end(i);
			break;
		}
		/*
		 * subtract out the overhead
		 */
		tick();
		for( ; i; i--)
			;
		tock();
		time_print(rep);
	}
	time_average(repcount);

	/*
	 * bye!
	 */
	exit(failed);
}

#define SALTARR(indf, indv, modf, modv, arrf, arrv, salf, salv)		\
	switch(indf){							\
	case 0:  indv = "type punning indexing mode";		break;	\
	case 1:  indv = "usual indexing mode";			break;	\
	default: indv = "unknown indexing mode";		break;	\
	}								\
	switch(modf){							\
	case 0:  modv = "auto-increment addressing mode";	break;	\
	case 1:  modv = "direct addressing mode";		break;	\
	default: modv = "unknown addressing mode";		break;	\
	}								\
	switch(arrf){							\
	case 0:  arrv = "shift-and-mask bit access";		break;	\
	case 1:  arrv = "bitfields bit access";			break;	\
	case 2:  arrv = "bytes bit access";			break;	\
	default: arrv = "unknown bit access";			break;	\
	}								\
	switch(salf){							\
	case 0:  salv = "on-fly salting";			break;	\
	case 1:  salv = "precomputed salting";			break;	\
	default: salv = "unknown salting";			break;	\
	}

/*
 * print the header
 */
void prheader()
{
	char *ind, *mod, *arr, *sal;

	switch(what){
	case TST_DES:			/* DES, deszip interface */
		SALTARR(des_tindex(), ind, des_pmode(), mod, 
				des_arrange(), arr, des_howsalt(), sal)
		printf(
		"DES: base %d, path %d, kperm %d\n\t%s, %s, %s, %s\n\tDESZIP interface\n",
			des_wbits(), des_path(), des_nkey(), 
							arr, sal, mod, ind);
		break;
	case TST_UDES:			/* DES, UNIX interface */
		SALTARR(des_tindex(), ind, des_pmode(), mod, 
				des_arrange(), arr, des_howsalt(), sal)
		printf(
		"DES: base %d, path %d, kperm %d\n\t%s, %s, %s, %s\n\tUNIX interface\n",
			des_wbits(), des_path(), des_nkey(),
							 arr, sal, mod, ind);
		break;
	case TST_SDES:			/* DES, UNIX library function */
		printf("DES: UNIX library function (UNIX interface)\n");
		break;
	case TST_CRYPT:			/* crypt(3), deszip interface */
		SALTARR(sdes_tindex(), ind, sdes_pmode(), mod, 
				sdes_arrange(), arr, sdes_howsalt(), sal)
		printf(
		"CRYPT: base %d, path %d, kperm %d\n\t%s, %s, %s, %s\n\tDESZIP interface\n",
			sdes_wbits(), sdes_path(), sdes_nkey(), 
							arr, sal, mod, ind);
		break;
	case TST_UCRYPT:
		SALTARR(cdes_tindex(), ind, cdes_pmode(), mod, 
				cdes_arrange(), arr, cdes_howsalt(), sal)
		printf(
		"CRYPT: base %d, path %d, kperm %d\n\t%s, %s, %, %ss\n\tUNIX interface\n",
			cdes_wbits(), cdes_path(), cdes_nkey(), 
							arr, sal, mod, ind);
		break;
	case TST_SCRYPT:
		printf("CRYPT: UNIX library function (UNIX interface)\n");
		break;
	}
}

#define SETSALTARR(indf, indv, modf, modv, arrf, arrv, salf, salv)	\
		switch(indf){						\
		case 0:  indv = "ty"; break;				\
		case 1:  indv = "in"; break;				\
		default: indv = "??"; break;				\
		}							\
		switch(modf){						\
		case 0:  modv = "ai"; break;				\
		case 1:  modv = "im"; break;				\
		default: modv = "??"; break;				\
		}							\
		switch(arrf){						\
		case 0:  arrv = "sm"; break;				\
		case 1:  arrv = "bf"; break;				\
		case 2:  arrv = "by"; break;				\
		default: arrv = "??"; break;				\
		}							\
		switch(salf){						\
		case 0:  salv = "of"; break;				\
		case 1:  salv = "pc"; break;				\
		default: salv = "??"; break;				\
		}
/*
 * print the information
 */
 void prinfo(ntests, good, bad)
int ntests;
int good;
int bad;
{
	char *ind, *mod, *arr, *sal;

	if (header)
		printf("what  ver path key bits salt mode indx int test succ fail       rps      spr\n");

	switch(what){
	case TST_HDRONLY:		/* just print the header */
		exit(0);
		/* FALLS THROUGH */
	case TST_DES:			/* DES, deszip interface */
		SETSALTARR(des_tindex(), ind, des_pmode(), mod,
				 des_arrange(), arr, des_howsalt(), sal)
		printf("des    %2d  %2d   %d   %2s   %2s   %2s   %2s   D  ",
			des_wbits(), des_path(), des_nkey(),
							 arr, sal, mod, ind);
		break;
	case TST_UDES:			/* DES, UNIX interface */
		SETSALTARR(des_tindex(), ind, des_pmode(), mod,
				 des_arrange(), arr, des_howsalt(), sal)
		printf("des    %2d  %2d   %d   %2s   %2s   %2s   %2s   U  ",
			des_wbits(), des_path(), des_nkey(),
							 arr, sal, mod, ind);
		break;
	case TST_SDES:			/* DES, UNIX library function */
		printf("des   sys  xx   x   xx   xx   xx   xx   S  ");
		break;
	case TST_CRYPT:			/* crypt(3), deszip interface */
		SETSALTARR(sdes_tindex(), ind, sdes_pmode(), mod,
				 sdes_arrange(), arr, sdes_howsalt(), sal)
		printf("crypt  %2d  %2d   %d   %2s   %2s   %2s   %2s   D  ",
			sdes_wbits(), sdes_path(), sdes_nkey(),
							 arr, sal, mod, ind);
		break;
	case TST_UCRYPT:
		SETSALTARR(cdes_tindex(), ind, cdes_pmode(), mod,
				 cdes_arrange(), arr, cdes_howsalt(), sal)
		printf("crypt  %2d  %2d   %d   %2s   %2s   %2s   %2s   U  ",
			cdes_wbits(), cdes_path(), cdes_nkey(),
							 arr, sal, mod, ind);
		break;
	case TST_SCRYPT:
		printf("crypt sys  xx   x   xx   xx   xx   xx   S  ");
		break;
	}
	printf(" %3d  %3d  %3d ", ntests, good, bad);
}


/*
 * this calls the des encryption routine
 */
int dodes(tstct, unixorno)
int tstct;		/* test number */
int unixorno;		/* 1 if UNIX interface, 2 if lib function */
{
	register int i, j;		/* counter in a for loop */
	char exp[64];			/* used to expand Uchars to bits */
	Uchar p[8];			/* used to compress bits to Uchars */
	Uchar pla[8], key[8], enc[8];	/* input */
	int redv;			/* value read */

	/*
	 * read the input
	 */
	for(i = 0; i < 8; i++){
		if (scanf("%X", &redv) != 1)
			return(-1);
		key[i] = redv;
	}
	for(i = 0; i < 8; i++){
		if (scanf("%X", &redv) != 1)
			return(-1);
		pla[i] = redv;
	}
	for(i = 0; i < 8; i++){
		if (scanf("%X", &redv) != 1)
			return(-1);
		enc[i] = redv;
	}
#ifdef PRSTR
printf("plain 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
		pla[0], pla[1], pla[2], pla[3], pla[4], pla[5], pla[6], pla[7]);
printf("key   0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
		key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
printf("crypt 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
		enc[0], enc[1], enc[2], enc[3], enc[4], enc[5], enc[6], enc[7]);
#endif
	
				
	/*
	 * if unix type encryption, need to pass bit arrays
	 */
	if (what == TST_UDES || what == TST_SDES){
#ifdef PRSTR
printf("ENCRYPT ***** \n");
#endif
		/*
		 * expand and set the key
		 */
		expand(key, exp);
		if (what == TST_UDES)	   dzsetkey(exp);
		else if (what == TST_SDES) setkey(exp);
		/*
		 * now encrypt and check
		 */
		expand(pla, exp);
		if (what == TST_UDES)	   dzencrypt(exp, 0);
		else if (what == TST_SDES) encrypt(exp, 0);
		compress(exp, p);
		j = 0;
		for(i = 0; i < 8; i++)
			if (p[i] != enc[i])
				j++;
		if (j)
			bitdiff("enc", p, enc);
#ifdef PRSTR
printf("ENCRYPT %s -- now DECRYPT ***** \n", j ? "failed" : "succeeded");
#endif
		/*
		 * now decrypt and check
		 */
		if (what == TST_UDES)	   dzencrypt(exp, 1);
		else if (what == TST_SDES) encrypt(exp, 1);
		compress(exp, p);
		for(i = 0; i < 8; i++)
			if (p[i] != pla[i])
				j++;
		if (j){
			bitdiff("dec", p, pla);
#ifdef PRSTR
printf("DECRYPT failed ***** \n");
#endif
			return(1);
		}
#ifdef PRSTR
printf("DECRYPT succeeded ***** \n");
#endif
	}
	else{
#ifdef PRSTR
printf("ENCRYPT ***** \n");
#endif
		/*
		 * use the DES algorithms directly
		 */
		for(j = 0; j < 8; j++)
			p[j] = pla[j];
		des_schedule(0);
		des_key(key);
		des_run(p);
		j = 0;
		for(i = 0; i < 8; i++)
			if (p[i] != enc[i])
				j++;
		if (j)
			bitdiff("enc", p, enc);
#ifdef PRSTR
printf("ENCRYPT %s -- now DECRYPT ***** \n", j ? "failed" : "succeeded");
#endif
		des_schedule(1);
		des_key(key);
		des_run(p);
		for(i = 0; i < 8; i++)
			if (p[i] != pla[i])
				j++;
		if (j){
			bitdiff("dec", p, pla);
#ifdef PRSTR
printf("DECRYPT failed ***** \n");
#endif
			return(1);
		}
#ifdef PRSTR
printf("DECRYPT succeeded ***** \n");
#endif
	}

	return(0);
}

/*
 * report differing bits by number
 */
void bitdiff(ebuf, this, real)
char *ebuf;			/* message prefix (enryption, decryption) */
Uchar this[8], real[8];		/* bytes which differ somewhere */
{
	register int i;			/* counter in a for loop */
	Uchar temp[64];

	if (!verbose)
		return;
	fprintf(stderr, "%s: real: ", ebuf);
	expand(real, temp);
	for(i = 0; i < 64; i++)	fputc('0'+temp[i], stderr);
	fprintf(stderr, "\n%*c  this: ", strlen(ebuf), ' ');
	expand(this, temp);
	for(i = 0; i < 64; i++)	fputc('0'+temp[i], stderr);
	fputc('\n', stderr);
}

/*
 * this calls the crypt hashing routine
 */
int docrypt(tstct, unixorno)
int tstct;		/* test number */
int unixorno;		/* 1 if UNIX interface, 2 if library function */
{
	char encpasswd[BUFSIZ];		/* encrypted password */
	char salt[BUFSIZ];		/* salt */
	char passwd[BUFSIZ];		/* plaintext password */
	register char *p;		/* points to result of encryption */
	register int i;			/* counter for result comparison */
	register pA96 q, r;		/* points to results of "decryption" */
	int rval = 0;			/* return value */

	/*
	 * do the encryption and count the number of errors
	 */
	if (scanf("%s%s%s", encpasswd, salt, passwd) != 3)
		return(-1);
	/*
	 * apply the encryption routine to the test data
	 */
	if (unixorno){
		p = (what == TST_UCRYPT) ?
				dzcrypt(passwd, salt) :
					crypt(passwd, salt);
	 	/* print the results */
		if (strcmp(p, encpasswd) != 0){
			if (verbose){
				printf("password test #%2d: %-8.8s (%2s ",
		  				tstct, passwd, salt);
				printf(" salt) --> %s; CORRECT IS %s\n",
						p, encpasswd);
			}
			return(1);
		}
	}
	else{
		q = dzscrypt(passwd, salt);
		r = dzucrypt(encpasswd);
		for(i = 0; i < (sizeof(A96)/sizeof(Unit)); i++)
			if (q[i] != r[i])
				break;
	 	/* print the results */
		if (i != (sizeof(A96)/sizeof(Unit))){
			if (verbose){
				printf("password test #%2d: %-8.8s (%2s ",
		  				tstct, passwd, salt);
				printf(" salt) --> [failed]\n");
			}
			return(1);
		}
	}

	return(rval);
}


/* = used to provide compatibility with UNIX DES routines (see crypt(3)) = */
/*
 * change from 8 bits/Uchar to 1 bit/Uchar
 */
void expand(from, to)
Uchar from[8];			/* 8bit/Uchar string */
char to[64];			/* 1bit/char string */
{
	register int i, j;		/* counters in for loop */

	for(i = 0; i < 8; i++)
		for(j = 0; j < 8; j++)
			to[i * 8 + j] = (from[i]>>(7-j))&01;
}
/*
 * change from 1 bit/char to 8 bits/Uchar
 */
void compress(from, to)
char from[64];			/* 1bit/char string */
Uchar to[8];			/* 8bit/Uchar string */
{
	register int i, j;		/* counters in for loop */

	for(i = 0; i < 8; i++){
	 	to[i] = 0;
		for(j = 0; j < 8; j++)
			to[i] = (from[i * 8 + j]<<(7-j))|to[i];
	}
}

#ifdef ALONE
des_bits() { return(0); }
des_path() { return(0); }
des_permkey() { return(0); }
des_pmode() { return(0); }
des_run() {}
des_schedule(n) int n; { }
des_key(x) Uchar *x; { }
cdes_bits() { return(0); }
cdes_path() { return(0); }
cdes_permkey() { return(0); }
sdes_bits() { return(0); }
sdes_path() { return(0); }
sdes_permkey() { return(0); }
pA96 dzscrypt(p, s) char *p, *s; { return((pA96) NULL); }
pA96 dzucrypt(p) char *p; { return((pA96) NULL); }
void fcsetkey(p) char *p; { }
void fcencrypt(p, n) char *p; int n; { }
char *fccrypt(p, s) char *p, *s; { return((char *) NULL); }
#endif
#ifdef NOSTDCRYPT
void setkey(p) char *p; { }
void encrypt(p, n) char *p; int n; { }
char *crypt(p, s) char *p, *s; { return((char *) NULL); }
#endif

/*============== timing routines ===============*/

#ifdef BSD4
#	include <signal.h>
#	include <sys/time.h>
#	include <sys/resource.h>
#	define	ALARMSIG SIGVTALRM
#	define	utimelt	ru_utime	/* user time element */
#	define	cvttime(x)	/* convert a time to a double */\
		((double)((x).tv_sec)+(((double)((x).tv_usec))/1.0e6))
#	define	gettime(x)	/* get the time used */\
		getrusage(0, x);
#	define	inittime(x,y)	/* set the timer limit */\
		(x)->it_interval.tv_sec = 0L; (x)->it_interval.tv_usec = 0L;\
		(x)->it_value.tv_sec = (long) (y); (x)->it_value.tv_usec = 0L;
#	define	settime(x)	/* set the timer itself */\
		setitimer(ITIMER_VIRTUAL, &(x), (struct itimer *)NULL)
int getrusage();		/* get resource usage */
int setitimer();		/* set an interval timer */
typedef struct rusage TRES;	/* type returned by gettime */
typedef struct itimerval TIMER;	/* type to set timer */
#endif
#ifdef SYSV
#	include <time.h>
#	include <sys/types.h>
#	include <sys/param.h>
#	include <sys/times.h>
#	include <signal.h>
#	ifndef HZ
#		define	HZ	(CLK_TCK)
#	endif
#	define	ALARMSIG SIGALRM
#	define	utimelt	tms_utime	/* user time element */
#	define	cvttime(x)	/* convert a time to a double */\
		((double)(x)/(double)HZ)
#	define	gettime(x)	/* get the time used */\
		times(x);
#	define	inittime(x,y)	/* set the timer limit */\
		*(x) = (unsigned) (2 * (y));
#	define	settime(x)	/* set the timer itself */\
		alarm(x)
typedef struct tms TRES;	/* type returned by gettime */
typedef unsigned TIMER;		/* type to set timer */
#endif

/*
 * These are the variables that hold the times and resources.
 */
TRES res1;		/* resources when timing begins */
TRES res2;		/* resources when timing ends */
double tottim = 0.0;	/* total (virtual/user) time */
long totcnt = 0L;	/* total number of calls */
double cumtim = 0.0;	/* cumulative time so far */
long cumcnt = 0L;	/* cumulative number of calls */

/*
 * handle a timer interrupt
 */
void bing()
{
	/*
	 * reset the alarm
	 */
	(void) signal(ALARMSIG, bing);
	/*
	 * say you got it
	 */
	go_on = 0;
}
		
/*
 * This sets up an interrupt to occur after arg seconds of virtual
 * (user + system) time.  This also grabs the current time for more
 * accurate resolution.
 */
void time_start(arg)
int arg;
{
	TIMER ringat;			/* when to interrupt */

	/*
	 * set up the alarm
	 */
	(void) signal(ALARMSIG, bing);

	/*
	 * initialize the timer and the counts
	 */
	inittime(&ringat, arg);
	tottim = 0.0;
	totcnt = 0L;

	/*
	 * get the current resource usage
	 */
	gettime(&res1);

	/*
	 * set up the interrupt
	 */
	if (settime(ringat) < 0){
		perror("timer problem");
		exit(1);
	}
}

/*
 * end timer, grab and save statistics
 */
void time_end(count)
int count;
{
	/*
	 * get the current resource usage
	 */
	gettime(&res2);

	/*
	 * adjust the time and count accordingly
	 */
	tottim += cvttime(res2.utimelt) - cvttime(res1.utimelt);
	totcnt += count;
}

/*
 * start the timer
 */
void tick()
{
	/*
	 * get the current resource usage
	 */
	gettime(&res1);
}

/*
 * end timer, subtract the relevant time
 */
void tock()
{
	/*
	 * get the current resource usage
	 */
	gettime(&res2);

	/*
	 * adjust the time and count accordingly
	 */
	tottim -= cvttime(res2.utimelt) - cvttime(res1.utimelt);
}


/*
 * analyze and print the results
 */
void time_print(n)
int n;
{
	/*
	 * compute and print total time
	 */
	if (intermed){
		if (verbose && header){
			printf("iter    time         runs          rsp         spr\n");
			header = 0;
		}
		printf("%3d: %10.3e\t%11d\t%10.3e\t%10.3e\n",
			n, tottim, totcnt,
			((double)totcnt)/tottim, tottim/((double)totcnt));
	}
	cumtim += tottim;
	cumcnt += totcnt;
}

/*
 * analyze and print the results
 */
void time_average(n)
int n;
{
	/*
	 * don't do anything if not timing
	 */
	if (n == 0){
		putchar('\n');
		return;
	}
	/*
	 * compute and print total time
	 */
	cumtim /= n;
	cumcnt /= n;
	if (verbose && header){
		printf("iter    time         runs          rsp         spr\n");
		header = 0;
	}
	if (verbose)
		printf("av: %10.3e\t%11d\t%10.3e\t%10.3e\n",
			cumtim, cumcnt,
			((double)cumcnt)/cumtim, cumtim/((double)cumcnt));
	else
		printf("%10d %11.5e\n", 
			(int)(((double)cumcnt)/cumtim), cumtim/((double)cumcnt));
}
