/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
 * See COPYING in top-level directory.
 */

/* simple program that calls the libexample library */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include "example.h"

#define NITER 100000
#define ARRAY_SIZE  (1024*1024)

#define TIME_DIFF(t1, t2)					\
  ((t2.tv_sec - t1.tv_sec) * 1e9 + (t2.tv_nsec - t1.tv_nsec))


int bin_function(double *t, int size, int n) {
  int res = n;
  int i;
  for(i=0; i<NITER_COMPUTE; i++) {
    res +=(int) t[(i*n)%size];
  }
  return res;
}

int bin_instrumented_function(double *t, int size, int n) {
  int res = n;
  int i;
  for(i=0; i<NITER_COMPUTE; i++) {
    res +=(int) t[(i*n)%size];
  }
  return res;
}

double *t = NULL;
int size;
double benchmark_function(int (*function)(double*, int, int),
			int (*instrumented_function)(double*, int, int),
			const char* fname) {
  struct timespec t1, t2;
  struct timespec ti1, ti2;
  int i;
  int res = 0;
  printf("Benchmarking %s\n", fname);
  printf("---------------\n");

  /* Run the instrumented function */
  printf("Running the instrumented function...\n");
  int resi = 0;
  clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti1);
  for(i=0; i<NITER; i++) {
    resi += instrumented_function(t, size, i);
  }
  clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti2);
  printf("resi = %d\n", resi);

  double duration_i = TIME_DIFF(ti1, ti2);
  printf("%d iterations in %lf ms (%lf ns per function call)\n", NITER, duration_i/1e6, duration_i/NITER);

  /* Run the non-instrumented function */
  printf("Running the non-instrumented function...\n");
  clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t1);
  for(i=0; i<NITER; i++) {
    res += function(t, size, i);
  }
  clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
  printf("res = %d\n", res);

  double duration = TIME_DIFF(t1, t2);
  printf("%d iterations in %lf ms (%lf ns per function call)\n", NITER, duration/1e6, duration/NITER);


  /* Print the result */
  assert(res == resi);
  double duration_diff = duration_i - duration;
  printf("overhead: %lf ms (%lf ns per function call)\n", duration_diff/1e6, duration_diff/NITER);
  return duration_diff/NITER;
}

void init() {
  size = ARRAY_SIZE;
  t = malloc(sizeof(double)*size);
  int i;
  for(i=0; i<size; i++) {
    t[i] = (i*(i*17))%size;
  }
}

int main(int argc, char**argv) {
  int i;

  init();
  double diff[10][2];
  double total_diff[2] = {0, 0};
  for(i=0; i<10; i++) {
    diff[i][0] = benchmark_function(bin_function, bin_instrumented_function, "bin_function");
    total_diff[0] += diff[i][0];
    //diff[i][1] = benchmark_function(lib_function, lib_instrumented_function, "lib_function");
    //total_diff[1] += diff[i][1];
  }

  printf("\n\nSummary:\n");
  printf("Average diff for bin: %lf ns\n", total_diff[0]/10);
  printf("Average diff for lib: %lf ns\n", total_diff[1]/10);


  return 0;
}
