#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>

typedef unsigned long (*foofunc)(unsigned long, const unsigned char *,
			unsigned int len);

volatile int nodce;

#define NTIMINGS 100
#if defined(__arm__) || defined(__powerpc__)
#define NLOOPS 1000
#define MAX_LEN  16384/2
#else
#define NLOOPS 1000
#define MAX_LEN  16384
#endif

#define TIMER_SECOND_MICRO 1000000L
static struct timeval
timeval_adjust (struct timeval a)
{
  while (a.tv_usec >= TIMER_SECOND_MICRO)
    {
      a.tv_usec -= TIMER_SECOND_MICRO;
      a.tv_sec++;
    }

  while (a.tv_usec < 0)
    {
      a.tv_usec += TIMER_SECOND_MICRO;
      a.tv_sec--;
    }

  if (a.tv_sec < 0)
      /* Change negative timeouts to 0. */
      a.tv_sec = a.tv_usec = 0;

  return a;
}
static void timeit(foofunc f, const char *name, unsigned char *buffer, int cb)
{
    int64_t timings[NTIMINGS];
    int64_t sum=0, avg, stdev;
    unsigned j;

    for (j=0;j<NTIMINGS;j++) {
	struct timeval tv0, tv1, tv_sub;
	unsigned long crc0 = 0;
	int i;

	for (i=0; i<cb; ++i)
	    buffer[i] ^= rand() % 256;

	gettimeofday(&tv0, NULL);
	for (i=0;i<NLOOPS;i++)
	    crc0 = f(crc0, buffer, cb);
	gettimeofday(&tv1, NULL);

	nodce = crc0;
	tv_sub.tv_sec =tv1.tv_sec - tv0.tv_sec;
	tv_sub.tv_usec =tv1.tv_usec - tv0.tv_usec;
	tv_sub = timeval_adjust (tv_sub);
	timings[j] = tv_sub.tv_sec*1000000 + tv_sub.tv_usec;
	sum += timings[j];
    }
    avg = sum / NTIMINGS;

    stdev = 0;
    for (j=0;j<NTIMINGS;j++) {
	int64_t diff = timings[j] - avg;
	diff *= diff;
	stdev += diff;
    }
    stdev /= NTIMINGS;
    stdev = sqrt(stdev);
    if (name != 0)
	printf("%s:%7lld +/-%.1f%%\n", name, avg, stdev*100./avg);
}

unsigned long crc32_le(unsigned long crc, const unsigned char *buf,
		unsigned int len);
unsigned long crc32timings(unsigned long crc, const unsigned char *buffer,
		unsigned int len)
{
    int i = MAX_LEN/len;

    while (--i >= 0)
	    crc = ipsum_calc_block_addc(buffer, len, crc);
    return crc;
}

int main()
{
    static char unsigned buffer[MAX_LEN];
    srand(time(0));
    int run;
    /* Page in memory */
    timeit(crc32timings, 0, buffer, MAX_LEN);
    for (run=0; run<1; ++run) {
	int cb;
	for (cb=64; cb<=MAX_LEN; cb <<= 1) {
	    char n[128];
	    sprintf(n, "%5d byte buffer", cb);
	    timeit(crc32timings, n, buffer, cb);
	}
    }
    return 0;
}
