/******************************************************************
 * time_instructions2.c
 *
 * Time instruction sequences of varying lengths
 * (*without* using ICOS)
 *
 * (C) 2019 Zachary Kurmas
 ******************************************************************/

#include <stdio.h>

// It is assumed that a separate file (either C or assembly)  will
// define and populate this array.
// It represents the list of functions to be timed.
extern int num_functions;
extern unsigned long (*functions[5000])();
extern unsigned num_ops[5000];

///////////////////////////////////////////////////////////////////////
//
// Repeatedly time the code passed in function_to_time and report 
// statistics.
//
///////////////////////////////////////////////////////////////////////
static void take_measurement (unsigned long (*function_to_time)(),
				              unsigned num_ops) {

  int iterations = 2500;
  int warm_up =500;

  // Warm up the processor with "untimed" runs
  // However, we keep track of the minimum so that the 
  // compiler doesn't optimize the loop away.
  unsigned long min = function_to_time();
  for (int i = 0; i < warm_up; i++) {
    unsigned long value = function_to_time();
    min = (value < min) ? value : min;
  }

  // Don't take a minimum from the "warm up"
  
  min *= 100;
  // (sum and sum_sq need to be doubles, otherwise they might overflow)
  double sum = 0;
  double sum_sq = 0;
  unsigned long max = 0;
  double variance = 0;
  for (int i = 1; i <= iterations; i++) {

    // Generate a data piont
    unsigned long value = function_to_time();

    // Do the "recordkeeping" (update sum, min, max, etc.)
    double d_value = (double)value;
    sum += d_value;
    sum_sq += d_value*d_value; 
    if (value > max) {
      max = value;
    }
    if (value < min) {
      min = value;
    }
    // Take care not to divide by 0 if i < 2
    variance = i>=2 ? (i*sum_sq - sum*sum)/(i*(i-1)) : 0;

    // printf("   %5lu, %5lu  %10lu %0.2f\n", num_ops, value, min, (sum/(double)i));
    if (i == iterations) {
      printf("%6d: avg=%0.2f min=%d (%0.2f) max=%d var=%0.2f\n", num_ops, ((double)sum) / i, min, ((double)min/num_ops), max, variance);
    }
  }// end


// printf("%5lu  %10lu %10lu\n", num_ops, min, (sum/iterations));

}


int main(int argc, char* argv[]) {
    for (int i = 0; i < num_functions; i++) {
    take_measurement(functions[i],  num_ops[i]);
  }
}