#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include "smartall.h"


int * nums;
int * parent_nums;
char * program;
int max_nums;
const int maxnum = 1<<25;
int debug;

/* Utility functions */

void sig_int_handler(int);
/*
 * My own version of an error routine.
 */
void M_error(char * errorstr, int exitval) {
  if (debug == 1 || exitval != 52) {
    printf("%s: %s\n", program, errorstr);
    sm_dump(0);
    exit(exitval);
  }
}


/*
 * This function assumes that the global array nums has been freed by this
 * time.  If it has not, you will have a memory leak here.
 */
void init_array(int length) {
  /* calloc does an implicit set of each element to 0. */
  nums = (int *)calloc(length, sizeof(int));
}
void init_parent(int length) {
  /* calloc does an implicit set of each element to 0. */
  parent_nums = (int *) calloc(length, sizeof(int));
}


/*
 * This function will output the entire array, and give statistics for the 
 * numbers calculated.
 */
void out_array() {
  char buf[255];
  int i = 0;
  int sum = 0;
  int avg = 0;
  int total = 0;

  if (!nums) return;
  if (!parent_nums) 
    M_error("INTERNAL Error:: No parent array", 52);

  for (; i < max_nums; i++) {

    sum += nums[i]*i;
    total += nums[i];    
    avg = sum/(total?total:1);

    sprintf(buf, "%d = %d  ", i, nums[i]);
    if (debug) printf("%-20s", buf);
    if (debug && !(i % 4)) printf("\n");

    parent_nums[i] += nums[i];
    if (parent_nums[i] > maxnum)
      sig_int_handler(-1);

  }

  if (debug) printf("\navg = %d,  sum = %d,  total = %d\n", avg, sum, total);
  if (debug) printf("max_nums = %d\n", max_nums);

  free(nums);
  nums = NULL;
}
/*
 * This function will output the entire parent array, and give statistics for  
 * entire run.
 */
void out_final() {
  char buf[255];
  int i = 0;
  int sum = 0;
  int avg = 0;
  int total = 0;

  if (!parent_nums) 
    M_error("INTERNAL Error:: No parent array", 52);

  for (; i < max_nums; i++) {
    sum += parent_nums[i]*i;
    total += parent_nums[i];    
    avg = sum/(total?total:1);
  }
  for (i = 0; i < max_nums; i++) {
    sprintf(buf, "%d = %d (%2.2f%%)", i, parent_nums[i], parent_nums[i]/(double)total);
    printf("%-20s", buf);
    if (!(i % 4) && (i != 0)) printf("\n");
  }
  printf("\navg = %d,  sum = %d,  total = %d\n", avg, sum, total);
  printf("max_nums = %d\n", max_nums);
  free(parent_nums);
  parent_nums = NULL;
}

int number(int low, int high) {
  return ((rand()%(high-low+1))+low);
}

/*
 * This is the signal handler for ^C, because ^C is the prescribed method of 
 * exiting this program, the output is output in the ^C handler.
 * On the other hand, kill can be used...so this interrupt handler will be
 * for SIGINT, SIGHUP, SIGPIPE, SIGTERM, SIGQUIT, SIGILL, SIGABRT, SIGALRM
 */
void sig_int_handler(int p) {
  out_final();
  sm_dump(0);
  exit(0);
}



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

/* Trap basically all signals but SYS PWR TRAP BUS FPE SEGV EMT STOP KILL */
  signal(SIGINT, sig_int_handler);
  signal(SIGHUP, sig_int_handler);
  signal(SIGPIPE, sig_int_handler);
  signal(SIGTERM, sig_int_handler);
  signal(SIGQUIT, sig_int_handler);
  signal(SIGILL, sig_int_handler);
  signal(SIGABRT, sig_int_handler);
  signal(SIGALRM, sig_int_handler);
 
  program = argv[0];
  if (argc < 3)
    /* If we don't have enough arguments, then output usage. */
    M_error("Usage:  random <iterations> <maxNum>\n", 1); 

  /* Check arguments. */
  if (isdigit(*argv[1])) 
    iterations = atoi(argv[1]);
  else 
    M_error("Usage: random <iterations> <maxNum>\niterations must be a number.\n", 1);
    
  if (isdigit(*argv[2]))
    max_nums = atoi(argv[2]);
  else 
    M_error("Usage:  random <iterations> <maxNum>\nmaxNum must be a number.\n", 1);

  /* Check for a shadow argument. */
  /* This argument is not advertised to the user, and should not be.  It 
   * outputs far too much information. */
  if (argc == 4 && !strncmp(argv[4], "debug", 5)) 
    debug = 1;
  else if (argc > 3)
    M_error("Usage: random <iterations> <maxNum>\n", 1); 
  else 
    debug = 0;

  init_parent(max_nums);
  srand(time(0));
  while (1) {
    init_array(max_nums);   
    for (i = 0; i < iterations; i++) {
      if (nums[number(0,max_nums-1)]++ > maxnum)
      { out_array(); sig_int_handler(-1); } 
    }
    out_array();
    sleep(1);
  }
  
/*  .......*****-------  We Should NEVER Get Here -------*******.......       */
  out_final();
  return 0;
}
