/* RCS $Id: sysguess.c,v 1.12 1995/05/02 10:52:12 ae59 Exp ae59 $*/
/* $Log: sysguess.c,v $
 * Revision 1.12  1995/05/02  10:52:12  ae59
 * add rusage test
 *
 * Revision 1.11  1995/04/27  16:08:25  ae59
 * changed the format of the output table
 *
 * Revision 1.10  1995/04/12  12:47:15  ae59
 * add char test
 *
 * Revision 1.9  1995/03/17  16:21:50  ae59
 * add 64bit floating point union test
 *
 * Revision 1.8  1995/02/10  09:55:21  ae59
 * add system library request for nans
 *
 * Revision 1.7  1995/01/27  17:08:14  ae59
 * more comments, prettify output, add test for ieee decompose with
 * unions.
 *
 * Revision 1.6  1995/01/26  09:37:16  ae59
 * add comments, checked for 64 bit systems, more portability
 *
 * Revision 1.5  1995/01/25  14:00:31  ae59
 * changed format string, explicit type casting, include string.h
 *
 * Revision 1.4  1995/01/25  13:25:04  ae59
 * changed signaling, and output routines, check shift for negatives
 *
 * Revision 1.3  1994/09/29  10:04:02  ae59
 * args for main corrected
 *
 * Revision 1.2  1994/09/29  10:00:25  ae59
 * new
 *
 * Revision 1.1  1994/09/26  16:19:38  ae59
 * Initial revision
 *
 */
/*-----------------------------------------------------------*
 *DESCRIPTION : Guess system  parameters                     *
 *              This file is supposed to work on every system*
 *-----------------------------------------------------------*
 */
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <limits.h>
#ifdef __STDC__
#define _PROTOTYPE(fn, par) fn par
#define NOSTDC ""
#else
#define _PROTOTYPE(fn, par) fn ()
#define NOSTDC "not"
#endif

/*-----------------------------------------------*/
/* configuration                                 */
/*-----------------------------------------------*/
#ifdef NO_USE_IEEE
#define USE_IEEE	0
#else
#define USE_IEEE	1
#endif
#ifdef NO_USE_SIGNAL
#define USE_SIGNAL	0
#else
#define USE_SIGNAL	1
#endif
#ifdef USE_SYSTEM_NAN
#define USE_NAN   	1
#else
#define USE_NAN	0
#endif

#ifdef USE_RUSAGE 
#define USE_TEST_RUSAGE	1
#else
#define USE_TEST_RUSAGE  0
#endif
/*-----------------------------------------------*/
/* end configuration                             */
/*-----------------------------------------------*/
#define SUPPOSE_64BIT 	(LONG_MAX-INT_MAX)
#define A_LONGWORD 		0x12345678L
#define ZERO  			0.0
#define ONE 			1.0
#ifdef __STDC__
#define CHECKPP 		0	
#else
#define CHECKARG(s) 	'x'-'s'
#if CHECKARG(x) 
#define CHECKPP 		0	
#else
#define CHECKPP 		1	
#endif
#endif
#if CHECKPP
#define CHECKPPSTR		"in strings"
#define TYPE(type)		"type"
#else
#define CHECKPPSTR		"as ANSI proposes"
#define TYPE(type)		#type
#endif
/*------------------------------------------------------------------*/
/* NAME       : TestIntel                                           */
/* DESCRIPTION: Checks Endianness (Byte ordering) of system         */
/*------------------------------------------------------------------*/
#define TestIntel { unsigned long x=A_LONGWORD; char *c=(char*)&x;\
               intel= (int)((char)(x % 0x100)==*c);\
	          if (intel) puts(" Little endian supposed, like IBM PCs.");\
          	else  puts(" Big endian supposed, like SUN Sparcs.");}

/*------------------------------------------------------------------*/
/* NAME       : NIBBLES (type  )                                    */
/* DESCRIPTION: Gives number of nibbles of a type in int            */
/*------------------------------------------------------------------*/
#define NIBBLES(a) (int)(2*sizeof(a))
/*------------------------------------------------------------------*/
/* NAME       : WRITEHEX(REAL a)                                    */
/* DESCRIPTION: print a real as hex                                 */
/*------------------------------------------------------------------*/
#define WRITEHEX(a)\
{ 	char str[20],  buffer[20]; \
	long *ptr=(long*) &(a); int i; \
	size_t len = (sizeof(a) <= sizeof(long))?  sizeof(long): sizeof(a);\
	*str=*buffer='\0';\
	if (intel)\
	 for (i= len/sizeof(long)-1; i>=0; i--)\
	 { sprintf (buffer,"%*.*lx",NIBBLES(long),NIBBLES(long),ptr[i]);\
		strcat(str,buffer);\
	 }\
	else for (i=0; i<= len/sizeof(long)-1; i++)\
	     { sprintf (buffer,"%*.*lx",NIBBLES(long),NIBBLES(long),ptr[i]);\
	       strcat(str,buffer);\
	     }\
     printf("%s",str);\
}


/*------------------------------------------------------------------*/
/* NAME       : Arithm                                              */
/* DESCRIPTION: Counts sizse of datatypes type and gives sizeof     */
/*------------------------------------------------------------------*/
#define Arithm(type) \
{ type len=(type)0, maxi=(type)0, i=(type)0;\
  do { len += 1; maxi=i; i = i*2+1; } while  (!(i<=maxi));\
  printf("  %-15s|%-6lu|%-10lu|%-20lu|%lx\n" ,TYPE(type),(unsigned long)sizeof(type),\
 (unsigned long)(len-((i<0)?0:1)), (unsigned long)maxi, (unsigned long)maxi); \
}

#define trunc(z) (int)(z)
#undef abs
#define abs(x) ((x<0) ? (-x):(x))
_PROTOTYPE(int main,(int argc, char **arg));
_PROTOTYPE(void Arithmetic,(void));
_PROTOTYPE(void DoubleArithmetic,(void));
_PROTOTYPE(void FPUhandler,(int));
_PROTOTYPE(void TestShift,(void));
_PROTOTYPE(void Testunion,(void));

int cnt=0;
int intel=0;

/*------------------------------------------------------------------*/
/* NAME       : Arithmetic                                          */
/* DESCRIPTION: Computes parameters of float arithmetic             */
/*------------------------------------------------------------------*/
/* suppose integer overflow is not detected */
/* ieee traps are ignored */
void Arithmetic()
{
#if USE_IEEE
	int 	k;
	float 	x,y,z;
	int  radix   , precision, maxexp  , minexp ;
	float infty, _maxreal , _minreal, qnan;
#endif

	printf("o Calculate R=R(radix,mantlen,emin,Emax) for float (%lu) bit\n",
		(unsigned long)sizeof(float));
#if USE_IEEE
	printf("  R=R(");
	/* calculate  (x=2^k in (R(b,l,ex,em) : |((x+1)-x)-1| >=1 } */
	x=(float) ONE; 
	k=0;
	do    
	{
		x = x+x;
		y = x + 1;
		z = y - x;
		y = z - 1;
	}
	while (!(abs(y) -1 >= 0)) ;
	y =(float)  ONE;
	/* { calculate l: radix = (2^k+2^(l-1))-2^k <>0 }*/
	do    
	{
		 z = x+y;
		 y = y+y;
		 z = z - x;
	}
	while (!(z != 0));
	radix = trunc(z);
	printf("%d,"         ,radix);

	precision = 0;
	x =(float) ONE;
	do
	{
		precision = precision +1;
		x = x * radix;
		y = x + 1;
	}
     while (!(y-x !=1));
	printf("%d,"         ,precision);
	/*{ fill in  mantissa }*/
	y= (float) (radix - 1);
	maxexp =0;
	do
	{
		maxexp = maxexp+1;
		z = y;                               /*{ filled mantissa }*/
		x = (y+1)*radix;
		y = y * radix + (float) (radix - 1);
	}
     while (!(y == x)); 
	maxexp = maxexp -1; /* { correct maxexp }*/
	do
	{
		maxexp = maxexp +1;
	     _maxreal = z;
		z = z * radix;
		y = z * radix;
	}
     while (!(y == z)); 
	maxexp = maxexp -1; /* { correct maxexp }*/
	infty= y;

	/* { fill in  mantissa }*/
	y= (float) ONE;
	minexp =precision;
	do
	{
		minexp =  (minexp-1);
	     _minreal = y;
		y = y / radix ;
	}
     while (!(y == 0)); 
	printf("  %d(%x),"         ,minexp,abs(minexp));
	printf("  %d(%x))"         ,maxexp,maxexp);
	printf("\n  max: %e(" ,_maxreal); WRITEHEX(_maxreal);
	printf(")\t\tmin: %e(",_minreal); WRITEHEX(_minreal);
	printf(")\n  inf: "); WRITEHEX(infty);
	qnan = (2*y)/y;

	printf("\t\t\t\tqnan: "); WRITEHEX(qnan);
#else 
	puts("  Float analysis skipped ");
#endif /* use_ieee */
	puts("");
}

/*------------------------------------------------------------------*/
/* NAME       : DoubleArithmetic                                    */
/* DESCRIPTION: Computes parameters of double arithmetic            */
/*------------------------------------------------------------------*/
void DoubleArithmetic()
{
#if USE_IEEE
	int 	k;
	double 	x,y,z;
	int  radix   , precision, maxexp  , minexp ;
	double infty, _maxreal , _minreal, qnan;
#endif

	printf("o Calculate R=R(radix,mantlen,emin,Emax) for double (%lu) bit\n",
		(unsigned long)sizeof(double));
#if USE_IEEE
	printf("  R=R(");

	/* calculate  (x=2^k in (R(b,l,ex,em) : |((x+1)-x)-1| >=1 } */
	x=ONE; 
	k=0;
	do    
	{
		x = x+x;
		y = x + 1;
		z = y - x;
		y = z - 1;
	}
	while (!(abs(y) -1 >= 0)) ;
	y = ONE;
	/* { calculate l: radix = (2^k+2^(l-1))-2^k <>0 }*/
	do    
	{
		 z = x+y;
		 y = y+y;
		 z = z - x;
	}
	while (!(z != 0));
	radix = trunc(z);
	printf("%d,"         ,radix);

	precision = 0;
	x =ONE;
	do
	{
		precision = precision +1;
		x = x * radix;
		y = x + 1;
	}
     while (!(y-x !=1));
	printf("%d,"         ,precision);
	/*{ fill in  mantissa }*/
	y= radix - 1;
	maxexp =0;
	do
	{
		maxexp = maxexp+1;
		z = y;                               /*{ filled mantissa }*/
		x = (y+1)*radix;
		y = y * radix + (radix - 1);
	}
     while (!(y == x)); 
	maxexp = maxexp -1; /* { correct maxexp }*/
	do
	{
		maxexp = maxexp +1;
	     _maxreal = z;
		z = z * radix;
		y = z * radix;
	}
     while (!(y == z)); 
	maxexp = maxexp -1; /* { correct maxexp }*/
	infty= y;

	/* { fill in  mantissa }*/
	y= ONE;
	minexp =precision;
	do
	{
		minexp =  (minexp-1);
	     _minreal = y;
		y = y / radix ;
	}
     while (!(y == 0)); 
	printf("  %d(%x),"         ,minexp,abs(minexp));
	printf("  %d(%x))"         ,maxexp,maxexp);
	printf("\n  max: %19.16le(" ,_maxreal); WRITEHEX(_maxreal);
	printf(")\n  min: %19.16le(",_minreal); WRITEHEX(_minreal);
	printf(")\n  inf: "); WRITEHEX(infty);
	qnan = (2*y)/y;

	printf("\t\t\tqnan: "); WRITEHEX(qnan);
#if USE_NAN
	printf("\n\n  The system library provides :");
	x=signaling_nan(n);
	printf("\n  snan: "); WRITEHEX(x);
	x=quiet_nan(n);
	printf("\t\tqnan: "); WRITEHEX(x);
#else 
	puts("\n\n  Nan's are not supported by the system libraries");
	puts("  or USE_SYSTEM_NAN is not switched on ! ");
#endif
#else 
	puts("  Double analysis skipped ");
#endif /* use_ieee */
	puts("");
}
/*------------------------------------------------------------------*/
/* NAME       : FPUhandler                                          */
/* DESCRIPTION: Can be used for catch Fpu traps                     */
/*------------------------------------------------------------------*/
void FPUhandler (dummy)
int dummy;
{
	cnt++;
	return;
}


/*------------------------------------------------------------------*/
/* NAME       : TestShift                                           */
/* DESCRIPTION: Checks Shift right operation                        */
/*------------------------------------------------------------------*/
void TestShift()
{
#define B_ASHR1(x,y)             (((x)>=0) ? (x)>>(y) : (~(~(x)>>(y))))
#define B_ASHR2(x,y)             ((x)>>(y))
#define Checkshift(x,sz) if ( B_ASHR1(x,sz) != B_ASHR2(x,sz)) \
				  { printf("Define B_ASHR because %08lx <> %08lx\n",\
	               (long) B_ASHR1(x,sz), (long) B_ASHR2(x,sz)); return;};
  int i,i0,im,k;
  long l,lm; 
  for (i0=1 ; i0<=3 ; i0 += 2)
  {
		/*---------- checking (un)signed int ------------- */
		i=i0; im=-i0;
		for (k=0;k< 8*sizeof(int);k++)
		{
		   Checkshift(i,1);
		   Checkshift(im,1);
	   /*	printf("%08.8x\n1. %08.8x\n2. %08.8x\n",i,B_ASHR1(i,1) ,
				   B_ASHR2(i,1));*/
		   i <<=1; im *= 2;
		}
		/*---------- checking signed long ------------- */
		l= i0; lm=-i0;
		for (k=0;k< 8*sizeof(long);k++)
		{
		   Checkshift(l,1);
		   Checkshift(lm,1);
		   /* printf("%08.8x\n1. %08.8x\n2. %08.8x\n",l,B_ASHR1(l,1) ,
				   B_ASHR2(l,1));*/
		   l <<=1; lm *= 2;
		}
  }
  printf ("MSB is not inserted (like SUN).\n");
  return;
	
}

/*------------------------------------------------------------------*/
/* NAME       : PRINTF/D32                                          */
/* DESCRIPTION: Reports ieee number in hex an it's components       */
/*------------------------------------------------------------------*/
#define PRINTF32(lf) {int lb=2*(4+sizeof(lf.f)-sizeof(long));\
		printf("  F32:");WRITEHEX(lf.f);\
		printf("%*.*s: %d %08x %x\n",lb,lb," ",\
		lf.ieee.sign,lf.ieee.mant0, lf.ieee.expo);}

#define PRINTD32(lf) {printf("  D32:");WRITEHEX(lf.f);\
		printf(": %d %08x %08x %x\n", \
		lf.ieee.sign, lf.ieee.mant0, lf.ieee.mant1,lf.ieee.expo);}

#define PRINTD64(lf) {printf("  D64:");WRITEHEX(lf.f);\
		printf(": %d %08x %08x %x(%lx)\n", \
		lf.ieee.sign, lf.ieee.mant0, lf.ieee.mant1,lf.ieee.expo,lf.l);}
/*------------------------------------------------------------------*/
/* NAME       : Testunion                                           */
/* DESCRIPTION: Compose/Decompose ieee values with union contruct   */
/*------------------------------------------------------------------*/
void Testunion()
{
typedef union 
	{
		float f;
		struct 
		{
			unsigned int mant0 :23;
			unsigned int expo  : 8;
			unsigned int sign  : 1;
		} ieee;
	} float_l32_endian;

typedef union 
	{
		float f;
		struct 
		{
			unsigned int sign  : 1;
			unsigned int expo  : 8;
			unsigned int mant0 :23;
		} ieee;
	} float_b32_endian;

typedef union 
	{
		double f;
		struct 
		{
			unsigned int mant1 :32;
			unsigned int mant0 :20;
			unsigned int expo  :11;
			unsigned int sign  : 1;
		} ieee;
	} double_l32_endian;
typedef union 
	{
		double f;
		struct 
		{
			unsigned int sign  : 1;
			unsigned int expo  :11;
			unsigned int mant0 :20;
			unsigned int mant1 :32;
		}  ieee;
	} double_b32_endian;
typedef union 
	{
		double f;
		struct 
		{
			unsigned int mant1 :32;
			unsigned int mant0 :20;
			unsigned int expo  :11;
			unsigned int sign  : 1;
		} ieee;
		long l;
	} double_l64_endian;
typedef union 
	{
		double f;
		struct 
		{
			unsigned int sign  : 1;
			unsigned int expo  :11;
			unsigned int mant0 :20;
			unsigned int mant1 :32;
		}  ieee;
		long l;
	} double_b64_endian;

	double dx = 3.14159;
	float  x = (float)3.14159;

	if (intel) { float_l32_endian f32; f32.f = x; PRINTF32(f32); }
	else { float_b32_endian f32; f32.f = x; PRINTF32(f32); }
	if (intel) { double_l32_endian d32; d32.f = dx; PRINTD32(d32); }
	else { double_b32_endian d32; d32.f = dx; PRINTD32(d32); }
	if (sizeof(long)==sizeof(double)) 
	{
	 if (intel) { double_l64_endian d64; d64.f = dx; PRINTD64(d64); }
	 else { double_b64_endian d64; d64.f = dx; PRINTD64(d64); }
	}
	return;
}

#if USE_TEST_RUSAGE
#include <sys/time.h>
#include <sys/resource.h>
#define TIMETEST(a){ struct rusage buf; int ret=getrusage(0,&buf); }
#endif

int main(argc, argv)
int argc;
char **argv;
{
	printf("-- %s $Date: 1995/05/02 10:52:12 $--\n",argv[0]);
	printf("o __STDC__ %s defined in cc headers !\n",NOSTDC);
	printf("o Preprocessor resolves args from define %s.\n",CHECKPPSTR);
     printf("o Check endianness.");
	TestIntel;
     printf("o Checking signed arithmetic shift right. ");
	TestShift();

	printf("o Check for getrusage entry ");
#if USE_TEST_RUSAGE
	TIMETEST("dummy")
	printf("succeeded.\n");
#else
	printf("failed or skipped.\n");
#endif

#if USE_SIGNAL
     signal(SIGILL, SIG_IGN);
     signal(SIGTERM, SIG_IGN);
	signal(SIGFPE, SIG_IGN);
	if (errno== EINVAL) 
		printf("Warning : signal fallback for SIGFPE failed");
#endif

	printf("o (De)compose float/double with union.\n");
	Testunion();

	printf("o Compute size of data types.\n");
	printf("  data type      |sizeof|comp. bits|max                 |max hex\n");
	printf("  ---------------+------+----------+--------------------+---------\n");
	Arithm(char);
	Arithm(short);
	Arithm(int);
	Arithm(long);
	Arithm(unsigned char);
	Arithm(unsigned short);
	Arithm(unsigned int);
	Arithm(unsigned long);
	puts("");
	Arithmetic();
	DoubleArithmetic();

 	if (cnt>0) printf("Received SIGFPE at * %d times.",cnt);
	return 0;
}
