/**************************************************************************
 *                                                                        *
 *      Whetstone benchmark in C.  This program is a translation of the   *
 *      original Algol version in "A Synthetic Benchmark" by H.J. Curnow  *
 *      and B.A. Wichman in Computer Journal, Vol  19 #1, February 1976.  *
 *                                                                        *
 *      Used to test compiler efficiency, optimization, and double        *
 *      precision floating-point performance.  This version is specific   *
 *      to the Turbo-Amiga and Amiga but it can be easily adapted to      *
 *      other systems by replacing the clock() routine with your own. *
 *                                                                        *
 **************************************************************************/

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>

/* Specify pragma on(387); for the best High C timings with a 387. */

#define ITERATIONS   10       /* 1 Million Whetstone instructions    */

/* #define POUT  */           /* Leave as is for 'Official' result.  */

/* #define MTEST */           /* define for Module timing results    */
                              /* only.  Leave as is for 'Official'   */
                              /* result.                             */

double   e1[4],e2[4];
double   t, t1, t2, x, y, z, x1, x2, x3, x4;
long     i, j, k, l;
long     n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11;
long     m, m1, m2, loops, KWhets;

clock_t  starttime, stoptime, nulltime;
clock_t  sumtime, benchtime;

void main(void)
{
   printf("\n   Whetstone C Benchmark V1.0\n");

   starttime = clock();               /* Calculate timer call delay */
   stoptime  = clock();
   nulltime  = stoptime - starttime;

   /************************/
   /* initialize constants */
   /************************/

   t   =   0.499975;
   t1  =   0.500250;
   t2  =   2.0;

   /***********************/
   /* Set Module Weights. */
   /***********************/

   m = 10;                    /* m = 10 is used to obtain better timing  */
   loops = m * ITERATIONS;    /* accuracy only.  Slow systems should use */
   n1  =   0 * loops;         /* m = 1.                                  */
   n2  =  12 * loops;
   n3  =  14 * loops;
   n4  = 345 * loops;
   n5  =   0 * loops;
   n6  = 210 * loops;
   n7  =  32 * loops;
   n8  = 899 * loops;
   n9  = 616 * loops;
   n10 =   0 * loops;
   n11 =  93 * loops;


                              /* Start Timing the Whetstone here. */
   starttime = clock();

   /*********************************/
   /* MODULE 1:  simple identifiers */
   /*********************************/

   x1 =  1.0;
   x2 = -1.0;
   x3 = -1.0;
   x4 = -1.0;

   if( n1 > 0 )
   {
     for(i = 1; i <= n1; i++)
     {
      x1 = ( x1 + x2 + x3 - x4 ) * t;
      x2 = ( x1 + x2 - x3 - x4 ) * t;
      x3 = ( x1 - x2 + x3 + x4 ) * t;
      x4 = (-x1 + x2 + x3 + x4 ) * t;
     }
   }

#ifdef MTEST                                    /* Module 1 Run Time */
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m = 1;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n1, n1, n1, x1, x2, x3, x4);
#endif

   /*****************************/
   /* MODULE 2:  Array Elements */
   /*****************************/
#ifdef MTEST
   starttime = clock();
#endif

   e1[0] =  1.0;        /* Start at element 0 in C, vice 1 in Fortran */
   e1[1] = -1.0;
   e1[2] = -1.0;
   e1[3] = -1.0;

   if( n2 > 0 )
   {
     for (i = 1; i <= n2; i++)
     {
      e1[0] = ( e1[0] + e1[1] + e1[2] - e1[3] ) * t;
      e1[1] = ( e1[0] + e1[1] - e1[2] + e1[3] ) * t;
      e1[2] = ( e1[0] - e1[1] + e1[2] + e1[3] ) * t;
      e1[3] = (-e1[0] + e1[1] + e1[2] + e1[3] ) * t;
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   sumtime   = benchtime;
   m = 2;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n2, n3, n2, e1[0], e1[1], e1[2], e1[3]);
#endif

   /*********************************/
   /* MODULE 3:  Array as Parameter */
   /*********************************/
#ifdef MTEST
   starttime = clock();
#endif

   if( n3 > 0 )
   {
     for (i = 1; i <= n3; i++)
     {
      pa(e1);
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m1 = benchtime;
   m = 3;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n3, n2, n2, e1[0], e1[1], e1[2], e1[3]);
#endif

   /********************************/
   /* MODULE 4:  Conditional Jumps */
   /********************************/
#ifdef MTEST
   starttime = clock();
#endif

   j = 1;

   if( n4 > 0 )
   {
     for (i = 1; i <= n4; i++)
     {
      if (j == 1)
         j = 2;
      else
         j = 3;

      if (j > 2)
         j = 0;
      else
         j = 1;

      if (j < 1 )
         j = 1;
      else
         j = 0;
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   sumtime   = sumtime + benchtime;
   m = 4;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
   starttime = clock();
#endif

#ifdef POUT
   Pout(n4, j, j, x1, x2, x3, x4);
#endif

   /**********************/
   /* MODULE 5:  Omitted */
   /**********************/


#ifdef MTEST
   benchtime = 0;
   m = 5;
   x = 0.0;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n4, j, j, x1, x2, x3, x4);
#endif

   /*********************************/
   /* MODULE 6:  Integer Arithmetic */
   /*********************************/
#ifdef MTEST
   starttime = clock();
#endif

   j = 1;
   k = 2;
   l = 3;

   if( n6 > 0 )
   {
     for (i = 1; i <= n6; i++)
     {
      j = j * (k - j) * (l -k);
      k = l * k - (l - j) * k;
      l = (l - k) * (k + j);

      e1[l - 2] = j + k + l;          /* Remember we started at e1[0]. */
      e1[k - 2] = j * k * l;          /* l-2 in C, vice l-1 in Fortran */
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   sumtime   = sumtime + benchtime;
   m = 6;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n6, j, k, e1[0], e1[1], e1[2], e1[3]);
#endif

   /**************************************/
   /* MODULE 7:  Trigonometric Functions */
   /**************************************/
#ifdef MTEST
   starttime = clock();
#endif

   x = 0.5;
   y = 0.5;

   if( n7 > 0 )
   {
     for(i = 1; i <= n7; i++)
     {
      x = t * atan(t2*sin(x)*cos(x)/(cos(x+y)+cos(x-y)-1.0));
      y = t * atan(t2*sin(y)*cos(y)/(cos(x+y)+cos(x-y)-1.0));
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   sumtime   = sumtime + benchtime;
   m = 7;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x1);
#endif

#ifdef POUT
   Pout(n7, j, k, x, x, y, y);
#endif

   /******************************/
   /* MODULE 8:  Procedure Calls */
   /******************************/
#ifdef MTEST
   starttime = clock();
#endif

   x = 1.0;
   y = 1.0;
   z = 1.0;

   if( n8 > 0 )
   {
     for (i = 1; i <= n8; i++)
     {
      p3(x, y, &z);
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m1 = m1 + benchtime;
   m = 8;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x1);
#endif

#ifdef POUT
   Pout(n8, j, k, x, y, z, z);
#endif

   /*******************************/
   /* MODULE 9:  Array References */
   /*******************************/
#ifdef MTEST
   starttime = clock();
#endif

   j = 1;
   k = 2;
   l = 3;

   e1[0] = 1.0;
   e1[1] = 2.0;
   e1[2] = 3.0;

   if( n9 > 0 )
   {
     for(i = 1; i <= n9; i++)
     {
      p0();
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m1 = m1 + benchtime;
   m = 9;
   x = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n9, j, k, e1[0], e1[1], e1[2], e1[3]);
#endif

   /**********************************/
   /* MODULE 10:  Integer Arithmetic */
   /**********************************/
#ifdef MTEST
   starttime = clock();
#endif

   j = 2;
   k = 3;

   if( n10 > 0 )
   {
     for(i = 1; i <= n10; i++)
     {
      j = j + k;
      k = j + k;
      j = k - j;
      k = k - j - j;
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = 0;
   m = 10;
   x = 0.0;
   printf("   End Module %ld.  Benchtime(sec) = %lf\n",m,x);
#endif

#ifdef POUT
   Pout(n10, j, k, x1, x2, x3, x4);
#endif

   /**********************************/
   /* MODULE 11:  Standard Functions */
   /**********************************/
#ifdef MTEST
   starttime = clock();
#endif

   x = 0.75;

   if( n11 > 0 )
   {
     for(i = 1; i <= n11; i++)
     {
      x = sqrt( exp( log(x) / t1) );
     }
   }

#ifdef MTEST
   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   sumtime   = sumtime + benchtime;
   m = 11;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.  Benchtime(sec) = %lf\n\n",m,x1);
#endif

#ifdef POUT
   Pout(n11, j, k, x, x, x, x);
#endif

   /********************************************************/
   /* MODULE 12:  Array as a Parameter.                    */
   /* Same as Module 3 except Subroutine overhead removed. */
   /********************************************************/

#ifdef MTEST

   starttime = clock();
   if( n3 > 0 )
   {
     for (i = 1; i <= n3; i++)
     {
      j = 0;
        lab:
      e2[0] = (  e2[0] + e2[1] + e2[2] - e2[3] ) * t;
      e2[1] = (  e2[0] + e2[1] - e2[2] + e2[3] ) * t;
      e2[2] = (  e2[0] - e2[1] + e2[2] + e2[3] ) * t;
      e2[3] = ( -e2[0] + e2[1] + e2[2] + e2[3] ) /t2;
      j++;

      if (j < 6)
         goto lab;
     }
   }

   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m2   = benchtime;
   m = 3;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x1);

#endif

#ifdef POUT
   Pout(n11, j, k, x, x, x, x);
#endif

   /********************************************************/
   /* MODULE 13:  Array as a Parameter.                    */
   /* Same as Module 8 except Subroutine overhead removed. */
   /********************************************************/

#ifdef MTEST

   starttime = clock();
   x = 1.0;
   y = 1.0;
   z = 1.0;

   if( n8 > 0 )
   {
     for (i = 1; i <= n8; i++)
     {
      x = t * (x + y);
      y = t * (x + y);
      z = (x + y) /t2;
     }
   }

   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m2   = m2 + benchtime;
   m = 8;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x1);

#endif

#ifdef POUT
   Pout(n11, j, k, x, x, x, x);
#endif

   /********************************************************/
   /* MODULE 14:  Array as a Parameter.                    */
   /* Same as Module 9 except Subroutine overhead removed. */
   /********************************************************/

#ifdef MTEST

   starttime = clock();
   j = 1;
   k = 2;
   l = 3;

   e1[0] = 1.0;
   e1[1] = 2.0;
   e1[2] = 3.0;

   if( n9 > 0 )
   {
     for(i = 1; i <= n9; i++)
     {
      e1[j] = e1[k];
      e1[k] = e1[l];
      e1[l] = e1[j];
     }
   }

   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   m2 = m2 + benchtime;
   m = 9;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   End Module %ld.   Benchtime(sec) = %lf\n",m,x1);

#endif

#ifdef POUT
   Pout(n11, j, k, x, x, x, x);
#endif

   /**************************/
   /* End of Whetstone Tests */
   /**************************/

#ifdef MTEST
   m1 = m1 + sumtime;
   m2 = m2 + sumtime;
   x1 = (double)m1/(double)CLOCKS_PER_SEC;
   x2 = (double)m2/(double)CLOCKS_PER_SEC;

   printf("Standard Whetstone Result\n");
   printf("   Benchtime(sec) = %lf\n",x1);
   x  = 100.0 * (double)loops / x1;
   KWhets = (long)x;
   printf("   KWhets/sec     = %ld\n",KWhets);

   printf("Result with subroutine call replaced with subroutine code\n");
   printf("   Benchtime(sec) = %lf\n",x2);
   x  = 100.0 * (double)loops / x2;
   KWhets = (long)x;
   printf("   KWhets/sec     = %ld\n",KWhets);

#else

   stoptime  = clock();
   benchtime = stoptime - starttime - nulltime;
   x1 = (double)benchtime/(double)CLOCKS_PER_SEC;
   printf("   Benchtime(sec) = %lf\n",x1);
   x2 = 100.0 * (double)loops / x1;
   KWhets = (long)x2;
   printf("   KWhets/sec     = %ld\n\n",KWhets);

#endif

   exit(0);

}

/*******************/
/* Subroutine pa() */
/*******************/

pa(e)                /* Exactly as in the Algol 60 version, but we */
                     /* could do away with that 'goto'.            */
double e[4];

{
   int j;

   j = 0;
     lab:
   e[0] = (  e[0] + e[1] + e[2] - e[3] ) * t;
   e[1] = (  e[0] + e[1] - e[2] + e[3] ) * t;
   e[2] = (  e[0] - e[1] + e[2] + e[3] ) * t;
   e[3] = ( -e[0] + e[1] + e[2] + e[3] ) / t2;
   j ++;

   if (j < 6)
      goto lab;
}

/************************/
/* Subroutine p3(x,y,z) */
/************************/

p3(x, y, z)

double x, y, *z;

{
   x  = t * (x + y);
   y  = t * (x + y);
   *z = (x + y) /t2;
}

/*******************/
/* Subroutine p0() */
/*******************/

p0()
{
   e1[j] = e1[k];
   e1[k] = e1[l];
   e1[l] = e1[j];
}

/*********************/
/* Subroutine Pout() */
/*********************/

#ifdef POUT
Pout(n, j, k, x1, x2, x3, x4)

long  n, j, k;
double x1, x2, x3, x4;

{
   printf("%5ld %5ld %5ld   %11.3le %11.3le %11.3le %11.3le\n",
      n, j, k, x1, x2, x3, x4);
}
#endif

/*-- End Of Whetstone C Source Code -----------------*/
