#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define HOST_WIDE_INT long long
typedef HOST_WIDE_INT gpi_int;

static gpi_int
compute_checksum_original (unsigned char *buf, gpi_int size)
{
  gpi_int sum = 0, n = 0;
  
  for (n = 0; n < size; n++)
    sum += n * buf[n];
  return sum;
}

static gpi_int
compute_checksum_unrolled (unsigned char *buf, gpi_int size)
{
  gpi_int sum = 0, n = 0;
  
	while ( size > 4 ) {
		sum += n * buf[n];
		n++;
		sum += n * buf[n];
		n++;
		sum += n * buf[n];
		n++;
		sum += n * buf[n];
		n++;
		size -= 4;
	}
	while ( size > 0 ) {
		sum += n * buf[n];
		n++;
		size--;
	}
  return sum;
}

static gpi_int
compute_checksum_native (unsigned char *buf, gpi_int size)
{
  gpi_int sum = 0, n = 0;
  
  if ( size > 0x7fffffff || sizeof(int) < 4) {
	  for (n = 0; n < size; n++)
  	  sum += n * buf[n];
  } else {
	  int ni = 0;
	  int sizei = size;
		while ( sizei > 4 ) {
			sum += ni * buf[ni];
			ni++;
			sum += ni * buf[ni];
			ni++;
			sum += ni * buf[ni];
			ni++;
			sum += ni * buf[ni];
			ni++;
			sizei -= 4;
		}
		while ( sizei > 0 ) {
			sum += ni * buf[ni];
			ni++;
			sizei--;
		}
  }
  return sum;
}

static gpi_int
compute_checksum_shift (unsigned char *buf, gpi_int size)
{
  gpi_int sum = 0, n = 0;
  
  if ( size > 0x7fffffff || sizeof(int) < 4) {
	  for (n = 0; n < size; n++)
  	  sum += n * buf[n];
  } else {
	  int ni = 0;
	  int sizei = size;
		while ( sizei > 8 ) {
			sum += (int)buf[ni] << 0;
			ni++;
			sum += (int)buf[ni] << 4;
			ni++;
			sum += (int)buf[ni] << 8;
			ni++;
			sum += (int)buf[ni] << 12;
			ni++;
			sum += (int)buf[ni] << 16;
			ni++;
			sum += (int)buf[ni] << 20;
			ni++;
			sum += (int)buf[ni] << 24;
			ni++;
			sum += (int)buf[ni] << 28;
			ni++;
			sizei -= 8;
		}
		while ( sizei > 0 ) {
			sum += (int)buf[ni] << 0;
			ni++;
			sizei--;
		}
  }
  return sum;
}

static gpi_int
compute_checksum_add (unsigned char *buf, gpi_int size)
{
  gpi_int sum = 0, n = 0;
  
  if ( size > 0x7fffffff || sizeof(int) < 4) {
	  for (n = 0; n < size; n++)
  	  sum += n * buf[n];
  } else {
	  int ni = 0;
	  int sizei = size;
	  int sumi = 0;
		while ( sizei > 8 ) {
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sumi += (int)buf[ni];
			ni++;
			sizei -= 8;
		}
		while ( sizei > 0 ) {
			sumi += (int)buf[ni];
			ni++;
			sizei--;
		}
		sum = sumi;
  }
  return sum;
}

#define SIZE 27000000L
// 10820508
unsigned char *buf;

typedef gpi_int (*checksum_t)(unsigned char *buf, gpi_int size);

void timeit( const char *what, checksum_t checksum )
{
	gpi_int sum;
	struct timeval start, finish;
	long long used;

	gettimeofday( &start, NULL );
	sum = checksum( buf, SIZE );
	gettimeofday( &finish, NULL );
	used = (finish.tv_sec - start.tv_sec) * 1000000 +  finish.tv_usec - start.tv_usec;
	printf( "time=%8lld   sum=%20lld   %s\n", used, sum, what );
}

int main()
{
	gpi_int checksum;
	unsigned char *p;
	gpi_int i;
	
	buf = malloc( SIZE );
	p = buf;
	for ( i = 1; i <= SIZE; i++ ) {
		*p++ = i;
	}
	printf( "sizeof: %d\n", sizeof(checksum) );
	timeit( "compute_checksum_original", &compute_checksum_original );
	timeit( "compute_checksum_unrolled", &compute_checksum_unrolled );
	timeit( "compute_checksum_native", &compute_checksum_native );
	timeit( "compute_checksum_shift", &compute_checksum_shift );
	timeit( "compute_checksum_add", &compute_checksum_add );

	return 0;
}

