/**************************************************************************
 * 
 * Filename:
 * 
 *   whetstone.c
 * 
 * Description:
 * 
 *   Performs one million Whetstone instructions, a number of times, then
 *   prints the execution speed in K Whetstone Instructions per Second
 *   (KWIPS). For example, if ntimes = 1, and the execution time is 1
 *   second, then the result is 1000 KWIPS.
 * 
 * Credits:
 * 
 *   This source text was translated from the Ada PIWG test A000093 by
 *   Chris Nettleton, November 1996.
 *
 * Revision:
 * 
 *   $Id: whetstone.c,v 1.1.1.1 2004/05/10 20:33:29 cvs Exp $
 *
 **************************************************************************/

#include <report.h>
#include <time.h>

/* The KWIPS rating is the number of Kilo Whetstone Instructions Per
 * Second. The following code is worth 1 million whetstones for each
 * iteration, so to compute the rating, run the code ntimes, then: rating
 * = 1000 / time taken in secs / ntimes ntimes should be set so the
 * benchmark takes about ten seconds to run so you can get an approximate
 * verification of the printed rating.
 *
 * A 25MHz Motorola 68020 with 68881 scores about 2000 KWIPS, so by setting
 * ntimes to 20, the execution time is 10 secs, which is a good time from
 * the point of view of timing the clock function, and from checking using
 * a stop watch. ntimes = 100 is good on a Pentium.  */

static const int ntimes = 1;

/* Benchmark timing stuff */

static long rating;
static clock_t start_time;
static clock_t stop_time;


/*
 * My floating abs (we don't have a math library)
 */
double fabs (double x);


/* These routines are provided for use by ACM SIGAda PIWG for making
 * measurements. These routines are copyrighted and may only be used for
 * performance measurements. These math routines must not be distributed
 * without this notice. No permission is granted to any party to modify,
 * to redistribute, to sell, to give away, or to otherwise use or transmit
 * these math routines without express written permission from Westinghous
 * Electric Corporation, c/o Jon Squire, P.O. Box 746 MS1615, Baltimore,
 * MD 21203.  */

const float pi_2 = 1.57079632679489661;

static float
sin (float x)
{
  const float c1 = 1.57079631847;
  const float c3 = -0.64596371106;
  const float c5 = 0.07968967928;
  const float c7 = -0.00467376557;
  const float c9 = 0.00015148419;

  float x_norm;
  float x_int;
  float x_2;
  float y;

  x_norm = x / pi_2;
  if (fabs (x_norm) > 4.0)
    {
      x_int = (float) ((int) (x_norm / 4.0));
      x_norm = x_norm - 4.0 * x_int;
    }

  if (x_norm > 2.0)
    x_norm = 2.0 - x_norm;
  else if (x_norm < -2.0)
    x_norm = -2.0 - x_norm;

  if (x_norm > 1.0)
    x_norm = 2.0 - x_norm;
  else if (x_norm < -1.0)
    x_norm = -2.0 - x_norm;

  x_2 = x_norm * x_norm;
  y = (c1 + (c3 + (c5 + (c7 + c9 * x_2) * x_2) * x_2) * x_2) * x_norm;

  return y;
}

static float
cos (float x)
{
  return sin (x + pi_2);
}

static float
atan (float x)
{
  const float c1 = 0.9999993329;
  const float c3 = -0.3332985605;
  const float c5 = 0.1994653599;
  const float c7 = -0.1390853351;
  const float c9 = 0.0964200441;
  const float c11 = -0.0559098861;
  const float c13 = 0.0218612288;
  const float c15 = -0.0040540580;

  float a_2;
  float y;
  float a;

  a = x;
  if (fabs (a) > 1.0)
    a = 1.0 / a;

  a_2 = a * a;
  y =
    (c1 +
     (c3 +
      (c5 +
       (c7 +
	(c9 +
	 (c11 +
	  (c13 + c15 * a_2) * a_2) * a_2) * a_2) * a_2) * a_2) * a_2) * a;

  if (fabs (x) >= 1.0)
    {
      if (x < 0.0)
	y = -(pi_2 + y);
      else
	y = pi_2 - y;
    }

  return y;
}

static float
sqrt (float x)
{
  float y, root_pwr, x_norm;
  const float a = 2.1902;
  const float b = -3.0339;
  const float c = 1.5451;

  x_norm = x;
  root_pwr = 1.0;

  if (x <= 0.0)
    return 0.0;

  if (x > 1.0)
    {
      while (x_norm > 1.0)
	{
	  root_pwr = root_pwr * 2.0;
	  x_norm = x_norm * 0.25;
	}
    }
  else
    {
      while (x_norm < 0.25)
	{
	  root_pwr = root_pwr * 0.5;
	  x_norm = x_norm * 4.0;
	}
    }

  y = a + b / (c + x_norm);
  y = 0.5 * (y + x_norm / y);
  y = 0.5 * (y + x_norm / y);
  y = y * root_pwr;

  return y;
}

static float
exp (float x)
{
  const float c1 = 9.99999900943303e-01;
  const float c2 = 5.00006347344554e-01;
  const float c3 = 1.66667985598315e-01;
  const float c4 = 4.16350120350139e-02;
  const float c5 = 8.32859610677671e-03;
  const float c6 = 1.43927433449119e-03;
  const float c7 = 2.04699933614437e-04;

  float x1;
  float y;
  float e_pwr = 1.0;
  float e = 2.71828182845905;

  x1 = fabs (x);
  if (x1 > 88.0)
    return 0.0;

  while (x1 >= 1.0)
    {
      e_pwr = e_pwr * e * e;
      x1 = x1 - 2.0;
    }

  y =
    1.0 + (c1 +
	   (c2 +
	    (c3 +
	     (c4 + (c5 + (c6 + c7 * x1) * x1) * x1) * x1) * x1) * x1) * x1;
  y = y * e_pwr;
  if (x < 0.0)
    y = 1.0 / y;

  return y;
}

static float
log10 (float x)
{
  const float c1 = 0.868591718;
  const float c3 = 0.289335524;
  const float c5 = 0.177522071;
  const float c7 = 0.094376476;
  const float c9 = 0.191337714;
  const float c_r10 = 3.1622777;

  float y;
  float x_norm;
  float x_log;
  float frac;
  float frac_2;

  x_log = 0.5;
  x_norm = x;
  if (x <= 0.0)
    return 0.0;

  if (x >= 10.0)
    {
      while (x_norm >= 10.0)	/* reduce to 1.0 .. 10.0 */
	{
	  x_log = x_log + 1.0;
	  x_norm = x_norm * 0.1;
	}
    }
  else
    {
      while (x_norm < 1.0)	/* reduce to 1.0 .. 10.0 */
	{
	  x_log = x_log - 1.0;
	  x_norm = x_norm * 10.0;
	}
    }

  frac = (x_norm - c_r10) / (x_norm + c_r10);
  frac_2 = frac * frac;
  y = (c1 + (c3 + (c5 + (c7 + c9 * frac_2) * frac_2) * frac_2) * frac_2)
    * frac;

  return y + x_log;
}


static float
log (float x)
{
  return 2.302585093 * log10 (x);
}

/*
 * End of copyrighted section
 */


float x1, x2, x3, x4, x, y, z, t, t1, t2;
float e1[5];
int j, k, l, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11;


static void
pa (float *e)
{
  /* tests computations with an array as a parameter */
  int j;
  /* T,T2 : FLOAT are global variables */

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

  j = j + 1;
  if (j < 6)
    goto LAB;
}

static void
p0 ()
{
  /*
   * tests computations with no parameters
   * T1,T2 : FLOAT are global
   * E1 : VECTOR (1..4) is global
   * J,K,L : INTEGER are global
   */

  e1[j] = e1[k];
  e1[k] = e1[l];
  e1[l] = e1[j];
}

static void
p3 (float *x, float *y, float *z)
{
  /*
   * tests computations with simple identifiers as parameters
   * T,T2 : FLOAT are global
   */

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

/*
 * Whetstone proper starts here
 */
int
main ()
{
  int I = 10;			/* loop count weighting factor */
  int cycle_no;			/* major loop counter */
  int i;			/* loop counter */

  test ("whetstone", "Whetstone scientific benchmark");

  /* Set constants */
  t = 0.499975;
  t1 = 0.50025;
  t2 = 2.0;

  /* Compute the execution frequency for the benchmark modules */
  n1 = 0;			/* Module 1 not executed */
  n2 = 12 * I;
  n3 = 14 * I;
  n4 = 345 * I;
  n5 = 0;			/* Module 5 not executed */
  n6 = 210 * I;
  n7 = 32 * I;
  n8 = 899 * I;
  n9 = 616 * I;
  n10 = 0;			/* Module 10 not executed */
  n11 = 93 * I;

  start_time = clock ();	/* Get Whetstone start time */

cycle_loop:
  for (cycle_no = 1; cycle_no <= ntimes; cycle_no++)
    {
      /* Module 1 : computations with simple identifiers */
      x1 = 1.0;
      x2 = -1.0;
      x3 = -1.0;
      x4 = -1.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;
	}
      /* end Module 1 */

      /* Module 2: computations with array elements */
      e1[1] = 1.0;
      e1[2] = -1.0;
      e1[3] = -1.0;
      e1[4] = -1.0;
      for (i = 1; i <= n2; i++)
	{
	  e1[1] = (e1[1] + e1[2] + e1[3] - e1[4]) * t;
	  e1[2] = (e1[1] + e1[2] - e1[3] + e1[4]) * t;
	  e1[3] = (e1[1] - e1[2] + e1[3] + e1[4]) * t;
	  e1[4] = (-e1[1] + e1[2] + e1[3] + e1[4]) * t;
	}
      /* end Module 2 */

      /* Module 3 : passing an array as a parmeter */
      for (i = 1; i <= n3; i++)
	pa (e1);
      /* end Module 3 */

      /* Module 4 : performing conditional jumps */
      j = 1;
      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;
	}
      /* end Module 4 */

      /* Module 5 : omitted */

      /* Module 6 : performing integer arithmetic */
      j = 1;
      k = 2;
      l = 3;
      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 - 1] = (float) (j + k + l);
	  e1[k - 1] = (float) (j * k * l);
	}
      /* end Module 6 */

      /* Module 7 : performing computations using trigonometric
         functions */
      x = 0.5;
      y = 0.5;
      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));
	}
      /* end Module 7 */

      /* Module 8 : procedure calls with simple identifiers as
         parameters */
      x = 1.0;
      y = 1.0;
      z = 1.0;
      for (i = 1; i <= n8; i++)
	p3 (&x, &y, &z);
      /* end Module 8 */

      /* Module 9 : array reference and procedure calls with no
         parameters */
      j = 1;
      k = 2;
      l = 3;
      e1[1] = 1.0;
      e1[2] = 2.0;
      e1[3] = 3.0;
      for (i = 1; i <= n9; i++)
	p0 ();
      /* end Module 9 */

      /* Module 10 : integer arithmetic */
      j = 2;
      k = 3;
      for (i = 1; i <= n10; i++)
	{
	  j = j + k;
	  k = k + j;
	  j = k - j;
	  k = k - j - j;
	}
      /* end Module 10 */

      /* Module 11 : performing computations using standard
         mathematical functions */
      x = 0.75;
      for (i = 1; i <= n11; i++)
	x = sqrt (exp (log (x) / t1));
      /* end Moudle 11 */

    }

  /* Get stop time after ntimes */
  stop_time = clock ();

  /* Now compute number of K Whetstones per sec */
  rating = (long) (1000.0 /
		   (((double) stop_time - (double) start_time) /
		    (double) CLOCKS_PER_SEC / (double) (ntimes)));

  comment ("Rating = %ld KWIPS", rating);

  if (rating < 100.0 || rating > 100000.0)
    failed ("measured rating out of limits");

  result ();
}