/*
 This file is part of pathrate.

 pathrate is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 pathrate is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with pathrate; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*-------------------------------------------------
   pathrate : an end-to-end capcity estimation tool
   Author   : Constantinos Dovrolis (dovrolis@cc.gatech.edu )
              Ravi S Prasad            ( ravi@cc.gatech.edu )
              
   Release  : Ver 2.4.1
   Support  : This work was supported by the SciDAC
              program of the US department 
--------------------------------------------------*/


#include "pathrate.h"
#include "pathrate_rcv.h"

/*-----------------------------------------------------------------------------*
 *  * Weiling: function get_kurtosis
 *   * requires at least 3 samples,
 *    * returns -99999 if error
 *     *-----------------------------------------------------------------------------*/
double get_kurtosis(double* bell_array, int size){
  double kurtosis, var, mean, temp, diff;
  int i;

  if(size < 3)
    return -99999;

  temp = 0;
  for(i = 0; i < size; i++)
    temp += bell_array[i];
  mean = temp / size;

  temp = 0;
  for(i = 0; i < size; i++) {
    diff = bell_array[i] - mean;
    temp += diff * diff;
  }
  var = temp / size;

  if(var == 0)
    return -99999;

  temp = 0;
  for(i = 0; i < size; i++){
    diff = bell_array[i] - mean;
    temp += diff * diff * diff * diff;
  }

  kurtosis = temp / (var * var);
  return kurtosis;
}

void prntmsg(FILE *fp) {
  fprintf(fp, "%s", message);
  fflush(fp);
}

/*
  Send a message through the control stream
*/
void send_ctr_msg(long ctr_code) 
{
  char ctr_buff[24];
  long ctr_code_n = htonl(ctr_code);
  memcpy((void*)ctr_buff, &ctr_code_n, sizeof(long));
  if (write(sock_tcp, ctr_buff, sizeof(long)) != sizeof(long)) {
    fprintf(stderr, "send control message failed:\n");
    exit(-1);
  }
  //printf ("Done sending %x\n", (ctr_code & 0x000000FF));
}


/*
        Receive an empty message from the control stream
*/
long recv_ctr_msg(int ctr_strm, char *ctr_buff)
{
  long ctr_code;
  if (read(ctr_strm, ctr_buff, sizeof(long)) != sizeof(long)){
    fprintf(stderr, "recv control message failed:\n");
    return(-1);
  }
  memcpy(&ctr_code, ctr_buff, sizeof(long));
  return(ntohl(ctr_code));
}


/*
  Print a bandwidth measurement (given in Mbps) in the appropriate units
*/
void print_bw(FILE *fp, double bw_v) 
{
  if (bw_v < 1.0) {
    sprintf(message," %.0f kbps ", bw_v*1000);prntmsg(fp);
  }
  else if (bw_v < 15.0) {
    sprintf(message," %.1f Mbps ", bw_v);prntmsg(fp);
  }
  else {   
    sprintf(message," %.0f Mbps ", bw_v);prntmsg(fp);
  }
}


/* 
   Terminate measurements  
*/
void termint(int exit_code) {
  int ctr_code;

  ctr_code = CONTINUE;
  send_ctr_msg(ctr_code);
  ctr_code = GAME_OVER;
  send_ctr_msg(ctr_code);
  fclose(pathrate_fp); close(sock_tcp); close(sock_udp);
  exit(exit_code);
}

/* Successful termination. Print result.  */
void happy_end(double bw_lo, double bw_hi)
{
  sprintf(message,"\n\n-------------------------------------------------\n");
  prntmsg(pathrate_fp);
  sprintf(message,"Final capacity estimate : ");prntmsg(pathrate_fp);
  print_bw(pathrate_fp, bw_lo); 
  sprintf(message," to ");prntmsg(pathrate_fp); 
  print_bw(pathrate_fp, bw_hi); 
  sprintf(message,"\n");prntmsg(pathrate_fp);
  sprintf(message,"-------------------------------------------------\n\n");
  prntmsg(pathrate_fp);
  if(verbose){
    fprintf(stdout,"-------------------------------------------------\nFinal capacity estimate : ");
    print_bw(stdout,bw_lo);
    fprintf(stdout," to ");
    print_bw(stdout,bw_hi);
    fprintf(stdout," \n-------------------------------------------------\n");
  }
  if (netlog) { /* print result for netlooger */
    struct tm *tm;
    struct hostent *rcv_host, *snd_host;
    char rcv_name[256];
    struct timeval curr_time;

    gettimeofday(&curr_time,NULL);
    tm = gmtime(&curr_time.tv_sec);
    fprintf(netlog_fp,"DATE=%4d",tm->tm_year+1900);
    print_time(netlog_fp,tm->tm_mon+1);
    print_time(netlog_fp,tm->tm_mday);
    print_time(netlog_fp,tm->tm_hour);
    print_time(netlog_fp,tm->tm_min);
    if (tm->tm_sec <10) {
      fprintf(netlog_fp,"0");
      fprintf(netlog_fp,"%1.6f",tm->tm_sec+curr_time.tv_usec/1000000.0);
    }
    else{
      fprintf(netlog_fp,"%1.6f",tm->tm_sec+curr_time.tv_usec/1000000.0);
    }
    gethostname(rcv_name, 255);
    rcv_host = gethostbyname(rcv_name);
    if(strcmp(rcv_name, "\0")!=0) fprintf(netlog_fp," HOST=%s",rcv_host->h_name);
    else fprintf(netlog_fp," HOST=NO_NAME");
    fprintf(netlog_fp," PROG=pathrate");
    fprintf(netlog_fp," LVL=Usage");
    if ((snd_host = gethostbyname(hostname)) == 0) {
        snd_host = gethostbyaddr(hostname,256,AF_INET);
    }
    fprintf(netlog_fp," PATHRATE.SNDR=%s",snd_host->h_name);
    fprintf(netlog_fp," PATHRATE.CAPL=%.1fMbps",bw_lo);
    fprintf(netlog_fp," PATHRATE.CAPH=%.1fMbps\n",bw_hi);
    fclose(netlog_fp);
  }
}

/* print time */
void print_time(FILE *fp, int time)
{
  if( time<10){
    fprintf(fp,"0");
    fprintf(fp,"%1d",time);
  }
  else{
    fprintf(fp,"%2d",time);
  }
}

/*
  Compute the time difference in microseconds
  between two timeval measurements
*/
double time_to_us_delta(struct timeval tv1, struct timeval tv2) 
{
   double time_us;
   time_us = (double)
     ((tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec));
   return time_us;
}

void time_copy(struct timeval time_val_old, struct timeval *time_val_new) 
{
   (*time_val_new).tv_sec =time_val_old.tv_sec;
   (*time_val_new).tv_usec=time_val_old.tv_usec;
}


/* 
  Order an array of doubles using bubblesort 
*/
void order(double unord_arr[], double ord_arr[], long no_elems)
{
  long i,j;
  double temp;
  for (i=0; i<no_elems; i++) ord_arr[i]=unord_arr[i];
  for (i=1; i<no_elems; i++) 
    for (j=i-1; j>=0; j--) {
      if (ord_arr[j+1]<ord_arr[j]) {
         temp=ord_arr[j]; ord_arr[j]=ord_arr[j+1]; ord_arr[j+1]=temp;
      }
      else break;
    }
}


/*
  Compute the average of the set of measurements <data>.
*/
double get_avg(double data[], long no_values)
{
   long i;
   double sum_;
   sum_ = 0; 
   for (i=0; i<no_values; i++) sum_ += data[i];
   return (sum_ / (double)no_values);
}



/*
  Compute the standard deviation of the set of measurements <data>.
*/
double get_std(double data[], long no_values)
{
   long i;
   double sum_, mean;
   mean = get_avg(data, no_values);
   sum_ = 0;
   for (i=0; i<no_values; i++) sum_ += pow(data[i]-mean, 2.);
   return sqrt(sum_ / (double)(no_values-1));
}


/*
  Detect a local mode in the set of measurements <ord_data>.
  Take into account only the valid (unmarked) measurements (vld_data[i]=1). 
  The bin width of the local mode detection process is <bin_wd>. 
  The set has <no_values> elements, and it is ordered in increasing sequence.
  The function returns the center value of the modal bin.
  Also, the following call-by-ref arguments return:
    mode_cnt:    # of measurements in modal bin
    bell_cnt:    # of measurements in entire mode   
             (modal bin+surrounding bins of the same `bell')
    bell_lo:     low bandwidth threshold of modal `bell'
    bell_hi:     high bandwidth threshold of modal `bell'
*/ 
double get_mode(double ord_data[], short vld_data[], 
    double bin_wd, int no_values, mode_struct *curr_mode)
{
  int 
    i, j, done, tmp_cnt, 
    mode_lo_ind, mode_hi_ind, bell_lo_ind, bell_hi_ind,
    bin_cnt,  bin_lo_ind,  bin_hi_ind,  
    lbin_cnt, lbin_lo_ind=0, lbin_hi_ind=0,  /* left bin */
    rbin_cnt, rbin_lo_ind=0, rbin_hi_ind=0;  /* right bin */
  double
    mode_lo, mode_hi, bin_lo,  bin_hi;
  /*Weiling: bin_toler, used as */
  double bin_cnt_toler;
  

  /* Check if all measurements have been already marked */
  j=0;
  for (i=0; i<no_values; i++) j+=vld_data[i];
  if (j==0) return LAST_MODE; /* no more modes */
  #ifdef MODES 
    printf("\n%d valid measurements\n", j);
  #endif
  /* Initialize mode struct */
  curr_mode->mode_cnt=0; 
  curr_mode->bell_cnt=0; 
  curr_mode->bell_lo=0; 
  curr_mode->bell_hi=0; 

  /* 
    Find the bin of the primary mode from non-marked values 
  */

  /* Find window of length bin_wd with maximum number of consecutive values */
  mode_hi=0; mode_hi_ind=0; mode_lo=0; mode_lo_ind=0;
  tmp_cnt=0;
  for (i=0;i<no_values;i++) {
    if (vld_data[i]) {
      j=i;
      while (j<no_values && vld_data[j] && ord_data[j]<=ord_data[i]+bin_wd) j++;
      if (tmp_cnt<j-i) {
        tmp_cnt = j-i;
        mode_lo_ind = i;
        mode_hi_ind = j-1;
      }
    }
  }

  curr_mode->mode_cnt  = tmp_cnt;
  mode_lo   = ord_data[mode_lo_ind];
  mode_hi   = ord_data[mode_hi_ind];
  #ifdef MODES 
    printf("Central mode bin from %.3f to %.3f (%d msrms)", mode_lo, mode_hi, *mode_cnt);
    printf(" - (%d-%d)\n", mode_lo_ind, mode_hi_ind);
  #endif

  curr_mode->bell_cnt  = tmp_cnt;
  curr_mode->bell_lo  = mode_lo;
  curr_mode->bell_hi  = mode_hi;
  bell_lo_ind     = mode_lo_ind;
  bell_hi_ind     = mode_hi_ind;
  
  /*
        Find all the bins at the *left* of the central bin that are part of the
        same mode's bell. Stop when another local mode is detected.
  */
  bin_cnt    = curr_mode->mode_cnt;
  bin_lo_ind =  mode_lo_ind;
  bin_hi_ind =  mode_hi_ind;
  bin_lo     =  mode_lo;
  bin_hi     =  mode_hi;
  
    /* Weiling:  noise tolerance is determined by bin_cnt_toler, and it's 
     * proportional to previous bin_cnt instead of constant BIN_NOISE_TOLER . */

  done=0;
  bin_cnt_toler = BIN_CNT_TOLER_kernel_percent * (bin_cnt);
  do {
      /* find window of measurements left of the leftmost modal bin with 
       (at most) bin_wd width and with the maximum number of measurements */
    lbin_cnt=0;
    if (bin_lo_ind>0) {
      for (i=bin_hi_ind-1; i>=bin_lo_ind-1; i--) {
        tmp_cnt=0;
        for (j=i; j>=0; j--) {
          if (ord_data[j]>=ord_data[i]-bin_wd) tmp_cnt++;
          else break;
        }
        if (tmp_cnt >= lbin_cnt) {
          lbin_cnt    = tmp_cnt;
          lbin_lo_ind = j+1;
          lbin_hi_ind = i;
        }
      }
    }
    #ifdef MODES 
         printf("Left bin: %.3f to %.3f", ord_data[lbin_lo_ind], ord_data[lbin_hi_ind]);
         printf(" (%d msrms)", lbin_cnt);
         printf(" - (%d-%d)", lbin_lo_ind, lbin_hi_ind);
    #endif
  
    if (lbin_cnt>0) {
      if (lbin_cnt < bin_cnt+bin_cnt_toler) {
         /* the bin is inside the modal bell */
        #ifdef MODES 
          printf(" - Inside mode");
        #endif

        /* update bell counters */
        /* Weiling:
         In prevoius version (2.3.0), the count and lo threshold counters are updated
         only if bin has a significant number of measurements
         In this version (2.3.1), the count and lo threshold counters are updated anyway
        */

        curr_mode->bell_cnt += bin_lo_ind-lbin_lo_ind;
        curr_mode->bell_lo   = ord_data[lbin_lo_ind];
        bell_lo_ind = lbin_lo_ind;
  
        /* reset bin counters for next iteration */
        bin_cnt     = lbin_cnt;
        bin_lo_ind  = lbin_lo_ind;
        bin_hi_ind  = lbin_hi_ind;
        bin_lo      = ord_data[lbin_lo_ind];
        bin_hi      = ord_data[lbin_hi_ind];
        bin_cnt_toler = BIN_CNT_TOLER_kernel_percent * (bin_cnt);
      } 
      else {
        /* the bin is outside the modal bell */
        done=1;
        #ifdef MODES 
        printf(" - Outside mode");
        #endif
      } 
      if (bin_lo_ind <= 1) done=1;
    } 
    else done=1;
    #ifdef MODES 
      printf("\n");
    #endif
  } while (!done);




  /* 
    Find all the bins at the *right* of the central bin that are part of the
    same mode's bell. Stop when another local mode is detected. 
  */
  bin_cnt    = curr_mode->mode_cnt;
  bin_lo_ind =  mode_lo_ind; 
  bin_hi_ind =  mode_hi_ind; 
  bin_lo     =  mode_lo;
  bin_hi     =  mode_hi; 
  
  done=0;
  do {
    /* find window of measurements right of the rightmost modal bin with 
       (at most) bin_wd width and with the maximum number of measurements */
    rbin_cnt=0;
    if (bin_hi_ind<no_values-1) {
      for (i=bin_lo_ind+1; i<=bin_hi_ind+1; i++) {
        tmp_cnt=0;
        for (j=i; j<no_values; j++) {
           if (ord_data[j]<=ord_data[i]+bin_wd) tmp_cnt++; 
           else break;
         }
        if (tmp_cnt >= rbin_cnt) {
          rbin_cnt    = tmp_cnt;
          rbin_lo_ind = i;
          rbin_hi_ind = j-1;
        }
      }
    }
    #ifdef MODES 
       printf("Right bin: %.3f to %.3f", ord_data[rbin_lo_ind], ord_data[rbin_hi_ind]);
       printf(" (%d msrms)", rbin_cnt);
       printf(" - (%d-%d)", rbin_lo_ind, rbin_hi_ind);
    #endif

    if (rbin_cnt>0) {
      if (rbin_cnt < bin_cnt+bin_cnt_toler) { 
              /* the bin is inside the modal bell */
        #ifdef MODES 
          printf(" - Inside mode");
        #endif

        /* update bell counters */
        /* Weiling:
         In prevoius version (2.3.0), the count and lo threshold counters are updated
         only if bin has a significant number of measurements
         In this version (2.3.1), the count and lo threshold counters are updated anyway
        */

        curr_mode->bell_cnt += rbin_hi_ind-bin_hi_ind;
        curr_mode->bell_hi   = ord_data[rbin_hi_ind];
        bell_hi_ind = rbin_hi_ind;
  
            /* reset bin counters for next iteration */
        bin_cnt     = rbin_cnt;
        bin_lo_ind  = rbin_lo_ind;
        bin_hi_ind  = rbin_hi_ind;
        bin_lo      = ord_data[rbin_lo_ind];
        bin_hi      = ord_data[rbin_hi_ind];
        bin_cnt_toler = BIN_CNT_TOLER_kernel_percent * (bin_cnt);
      }
      else {
              /* the bin is outside the modal bell */
        done=1;
        #ifdef MODES 
          printf(" - Outside mode");
        #endif
      }
      if (rbin_hi_ind >= no_values-2) done=1;
    }
    else done=1;
    #ifdef MODES 
      printf("\n");
    #endif
  } while (!done);

  /* Mark the values that make up this modal bell as invalid */ 
  for (i=bell_lo_ind; i<=bell_hi_ind; i++) vld_data[i]=0;

  /* Report mode characteristics */
  if (curr_mode->mode_cnt > BIN_NOISE) 
  {
    sprintf(message,"\t* Mode:"); prntmsg(pathrate_fp);  
    if (verbose) prntmsg(stdout);
    print_bw(pathrate_fp, mode_lo);
    if (verbose) print_bw(stdout, mode_lo);
    sprintf(message,"to"); prntmsg(pathrate_fp); 
    if (verbose) prntmsg(stdout);
    print_bw(pathrate_fp, mode_hi);
    if (verbose) print_bw(stdout, mode_hi);
    sprintf(message," - %ld measurements\n\t  Modal bell: %ld measurements - low :",
        curr_mode->mode_cnt, curr_mode->bell_cnt); prntmsg(pathrate_fp);
    if (verbose) prntmsg(stdout);
    print_bw(pathrate_fp, curr_mode->bell_lo);
    if (verbose) print_bw(stdout, curr_mode->bell_lo);
    sprintf(message,"- high :"); prntmsg(pathrate_fp); 
    if (verbose) prntmsg(stdout);
    print_bw(pathrate_fp, curr_mode->bell_hi);
    if (verbose) print_bw(stdout, curr_mode->bell_hi);
    sprintf(message,"\n");prntmsg(pathrate_fp);
    if (verbose) prntmsg(stdout);
    /* Weiling: calculate bell_kurtosis*/
    curr_mode->bell_kurtosis = get_kurtosis(ord_data + bell_lo_ind , bell_hi_ind - bell_lo_ind + 1);
    if(curr_mode->bell_kurtosis == -99999){
      sprintf(message, "\nWarning!!! bell_kurtosis == -99999\n"); prntmsg(pathrate_fp);
      if (verbose) prntmsg(stdout);
      return UNIMPORTANT_MODE;
    }
  
  /* Return the center of the mode, as the average between the high and low
       ends of the corresponding bin */
    curr_mode->mode_value_lo = mode_lo;
    curr_mode->mode_value_hi = mode_hi;
    return (LOCAL_MODE); 
  } 
  else return UNIMPORTANT_MODE; 
}

void sig_alrm(int signo)
{
    return;
}

void *ctr_listen(void *arg)
{
  fd_set readset;
  long ret_val ;
  char ctr_buff[8];
  pthread_t  *dad_id;

  dad_id = (pthread_t *) arg;

  FD_ZERO(&readset);
  FD_SET(sock_tcp,&readset);
  if (select(sock_tcp+1,&readset,NULL,NULL,NULL) > 0 ){
    if ( FD_ISSET(sock_tcp,&readset) ){
      if (( ret_val = recv_ctr_msg(sock_tcp,ctr_buff)) != -1 ){

        if (ret_val == SENT_TRAIN) {
          pthread_kill(*dad_id, SIGALRM);
        }
        else {
          if (verbose) printf("\n\nSender sent terminate signal. Exiting...\n");
          termint(-1);
        }
      }
    }
  }
  pthread_exit(NULL);
}

/* Receive a complete packet train from the sender */
int recv_train(int train_len, int * round, struct timeval *time[]) {
  int exp_pack_id=0, exp_train_id=0;
  int pack_id, round_id, train_id;
  char pack_buf[1600];
  long ctr_code;
  int pack_sz = 1500;
  int bad_train=0;
  pthread_t th_id, my_id;
  pthread_attr_t attr ;

  struct sigaction sigstruct ;

  sigstruct.sa_handler = sig_alrm ;
  sigemptyset(&sigstruct.sa_mask);
  sigstruct.sa_flags = 0 ;
  #ifdef SA_INTERRUPT
     sigstruct.sa_flags |= SA_INTERRUPT ;
  #endif
  sigaction(SIGALRM , &sigstruct,NULL );
   
  *time = (struct timeval *) malloc((train_len+1)*sizeof(struct timeval));
  if (time == NULL) {
    fprintf(stderr,"Pathrate_rcv: In recv_train: Insufficient memory\n");
    exit(-1);
  }

  pthread_attr_init(&attr);
  my_id = pthread_self();
  if(pthread_create(&th_id, &attr, ctr_listen, (void *) &my_id)){
    perror("Pathrate_rcv: In recv_train:");
    fprintf(stderr,"Pathrate_rcv: In recv_train: Error creating thread\n");
    exit(-1);
  }

  recv_train_done = 0;
  if (!retransmit) {
    (*round)++;
  }
  ctr_code = SEND | ((*round)<<8);
  send_ctr_msg(ctr_code);
  do {
    if (recvfrom(sock_udp, pack_buf, pack_sz, 0, (struct sockaddr*)0, (int*)0) == -1) {
      /* interrupted??? */
      if (errno == EINTR) {
        if (exp_pack_id==train_len+1) recv_train_done = 1;
        pthread_join(th_id, NULL);

        sigstruct.sa_handler = SIG_DFL ;
        sigemptyset(&sigstruct.sa_mask);
        sigstruct.sa_flags = 0 ;
        sigaction(SIGALRM , &sigstruct,NULL );

        if (recv_train_done) {
          /* Send control message to ACK burst */
          retransmit=0;
          ctr_code = ACK_TRAIN;
          send_ctr_msg(ctr_code);
          return(bad_train);
        }
        else {
          /* send signal to recv_train */
          retransmit=1;
          ctr_code = NEG_ACK_TRAIN;
          send_ctr_msg(ctr_code);
          return(2);
        }
      }
      else { 
#ifdef DEBUG
  fprintf(stderr,"DEBUG: Why here???\n");
#endif
        perror("RECVFROM:");
        termint(-1);
      }
    }
    else {
      gettimeofday(*time+exp_pack_id, (struct timezone*)0);
      memcpy(&pack_id, pack_buf, sizeof(long));
      pack_id=ntohl(pack_id);
      memcpy(&train_id, pack_buf+sizeof(long), sizeof(long));
      train_id=ntohl(train_id);
      memcpy(&round_id, pack_buf+2*sizeof(long), sizeof(long));
      round_id=ntohl(round_id);

      // printf("Pack %d, %d, %d, %d, %d\n", pack_id, train_id, round_id, exp_pack_id, exp_train_id);
     
      if (round_id!=(*round)) {
        bad_train = 1;
      }
      else {
        if (pack_id==0) {
          bad_train=0;
          exp_pack_id=1;
          exp_train_id=train_id;
        }
        else if (pack_id==exp_pack_id ) {// && train_id==exp_train_id) 
          exp_pack_id++;
        }
        else if (train_id != exp_train_id) {
          bad_train = 2;
        }
      }
    }
  }while (1);
}

/* Print help on the options */
void help(void) {
  fprintf (stderr,"pathrate_rcv options\n");
  fprintf (stderr,"-s <host> : sender host (required)\n");
  fprintf (stderr,"-Q        : quick termination mode\n");
  fprintf (stderr,"-q        : quite mode\n");
  fprintf (stderr,"-v        : verbose mode\n");
  fprintf (stderr,"-o <file> : print log in file\n");
  fprintf (stderr,"-O <file> : append log in file\n");
  fprintf (stderr,"-N <file> : print result in ULM format in file\n");
  fprintf (stderr,"-h|-H     : print this help and exit \n");
  fprintf (stderr,"usage: pathrate_rcv [-H|-h] [-Q] [-q|-v] [-o|-O <filename>] [-N <filename>] -s <sender>\n");
  exit(-1);
}

/* Trying long trains to detect capacity in case interrupt 
 * coalescing detected.
 */
int gig_path(int pack_sz, int round, int k_to_u_latency){
  int i, j, est = 0, ctr_code, tmp;
  int train_len=200, bad_train, no_of_trains = 10;
  struct timeval *pkt_time = NULL;
  double cap[300],ord_cap[300];

  sprintf(message,"Test with Interrupt Coalesence\n  %d Trains of length: %d \n", no_of_trains, train_len);
  prntmsg(pathrate_fp);
  if(Verbose) prntmsg(stdout);
  for (j=0; j<no_of_trains; j++){
    int disps[300];
    int num_b2b = 1;
    int k=0;
    int id[300];
    double tmp_cap, adr;

    sprintf(message,"  Train id: %d -> \n\t", j);
    prntmsg(pathrate_fp);
    if(Verbose) prntmsg(stdout);
    ctr_code = TRAIN_LEN | (train_len<<8);
    send_ctr_msg(ctr_code);
    ctr_code=PCK_LEN | (pack_sz<<8);
    send_ctr_msg(ctr_code);
    if ( pkt_time != NULL ) free (pkt_time);
    bad_train = recv_train(train_len, &round, &pkt_time);
    if (bad_train == 2) {/* train too big... try smaller */
      train_len-=20;
      sprintf(message,"Reducing train size to %d\n", train_len);
      //sprintf(message,"\n");
      prntmsg(pathrate_fp);
      if(Verbose) prntmsg(stdout);
      if (train_len < 100 && est < 5) {
        printf("Insufficient number of packet dispersion estimates.\n");
        fprintf(pathrate_fp,"Insufficient number of packet dispersion estimates.\n");
        printf("Probably a 1000Mbps path.\n");
        fprintf(pathrate_fp,"Probably a 1000Mbps path.\n");
        termint(-1);
      }
    }
    else {
      adr = (train_len - 2)*12000 /time_to_us_delta(pkt_time[1], pkt_time[train_len]);
      for (i=2; i<=train_len; i++){
        disps[i] = time_to_us_delta(pkt_time[i-1], pkt_time[i]);
        // fprintf(stderr,"%d %d, ",i, disps[i]);
      }
      id[k++]=1;
	    num_b2b = 1;
      for (i=2; i<train_len; i++){
        if ( (num_b2b<=5) ||(disps[i] < num_b2b*1.5*k_to_u_latency) ){
           num_b2b++;
        }
        else{
          id[k++]=i;
	        num_b2b = 1;
        }
      }
      for (i=1; i<k-1; i++) {
        tmp_cap = 12000.0*(id[i+1]-id[i]-1)/
	           (time_to_us_delta(pkt_time[id[i]], pkt_time[id[i+1]]));    
        if (tmp_cap >= .9*adr) {
          cap[est]=tmp_cap;
          print_bw(pathrate_fp, cap[est++]);
          if (Verbose) print_bw(stdout, cap[est-1]);
          sprintf(message,"\n\t");
          prntmsg(pathrate_fp);
          if(Verbose) prntmsg(stdout);
        }
      }
      sprintf(message, "Number of jump detected = %d\n", k);
      prntmsg(pathrate_fp);
      if (Verbose) prntmsg(stdout);
    }
  }
  if ( est >1 ) {
    order(cap,ord_cap,est);
    tmp = (int)(.9 * est );
    happy_end(ord_cap[tmp-1], ord_cap[tmp]);
  }
  else {
    printf("Insufficient number %d of packet dispersion estimates.\n", est);
    fprintf(pathrate_fp,"Insufficient number of packet dispersion estimates.\n");
    printf("Probably a 1000Mbps path.\n");
    fprintf(pathrate_fp,"Probably a 1000Mbps path.\n");
    termint(-1);
  }

  termint(0);
  return(1);
}

int intr_coalescence(struct timeval time[],int len, double latency)
{
  double delta[100];
  int b2b=0;
  int i;


  for (i=2;i<len;i++)
  {
    delta[i] = time_to_us_delta(time[i-1],time[i]);
#ifdef DEBUG 
   fprintf(stderr,"DEBUG: i %d, disp[i] %.2f\n", i, delta[i]);
#endif
    if ( delta[i] <= 2.5 * latency )
    {
      b2b++ ;
    }
  }
#ifdef DEBUG 
  fprintf(stderr,"DEBUG: b2b %d len %d\n",b2b,len);
#endif
  if ( b2b > .6*len ) return 1;
  else return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1