/*
 * This code is based on Arizona's Vegas TCP/IP. Written by
 * O. Ait-Hellal by changing Jacobson-Karels FTP source.
 * Originally written by S. Keshav. Revised by S. McCanne@LBL
 */

 /* The time resolution is supposed to be 10 ns */

#include <math.h>
#include <stdio.h>
#include "../kernel/real.h"
#include "tcp.h"

/* parameters for Vegas control prtocol */

#define   beta_thresh 4

/* max extrat buffers in the network (Vegas 2-4) */
#define  alpha_thresh 2

/*      min extrat buffers in the network  s     */
#define  gamma_thresh 1
 
    /*          extra buffers at slow-start           */

static void     vegas_output ();
static void     vegas_slowtimo ();
static void     vegas_timers ();
static void     vegas_ack ();

struct frame
{
  PKT_PTR       pkt;
 
  /* in BSD, packets are queued for retransmissions (prev, next).
     Here we create a packet every time we need to retransmit it */
 
  short         nn;
  int           timeout;
  long          last_sent;      /* seq of last pkt. sent */
  long          last_ack;       /* last ack recvd */
 
  int           cur_win;  /* current window size */
  int           ssthresh;       /* slow start threshold */
  int           count;   /* count of # of increments made
     * to cur_window so far */
  int           max_win;        /* max window size allowed */
  short         mdev;         /* of rtt estimate from mean */
  short         rtt;   /* actually, the estimate */
  short         srtt;
 
  long          t_rtseq;
 
  char          back_off;     /* determines multiplier for timeout */
 
  int           line_busy;      /* respect of the interpacket delay */

  char          n_retx[MAX_WINDOW]; /* number of retxs of packet i
         * in cur win */
 
  int           t_timer[MAX_TIMERS];
 
  char          t_dupacks;       /* number of dupliacte Acks seen */
  char          t_rexmtthresh;   /* number of duplicate Acks for retxsion */
  long          max_sent;
 
  char          dropped[MAX_WINDOW_SIZE];
 
                /* parameters for Vegas */
 
  char          transmits[MAX_WINDOW]; /* largest number an acked
     * segment was sent */
 
  timev         time_sent[MAX_WINDOW];
 
  int           base_RTT;         /* the smaller round-trip-time  */
 
               /* Vegas timer stuff */
  int           v_rtt;
  int           v_sa;
  int           v_sd;
  long          v_timeout;
 
  int           v_newcwnd;
 
  /* Stores new uninflated cwnd, used to reset cwnd later.*/
 
  /* Exp increase every other during slow-start. */
  char          v_exp_inc_do;
  char          v_exp_inc_flag;
  char          v_exp_inc_isless;
 
  /* Congestion detection (or more correctly avoidance ) variables.
   * The mechanism checks once per RTT. Current RTT began with sequence
   * v_cong_detect_begseq */
 
  char          num_ack_after_retx;   /* sequence number of ack after
          * retx is it the first or second */
 
  char          v_cong_detect_do;     /* whether to do it */
  long          v_cong_detect_begseq; /* When RTT began */

  char          v_cong_detect_predict_do;
  char          Transmits;
 
  /* Whether to predict the available bw during slow-start */
 
  long          v_cong_detect_sumRTT;
 
  /* Used to get average (avera = sumRTT/cntRTT) */
  int           v_cong_detect_cntRTT;
 
  /* number of rtt mesured during a RTT interval */
 
  /* The congestion window should stay within band. Going above
   * it means that not enough buffers are being used at the
   * bottleneck, so cwnd should increase. Going below the band means
   * that too many buffers are being used, so cwnd should decrease. */
 
  char          v_cong_detect_top_isless;
 
  /* => cwnd < top of band */
  char          v_cong_detect_bot_isless;
 
  /* => cwnd < bot of band */
  int         v_incr;
 
  /* How much to increase cwnd when an ACK is received */
 
  timev         v_time_cwin_chg;
  timev         v_cong_detect_begtime;
  timev         v_cong_detect_last_sendtime;
 
  /* Time when congestion window was last decreased. Used to prevent
   *decreasing cwin more than once for losses which occurred at the
   same cwin level */
  int           s_n_retx;
 
};

vegas()
{
  /* Dummy variables for nest that we don't use. */
  ident           sender, destn;
  int             key;
  int             i;
 
  timev now;
  struct frame    f;
 
  bzero ((char *) &f, sizeof f);
 
  f.nn = get_node_id ();
  abs_advance (node_start_time[f.nn]);
  printf ("Vegas ftp source: %d ", f.nn);
  printf ("sending to sink %d\n", assigned_sink[f.nn]);
 
  source_node_type[f.nn] = FTP_SOURCE;
 
  for (i = 0; i< MAX_TIMERS; i++)
    f.t_timer[i] = 0;
 
  f.last_sent         = -1;
  f.max_sent          = -1;
  f.last_ack          = -1;
  f.t_rexmtthresh     = 3;
  f.t_dupacks         = 0;
  f.num_ack_after_retx= 0;
  f.max_win           = ftp_window;
  f.ssthresh          = 32;
  f.cur_win           = 1;
  f.count             = 0;
 
  f.mdev              = 4;
  f.rtt               = 1;
  f.t_rtseq           = -1;
  f.srtt              = 1;
  f.back_off          = 0;
  f.timeout           = 2;
  f.s_n_retx          = 0;
  f.line_busy         = 0;
 
  f.v_cong_detect_predict_do = 0;
  f.v_exp_inc_do             = 1; /* 1 if you want to use vegas, otherwise Reno is running */
  f.v_exp_inc_flag           = 1;
  f.v_exp_inc_isless         = 0;
 
  f.v_cong_detect_do         = 1; /* 1 if you want to use vegas, otherwise Reno is running */
  f.v_cong_detect_begseq     = -1;
  (f.v_cong_detect_begtime).tv_sec  = 0;
  (f.v_cong_detect_begtime).tv_usec = 0;
  f.base_RTT = 100000000;
  f.v_cong_detect_sumRTT     = 0;
  f.v_cong_detect_cntRTT     = 0;
  f.v_cong_detect_top_isless = 0;
  f.v_cong_detect_bot_isless = 0;
 
  f.v_incr                   = 0;
  f.v_timeout                = 1000;
  f.v_sa                     = 300 << 8;
  f.v_rtt = f.v_sd           = 0;
  f.Transmits                = 0;
 
  f.t_rtseq = -1;
  f.back_off = 0;
  f.timeout = 2;
  f.t_timer[TCPT_REXMT] = f.timeout;
  /* send the first packet */
 
  (void) vegas_output (&f);
 
  /* schedule the next software timer  500 ms */
  {
 
    PKT_PTR         tick_pkt;
    float            delay;
    timev           now;
    now = runtime ();
 
    hold();
    tick_pkt = (PKT_PTR) malloc ((unsigned) sizeof (PKT));
    release(1);
    tick_pkt->source = tick_pkt->dest = f.nn;
    tick_pkt->size = ftp_size;
    tick_pkt->type = TICK;
    delay = 0.5;
    set_timer (delay, tick_pkt);

  }
 
  while (1)
    {
      sender = recvm (&destn, &key, &f.pkt);
      switch (f.pkt->type)
 {
 
 case ACK:
   vegas_ack (&f);
   break;
 
 case INT:
   hold ();
   f.line_busy = 0;
   free (f.pkt);
   release (1);
   vegas_output (&f);
   break;
 
 case TIMEOUT:
   hold ();
   free (f.pkt);
   release (1);
   break;
 case TICK:
   hold ();
   free (f.pkt);
   release (1);
   vegas_slowtimo (&f);
   break;

 default:
   hold ();
   free (f.pkt);
   release (1);
   pr_error ("Vegas_source recvd. a vague pkt ");
   break;
 }
    }
}

/*---------------------------------------------------------------------------*/
static void vegas_ack (fp)
  struct frame   *fp;
{
  PKT_PTR         pkt;
  int             i;
  long            last, cum_ack, rtt;
  timev           now;
 
  now = runtime();
 
  pkt     = fp->pkt;
  last    = fp->last_ack;
  cum_ack = pkt->seq_no;
  rtt     = (long)(100000000*(make_float(now) - make_float(fp->time_sent[cum_ack %MWS])));
 
  hold ();
  free (pkt);
  release (1);
  make_plot ("ack", cum_ack);
 
  if (cum_ack == fp->last_ack)  /* duplicate Ack seen */
    {
      if (++fp->t_dupacks > fp->t_rexmtthresh)
 {
   if (fp->cur_win < fp->max_win)
     fp->cur_win++;
   (void) vegas_output(fp);
   goto drop;
 
 }
 
      if (fp->t_dupacks == fp->t_rexmtthresh || (expire(fp, cum_ack)))
 {
 
   /*
    * Vegas retransmits when it receives a first or a second dup ack
    * if v_timeout has expired
    */
 
   long       oldsent  = fp->last_sent;
   int      old_cwnd = fp->cur_win;
   int      win      = min (fp->cur_win, fp->max_win);
 
   /*
    * we will check for timed out segments when
    * we receive the next two non-duplicate ACKS
    * (or less depending on how much is in transit)
    */
 
   fp->num_ack_after_retx = min(2, fp->last_sent-fp->last_ack);
 
   fp->last_sent = fp->last_ack;
   fp->cur_win = 1;
   if (fp->Transmits > 1)
     fp->v_timeout << 1;
   else
     fp->v_timeout += fp->v_timeout >> 3;
 
   /*
    * If cwnd hasn't changed since the packet was sent,
    * then we need to decrease it
    */
 
   if (make_float(fp->time_sent[(fp->last_ack+1) % MWS]) >= make_float(fp->v_time_cwin_chg))
     {
       if (win <= 3)
  win = 2;
       else if (fp->Transmits > 1) /* it is retxmd more than once */
  {
    fp->count = fp->count/2;
    win /= 2; /* exponential decrease */
  }
       else{
  fp->count = fp->count/4;
  win       = 3*win/4;
       }
       fp->v_newcwnd = win;
       fp->t_timer[TCPT_REXMT] = 0;
       fp->rtt = 0;
       (void) vegas_output(fp);
       fp->v_time_cwin_chg = runtime ();
       fp->cur_win = fp->v_newcwnd + fp->t_dupacks;
     }
   else /* no need to decrease cwnd */
     {
       (void) vegas_output(fp);
       fp->cur_win = old_cwnd;
 
     }
 
   if (fp->Transmits == 1)
     fp->t_dupacks = fp->t_rexmtthresh;
 
   if ( fp->last_sent < oldsent)
     fp->last_sent = oldsent ;
 }
      goto drop;
    }
 
  if (cum_ack < fp->last_ack)
    {
      fp->t_dupacks = 0;
      goto drop;
    }
 
  /* ack not seen before */
 
  if (fp->t_dupacks >= fp->t_rexmtthresh &&
      fp->cur_win > fp->ssthresh)
    fp->cur_win = fp->ssthresh;
 
  fp->t_dupacks = 0;
 
 
  /*
   * If transmit timer is running and timed sequence
   * number was acked, update smoothed round trip time.
   * Since we now have an rtt measurement, cancel the
   * timer backoff (cfp->, Phil Karn's retransmit alg.).
   * Recompute the initial retransmit timer.
   */
 
  if (fp->rtt && cum_ack >= fp->t_rtseq)
    {
      if (fp->v_timeout == 1000)
 {
   fp->v_rtt = rtt;
   fp->v_sa  = fp->v_rtt << 3;
   fp->v_sd  = fp->v_rtt << 1 ;
   fp->v_timeout = ((fp->v_sa >> 2) +
      fp->v_sd) >> 1;
 }
      if (fp->srtt != 0)
 {
   register short delta;
 
   /*
    * to the smoothing algorithm in rfc793
    * with an alpha of .875
    * (srtt = rtt/8 + srtt*7/8 in integers ???).
    * Adjust t_rtt to origin 0.
    */
 
   fp->rtt--;
   delta = fp->rtt - (fp->srtt >> 3);
   if ((fp->srtt += delta) <= 0)
     fp->srtt = 1;
 
   /*
    * We accumulate a smoothed rtt variance
    * (actually, a smoothed mean difference),
    * then set the retransmit timer to smoothed
    * rtt + 2 times the smoothed variance.
    * rttvar is stored integer
    * The following is equivalent
    * to rfc793 smoothing with an alpha of .75
    * (rttvar = rttvar*3/4 + |delta| / 4).
    * This replaces rfc793's wired-in beta.
    */
 
   if (delta < 0)
     delta = -delta;
   delta -= (fp->mdev >> 2);
   if ((fp->mdev += delta) <= 0)
     fp->mdev = 1;
 } else {
 
   /*
    * No rtt measurement yet - use the
    * unsmoothed rtt.  Set the variance
    * to half the rtt (so our first
    * retransmit happens at 2*rtt)
    */
 
   fp->srtt = fp->rtt << 3;
   fp->mdev = fp->rtt << 1;
 }
      fp->rtt      = 0;
      fp->back_off = 0;
      TCPT_RANGESET(fp->timeout,
      ((fp->srtt >> 2) + fp->mdev) >> 1,
      TCPTV_MIN, TCPTV_REXMTMAX);
    }

  if (cum_ack == fp->max_sent)
    fp->t_timer[TCPT_REXMT] = 0;
  else
    fp->t_timer[TCPT_REXMT] = fp->timeout;
  make_plot("timeout", fp->timeout);

  for (i = 0; i <= cum_ack - last; ++i)
    {
      /* reset the bit anyhow */
      fp->n_retx[(last + i) % MWS] = 0;
    }
 
  fp->last_ack = cum_ack;
 
  /*
   * when new data is acked, Reno increases
   * its cwnd on every Ack if doing slow-start
   * or on every RTT i doing congestion avoidance.
   */
 
  if (fp->last_ack >= fp->v_cong_detect_begseq) {
 
    int rtt1, sendEpoch, actual_rate, pred_rate, rttlen, diff_rate, thresh_rate;
    int win;
    if(fp->v_cong_detect_cntRTT > 0)
      rtt1 = (long) (fp->v_cong_detect_sumRTT / fp->v_cong_detect_cntRTT);
    else
      {
 rtt1 = rtt;
      }
 
    fp->v_cong_detect_sumRTT = 0;
    fp->v_cong_detect_cntRTT = 0;
    rttlen = fp->last_sent - fp->v_cong_detect_begseq;
 
    /*
     * if there was only one packet in transit, then update
     * base_RTT
     */
 
    if (rttlen <= 1 && rtt1 > 0)
      {
 fp->base_RTT = rtt1;
      }
    if (rtt1 > 0)
      {
 
 /*
  * the rates are in  pakets/sec, a time is given in 10 nsec
  */
 
 thresh_rate = 100000000/fp->base_RTT;
 actual_rate = (rttlen*100000000)/ rtt1;
 pred_rate = ((fp->last_sent - fp->last_ack)*(100000000/fp->base_RTT));
 diff_rate = pred_rate - actual_rate;
 
 /* If we are currently doing slow-start */
 if (fp->cur_win < fp->ssthresh)
   {
     /* chek if we are sending faster than available bandwith */
 
     if (diff_rate > gamma_thresh*thresh_rate)
       fp->v_exp_inc_isless = 1;
     else
       fp->v_exp_inc_isless = 0;
     /*
      * sendEpoch is the interval of time during which we sent
      * data. during slow-start, we usually don't send
      * during the whole RTT, only during part of beginning.
      */
 
     sendEpoch = (int)(100000000*(make_float(fp->v_cong_detect_last_sendtime) - make_float(fp->time_sent[fp->v_cong_detect_begseq % MWS])));
 
     win = fp->cur_win - 1;
 
     /*
      * If we trying to predict the available bandwith
      * during slow-start
      */
 
     if (fp->v_cong_detect_predict_do && sendEpoch > 0)
       {
  if (fp->v_exp_inc_do) {
    if (fp->v_exp_inc_flag) {
      if (fp->cur_win >= 8) {
        fp->ssthresh = (win*rtt1)/(2*sendEpoch);
        make_plot ("ssthresh", fp->ssthresh);
        fp->v_cong_detect_predict_do = 0;
      }
    }
    else {
      if (fp->cur_win >= 4){
        fp->ssthresh = (win*rtt1)/(sendEpoch);
        make_plot ("ssthresh", fp->ssthresh);
        fp->v_cong_detect_predict_do = 0;
      }
    }
  }
  else if (fp->cur_win >= 8){
    fp->ssthresh = (win*rtt1)/(2*sendEpoch);
    make_plot ("ssthresh", fp->ssthresh);
    fp->v_cong_detect_predict_do = 0;
  }
  if (fp->ssthresh <= fp->cur_win)
    fp->ssthresh = 2;
  make_plot ("ssthresh", fp->ssthresh);
       }
     fp->v_exp_inc_flag = !fp->v_exp_inc_flag;
   }
 
 /*
  * chek if we are sending too slowly (using less than
  * alpha_thresh buffers 2)
  */
 
 if (diff_rate > alpha_thresh*thresh_rate)
   fp->v_cong_detect_top_isless = 1;
 else
   fp->v_cong_detect_top_isless = 0;
 
 /*
  * Chek if we are sending too fast (using more than
  * beta_thresh-thresh worth of buffers)
  */
 
 if (diff_rate > beta_thresh*thresh_rate)
   fp->v_cong_detect_bot_isless = 1;
 else
   fp->v_cong_detect_bot_isless = 0;
      } /* end of rtt1 > 0 */
 
    fp->v_cong_detect_begseq = fp->last_sent + 1 ;
 
    /*
     * In vegas, the congestion avoidance mechanism decides by how
     * much data to inrease or dcrease the congestion window. During
     * slow-start, the increase only happen during every other RTT,
     * and then the increase is one segment, as long as we have not
     * overran the available bandwith (v_exp_inc_isless==0).
     * If we are not doing slow-start now, then if we need to send
     * faster (top_is_less == 0) we increase it by (1/cwnd), if
     * we need to send slower, then we decrease cwnd by one
     * immediately and we don't change the cwnd during the next RTT
     */
 
    if (fp->cur_win < fp->ssthresh){
      /* slow-start */
      if (fp->v_exp_inc_do){
 /* congestion avoidance during slow-start */
 if (!fp->v_exp_inc_flag)
   /* not increasing during this RTT */
   fp->v_incr = 0;
 else if (fp->v_exp_inc_isless) {
   /* Going faster than avail bw, so slow down */
   fp->ssthresh = 2;
   make_plot ("ssthresh", fp->ssthresh);
   fp->count    = 7*fp->count/8;
   fp->v_incr   = 0;
   fp->cur_win  = (7*fp->cur_win/8);
   if (fp->cur_win < 2)
     fp->cur_win = 2;
 
 }
 else
   /* increase by one with each ACK */
   fp->v_incr = ftp_size;
 
      }
      else
 /* not doing congestion avoidance during slow-start */
 fp->v_incr = ftp_size;
 
    }/* end of doing slow-start (cur_win<ssthresh) */
    else {
      if (fp->v_cong_detect_do) {
 /* doing congestion/detection avoidance */
 
 if (fp->v_cong_detect_bot_isless) {
   /* slow down */
 
   if (fp->cur_win < 2)
     fp->cur_win = 2;
   fp->cur_win -= 1;
   fp->v_incr   = 0;
 }
 else if (fp->v_cong_detect_top_isless)
   /* the current rate is fine */
   fp->v_incr = 0;
 else
   /* we can go faster */
   fp->v_incr = max(ftp_size /  fp->cur_win, 1);
      }
      else
 /* Not doing congestion detection/avoidance */
 fp->v_incr = max(ftp_size /  fp->cur_win, 1);
    }
 
  } /* end of code done once per RTT (cum_ack > beg_seq) */
 
  /*
   * Since we set how much to increase only once per RTT,
   * we need to check during slow-start if we have
   * surpassed sstthresh before the RTT period is over.
   */
 
  if (fp->v_incr == ftp_size && fp->cur_win >= fp->ssthresh)
    fp->v_incr = 0;
 
  /* increase congestion win */
 
  fp->count += fp->v_incr;
  if ((fp->count >= ftp_size) && ((fp->cur_win - (fp->last_sent - fp->last_ack + 1)) <= 2))
    {
      fp->cur_win = min (fp->cur_win + 1, fp->max_win);
      fp->count -= ftp_size;
      if (fp->cur_win == fp->max_win)
 fp->count = 0;
    }
 
  fp->cur_win = min (fp->cur_win, fp->max_win);
 
  /*
   * Do vegas RTT stuffp-> Vegas cheks for timeout when acks are received
   * (usually duplicate acks).
   */
 
  any_retransmission (fp, last);
 
  /*
   * transmits to the largest number of times
   * an acked segment was sent.
   */
 
  if ( (make_float(fp->time_sent[last%MWS]) !=0) && (fp->Transmits == 1)) {
    int n;
    long   rtt2;
 
    /* the rtt here is given, we have not to compute it again */
    rtt2 = rtt;
    fp->v_cong_detect_sumRTT += rtt2;
    fp->v_cong_detect_cntRTT++;
 
    if (rtt2 > 0) {
      fp->v_rtt = rtt2;
 
      /*
       * spike_timeInc is not used
       * vtcp_input.c line 1305
       * the time between two packets
       */
      if (fp->v_rtt < fp->base_RTT)
 {
   fp->base_RTT = fp->v_rtt;
 }
 
      n = fp->v_rtt - (fp->v_sa >> 3);
      fp->v_sa   += n;
      if (n <0) n = -n;
      n -= (fp->v_sd >> 2);
      fp->v_sd   += n;
 
      fp->v_timeout = (((fp->v_sa >> 2) + fp->v_sd) >> 1);
      /*
       * since we use more occurate time, we need
       * to make it a little bigger
       */
      fp->v_timeout += (fp->v_timeout >> 4);
 
    }/* end of if rtt2 > 0 */
  }/* end of vegas stuff */
 
  /*
   * Vegas cheks for timed out segments if we receive
   * a first or second no duplicate ack after retransmission
   */
 
  fp->last_ack = cum_ack;
  if (fp->last_sent < fp->last_ack)
    fp->last_sent = fp->last_ack;
 
  if (fp->num_ack_after_retx > 0)
    {
      fp->num_ack_after_retx--;
      if (expire (fp, fp->last_ack))
 {
   long   old_sent = fp->last_sent;
   int old_cwn = fp->cur_win;
 
   fp->t_dupacks = fp->t_rexmtthresh;
   fp->last_sent = fp->last_ack;
   fp->cur_win = 1;
 
   (void) vegas_output (fp);
   if (fp->last_sent < old_sent)
     fp->last_sent = old_sent;
   fp->cur_win =  old_cwn;
   goto drop;
 }else
   fp->num_ack_after_retx = 0;
    }
 
  (void) vegas_output (fp);
 
drop: ;
}

static void vegas_output (fp)
  struct frame   *fp;
{
  PKT_PTR         pkt;
  int             win, nqueued;
 
  win = min (fp->cur_win, fp->max_win);
 
  /* Send as mutch data as we can */
 
  if (fp->line_busy)
    return;
  if (fp->last_sent + 1 <= fp->last_ack + win)
    {
      int startseq = (++fp->last_sent);
      /* ok to send now */
 
      hold ();
      pkt = (PKT_PTR) calloc (1,(unsigned) sizeof (PKT));
      release (1);
 
      fp->pkt       = pkt;
      pkt->size     = ftp_size;
      pkt->gen_time = runtime ();
      pkt->type     = FTP;
      pkt->gs       = 0;
      pkt->seq_no   = max (fp->last_sent, fp->last_ack+1);
 
      pkt->dest = assigned_sink[fp->nn];
      if (pkt->dest < ABSOLUTE_MAX_NODES)
 pkt->dest += ABSOLUTE_MAX_NODES * realnum;
 
      pkt->source = realnum * ABSOLUTE_MAX_NODES + fp->nn;
 
      fp->dropped[pkt->seq_no % MAX_WINDOW_SIZE] = 0;
 
      fp->line_busy = 1;
      if (fp->last_sent > fp->max_sent)
 {
   fp->max_sent = fp->last_sent;
   if (fp->rtt == 0) {
     fp->rtt     = 1;
     fp->t_rtseq = startseq;
     make_plot("rt_seq", fp->t_rtseq);
   }
 }else
   {
     ++fp->n_retx[fp->last_sent % MWS];
     ++fp->s_n_retx;
   }
 
      if (fp->t_timer[TCPT_REXMT] == 0 && fp->last_sent != fp->last_ack)
 {
   fp->t_timer[TCPT_REXMT] = fp->timeout;
   fp->back_off            = 0;
 }
      ++fp->transmits[fp->last_sent % MWS];
      fp->time_sent[fp->last_sent % MWS] = runtime();
      fp->v_cong_detect_last_sendtime = runtime();
      vegas_send (fp);
 
    }
}

/*
 * This routine is called every 500 ms, updates the timers
 * (retransmission timer), and the round trip times.
 * The fast timeout for processing delayed Acks is not used
 */

static void vegas_slowtimo (fp)
  struct frame   *fp;
{
 
  if (fp->t_timer[TCPT_REXMT] && --fp->t_timer[TCPT_REXMT] == 0)
    {
 
      /*
       * when a timer expire, this routine calls tcp_usrreq
       * which determines which timer has been timed out,
       * and calls the appropriate routine. In our case
       * the only even which may happen is the retransmission
       * timer which has expired. So tcp_timers is directly called
       */
 
      (void) vegas_timers (fp);
    }
 
  if (fp->rtt)
    fp->rtt++;
 
  /* schedule the next software timer  500 ms */
  {
 
    PKT_PTR         tick_pkt;
    float            delay;
    timev           now;
    now = runtime ();
 
    hold();
    tick_pkt = (PKT_PTR) malloc ((unsigned) sizeof (PKT));
    release(1);
    tick_pkt->source = tick_pkt->dest = fp->nn;
    tick_pkt->size = ftp_size;
    tick_pkt->type = TICK;
    delay = 0.5;
    set_timer (delay, tick_pkt);

  }
 
}

/* TCP timeout processing */

static void vegas_timers (fp)
  struct frame   *fp;
{
  register int rexmt;
 
  if (++fp->back_off > TCP_MAXRXTSHIFT){
    printf ("connection timed out \n");
    printf("Acknowledged %ld packets\n", fp->last_ack);
    exit();
  }
 
  rexmt = ((fp->srtt >> 2) + fp->mdev) >> 1;
  rexmt *= tcp_backoff[fp->back_off];
  TCPT_RANGESET(fp->timeout, rexmt, TCPTV_MIN, TCPTV_REXMTMAX);
  fp->t_timer[TCPT_REXMT] = fp->timeout;
  make_plot("timeout", fp->timeout);
  /*
   * If losing, let the lower level know and try for
   * a better route.  Also, if we backed off this far,
   * our srtt estimate is probably bogus.  Clobber it
   * so we'll take the next rtt measurement as our srtt;
   * move the current srtt into rttvar to keep the current
   * retransmit times until then.
   */
  if (fp->back_off >  MAX_RT_SHIFT / 4)
    {
      fp->mdev += fp->srtt;
      fp->srtt = 0;
    }
  fp->last_sent = fp->last_ack;
  /*
   * If timing a segment in this window, stop the timer.
   */
 
  fp->rtt = 0;
 
  /*
   * Close the congestion window down to one segment
   * (we'll open it by one segment for each ack we get).
   * Since we probably have a window's worth of unacked
   * data accumulated, this "slow start" keeps us from
   * dumping all that data as back-to-back packets (which
   * might overwhelm an intermediate gateway).
   *
   * There are two phases to the opening: Initially we
   * open by one mss on each ack.  This makes the window
   * size increase exponentially with time.  If the
   * window is larger than the path can handle, this
   * exponential growth results in dropped packet(s)
   * almost immediately.  To get more time between
   * drops but still "push" the network to take advantage
   * of improving conditions, we switch from exponential
   * to linear window opening at some threshhold size.
   * For a threshhold, we use half the current window
   * size, truncated to a multiple of the mss.
   *
   * (the minimum cwnd that will give us exponential
   * growth is 2 mss.  We don't allow the threshhold
   * to go below this.)
   */
  {
    int win = fp->cur_win / 2;
    if (win < 2)
      win = 2;
    fp->cur_win   = 1;
    fp->ssthresh  = win;
    make_plot ("ssthresh", fp->ssthresh);
    fp->t_dupacks = 0;
    fp->count     = 0;
 
  }
  (void) vegas_output(fp);
}

static vegas_send (fp)
  struct      frame   *fp;
{
 
  PKT_PTR         pkt;
  ident           dest;
  timev           now;
  ident           node_number;
  int             win;
 
  node_number = get_node_id ();
  now         = runtime ();
  pkt         = fp->pkt;
 
  make_plot("seq", pkt->seq_no);
  make_plot ("win", fp->cur_win);
 
  dest = pkt->dest;
  if ((dest / ABSOLUTE_MAX_NODES) != realnum) /* not local */
    dest = cc_router;
  else
    dest = dest % ABSOLUTE_MAX_NODES;
 
  pkt->timeout = (long) (200 * MILLION);
 
  if (route[node_number][dest] == 0)
    pr_error ("Routing error in Vegas_source");
  if (sendm (route[node_number][dest], 0, (char *) pkt) isnt 1)
    pr_error ("Vegas_safe_send: message undeliverable ", pkt);
 
}
 

int expire (fp, seq)
  struct   frame   *fp;
int     seq;
{
  int    len = 0;
  long   time;
  timev  now;
 
  now = runtime();
 
  time = 100000000*(make_float(now)- make_float(fp->time_sent[(seq + 1) % MWS]));
 
  if (time >= fp->v_timeout && seq + 1 <= fp->max_sent){
    len++;
    fp->Transmits = fp->transmits[(seq + 1) % MWS];
  }
  return len;
 
}

any_retransmission (fp, sequence)
  struct   frame   *fp;
int     sequence;
{
  int i;
  fp->Transmits = 0;
  for (i=1; i <= (fp->last_ack - sequence); i++)
    {
      fp->Transmits = max(fp->Transmits, fp->transmits[(sequence+ i)%MWS]);
      fp->transmits[(sequence+ i)%MWS] = 0;
    }
}