/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1990, 2001 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Lawrence Berkeley Laboratory, 
 * Berkeley, CA.  The name of the University may not be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */


//
// TCP-Fusion
//

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>

#include "packet.h"
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "address.h"
#include "random.h"

#include "tcp-fusion.h"


//#define MYDEBUG

static class FusionTcpClass : public TclClass {
public:
	FusionTcpClass() : TclClass("Agent/TCP/Fusion") {}
	TclObject* create(int, const char*const*) {
		return (new FusionTcpAgent());
	}
} class_fusion;

///// 
// WestwoodNRTcpAgent()
FusionTcpAgent::FusionTcpAgent() : //Sack1TcpAgent(),
   //SACK
   fastrecov_(FALSE), pipe_(-1), next_pkt_(0), firstpartial_(0),
  // these where originally in TcpAgent()

  unaccounted_(0),RTTmax(0),dwnd_(0),Rmax(0),
  fr_a_(0), min_rtt_estimate(5.0), myseqno_(1),last_ts_(0),last_echoed_ts_(0),last_seq_(0),
  rtt_estimate(0), lastackrx_(0.0), fr_alpha_(0.9), tau_(1.0), total_time_(0.0), 
  total_size_(0.0), fr_prev_(20.0), reno_cwnd(0),
  Dmin(0), Sfactor(0), current_BE_(0), current_RE_(0), last_BE_sample_(0), last_RE_sample_(0)  

{
	bind_bool("partial_ack_", &partial_ack_);
	scb_ = new ScoreBoardRQ();
	// Read defaults variables from ns-defaults.tcl

  	bind("unaccounted_", &unaccounted_);
  	bind("fr_a_", &fr_a_);
	bind("fr_amin_", &fr_amin_);
	bind("fr_amax_", &fr_amax_);
	bind("fr_prev_", &fr_prev_);
  	bind("min_rtt_estimate", &min_rtt_estimate);
        bind("rtt_estimate", &rtt_estimate);
	bind("fr_alpha_", &fr_alpha_);
	bind("tau_", &tau_);
	bind("west_type_",&west_type_);
	bind("qest_",&qest_);
	bind("total_time_",&total_time_);
	bind("total_size_",&total_size_);
	bind("interp_type_",&interp_type_);

	bind("last_ts_",&last_ts_);
	bind("last_echoed_ts_",&last_echoed_ts_);
	bind("last_seq_",&last_seq_);
	bind("last_cwnd_",&last_cwnd_);
	bind("current_ts_",&current_ts_);
	bind("current_echoed_ts_",&current_echoed_ts_);
     
        bind("reno_cwnd",&reno_cwnd); 
        bind("RTTmax",&RTTmax);  
        bind("Rmax",&Rmax);
        bind("dwnd_",&dwnd_);
	bind("current_BE_", &current_BE_);
	bind("current_RE_", &current_RE_);
	bind("last_BE_sample_", &last_BE_sample_);
    	bind("last_RE_sample_", &last_RE_sample_);
        bind("Dmin",&Dmin);
        bind("Sfactor",&Sfactor);
           

	// these where originally in NewRenoTcpAgent()
//	bind("newreno_changes_", &newreno_changes_);
//	bind("newreno_changes1_", &newreno_changes1_);
//	bind("exit_recovery_fix_", &exit_recovery_fix_);
//	bind("partial_window_deflation_", &partial_window_deflation_);
//	bind("openadd_", &openadd_);
	//printf("Westwood New Reno binding done!\n");
}

FusionTcpAgent::~FusionTcpAgent(){
	delete scb_;
}

void FusionTcpAgent::reset (){
	scb_->ClearScoreBoard();
	TcpAgent::reset ();
}

int FusionTcpAgent::is_sacked(hdr_tcp *tcph, int seqlo, int seqhi)
{
	int i, sleft, sright;
	for (i=0; i < tcph->sa_length(); i++) {
		sleft = tcph->sa_left(i);
		sright = tcph->sa_right(i);

		if ((sright > seqlo && sright <= seqhi) ||
		    (sleft >= seqlo && sleft < seqhi) ||
		    (sleft < seqlo && sright > seqhi)) {
			return TRUE;
		}
	}
	return FALSE;
}

///// 
// dupack_action()
void FusionTcpAgent::dupack_action()

{
	int recovered = (highest_ack_ > recover_);
        
        if (recovered || (!bug_fix_ && !ecn_)) {
                goto sack_action;
        }
        
        if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
                last_cwnd_action_ = CWND_ACTION_DUPACK;
                /*
                 * What if there is a DUPACK action followed closely by ECN
                 * followed closely by a DUPACK action?
                 * The optimal thing to do would be to remember all
                 * congestion actions from the most recent window
                 * of data.  Otherwise "bugfix" might not prevent
                 * all unnecessary Fast Retransmits.
                 */
                reset_rtx_timer(1,0);
		if (singledup_ && LimTransmitFix_){
			pipe_ = maxseq_ - highest_ack_ - 1;
		}
		else {
			pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
	        }
		
                fastrecov_ = TRUE;
                scb_->MarkRetran(highest_ack_ + 1);		
                output(last_ack_ + 1, TCP_REASON_DUPACK);
                
                return;
        }

        if (bug_fix_) {
            
                return;
        }

sack_action:

/*    
     if (ssthresh_ > cwnd_) {
	fr_a_+=0.25;
	if (fr_a_ > 4)
	  fr_a_=4;
      } else {
	fr_a_ = 1;
      }
  	ssthresh_ = (int)((current_bwe_/size_/8) * min_rtt_estimate);
      	if (cwnd_ > ssthresh_) {
      		cwnd_ = ssthresh_;
      	}
	*/

recover_ = maxseq_;
if (oldCode_) { 
   	pipe_ = int(cwnd_) - numdupacks(cwnd_);
} else if (singledup_ && LimTransmitFix_) { 
	pipe_ = maxseq_ - highest_ack_ - 1;
} else { 
	pipe_ = maxseq_ - highest_ack_ - numdupacks(cwnd_);
}
double fr_now = Scheduler::instance().clock();
double rtt_estimate = t_rtt_ * tcp_tick_;


if ((rtt_estimate < min_rtt_estimate)&&(rtt_estimate > 0)) {
	min_rtt_estimate = rtt_estimate;
	}

// RTTmax caluculation for ARENO and CTCP

RTTmax= (1. - 0.125) * RTTmax + (0.125 * rtt_estimate);
  
if (RTTmax < (1.1 * min_rtt_estimate)) {
	RTTmax = 1.1 * min_rtt_estimate;
	}

if(wnd_option_ == 0) {
	
	/* TCP-Fusion */

	double sstemp= (cwnd_ * (min_rtt_estimate / rtt_estimate));

	if (sstemp < (cwnd_/2.0)) sstemp = cwnd_/2.0;
	if (sstemp < 2) sstemp = 2;

	reno_cwnd = cwnd_/2.;
	ssthresh_ = (int) reno_cwnd;          
	cwnd_ = sstemp;

}


if(wnd_option_ == 1){

        /* TCP-Adaptive Reno */

	double cwnd_decrease;

	cwnd_decrease = (RTTmax - min_rtt_estimate) / ((RTTmax - min_rtt_estimate) +
			(rtt_estimate - min_rtt_estimate));

	double sstemp = cwnd_ * cwnd_decrease;
        
	if (sstemp < (cwnd_/2)) sstemp = (cwnd_ / 2);
        if (sstemp < 2) sstemp = 2;
        ssthresh_ = (int) sstemp;
        cwnd_ = ssthresh_;
        reno_cwnd = cwnd_;
}

if (wnd_option_ == 2) {

	/* Compound TCP */

	dwnd_ = cwnd_ / 2. - reno_cwnd / 2.;
        if (dwnd_ < 0) dwnd_ = 0;
        double sstemp = cwnd_/2.;
        if (sstemp < 2) sstemp = 2;
        ssthresh_ = (int) sstemp;

        reno_cwnd = reno_cwnd / 2.;
        cwnd_ = ssthresh_;
}

if (wnd_option_ == 3) {

	/* TCP-Adaptive Reno (modified ver.) */

	double cwnd_decrease;

	cwnd_decrease = (RTTmax - min_rtt_estimate) / ((RTTmax - min_rtt_estimate) +
			(rtt_estimate - min_rtt_estimate));

	if (cwnd_decrease < 0.5) cwnd_decrease = 0.5;
	double sstemp = cwnd_ * cwnd_decrease;
        
        if (sstemp < 2) sstemp = 2;
        ssthresh_ = (int) sstemp;
        cwnd_ = ssthresh_;
        reno_cwnd *= cwnd_decrease;

}	

	trace_event("TCPWNR_FAST_RETX");
        recover_ = maxseq_;
        last_cwnd_action_ = CWND_ACTION_DUPACK;
	fastrecov_ = TRUE;
	scb_->MarkRetran(highest_ack_+1);
        // The slowdown was already performed
        // slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
        reset_rtx_timer(1,0);
        output(last_ack_ + 1, TCP_REASON_DUPACK);
        return;

}



void FusionTcpAgent::partial_ack_action()
{
	if ( next_pkt_ < highest_ack_ + 1) {
		next_pkt_ = highest_ack_ + 1;
	}

	int i;
	for (i = 1; i<=2; i++) {
		int getNext = scb_->GetNextUnacked(next_pkt_);
		if ( getNext > next_pkt_) {
			next_pkt_ = getNext;
		}
		if (t_seqno_ < next_pkt_) { 
			t_seqno_ = next_pkt_;
		}
		output(next_pkt_, TCP_REASON_PARTIALACK);
		scb_->MarkRetran(next_pkt_);
		++next_pkt_;
	}
	return;
}

/////
// timeout()
void FusionTcpAgent::timeout(int tno)
{
       
	/* retransmit timer */
	int no_frto = fastrecov_;
	if (tno == TCP_TIMER_RTX) {
		dupacks_ = 0;
		fastrecov_ = FALSE;
		timeout_ = TRUE;
		if (highest_ack_ > last_ack_)
			last_ack_ = highest_ack_;

		recover_ = maxseq_;
		scb_->ClearScoreBoard();
	
		if (highest_ack_ == maxseq_ && !slow_start_restart_) {
			/*
			 * TCP option:
			 * If no outstanding data, then don't do anything.
			 */
			return;
		};
		recover_ = maxseq_;
		if (highest_ack_ == -1 && wnd_init_option_ == 2)
			/* 
			 * First packet dropped, so don't use larger
			 * initial windows. 
			 */
			wnd_init_option_ = 1;
		if (highest_ack_ == maxseq_ && restart_bugfix_)
		       /* 
			* if there is no outstanding data, don't cut 
			* down ssthresh_.
			*/
			slowdown(CLOSE_CWND_ONE);
		else if (highest_ack_ < recover_ &&
		  last_cwnd_action_ == CWND_ACTION_ECN) {
		       /*
			* if we are in recovery from a recent ECN,
			* don't cut down ssthresh_.
			*/
			slowdown(CLOSE_CWND_ONE);
		}
		else {
			++nrexmit_;
			slowdown(CLOSE_FASTER);
		}
				/* if there is no outstanding data, don't back off rtx timer */
		if (highest_ack_ == maxseq_ && restart_bugfix_) {
			reset_rtx_timer(0,0);
		}
		else {
			reset_rtx_timer(0,1);
		}
		last_cwnd_action_ = CWND_ACTION_TIMEOUT;
		send_much(0, TCP_REASON_TIMEOUT, maxburst_);

	}
	else {
		timeout_nonrtx(tno);
	}
	if (no_frto)
	       frto_ = 0;	
}

void FusionTcpAgent::send_much(int force, int reason, int maxburst)
{
	register int found, npacket = 0;
	int win = window();
	int xmit_seqno;

	found = 1;
	if (!force && delsnd_timer_.status() == TIMER_PENDING)
		return;
	/*
	 * as long as the pipe is open and there is app data to send...
	 */
	while (((!fastrecov_ && (t_seqno_ <= last_ack_ + win)) ||
			(fastrecov_ && (pipe_ < int(cwnd_)))) 
			&& t_seqno_ < curseq_ && found) {

		if (overhead_ == 0 || force) {
			found = 0;
			xmit_seqno = scb_->GetNextRetran ();

#ifdef DEBUGSACK1A
			printf("highest_ack: %d xmit_seqno: %d\n", 
			(int)highest_ack_, xmit_seqno);
#endif
			if (xmit_seqno == -1) { 
				if ((!fastrecov_ && t_seqno_<=highest_ack_+win)||
					(fastrecov_ && t_seqno_<=highest_ack_+int(wnd_))) {
					found = 1;
					xmit_seqno = t_seqno_++;
#ifdef DEBUGSACK1A
					printf("sending %d fastrecovery: %d win %d\n",
						xmit_seqno, fastrecov_, win);
#endif
				}
			} else if (recover_>0 && xmit_seqno<=highest_ack_+int(wnd_)) {
				found = 1;
				scb_->MarkRetran (xmit_seqno);
				win = window();
			}
			if (found) {
				output(xmit_seqno, reason);
				if (t_seqno_ <= xmit_seqno)
					t_seqno_ = xmit_seqno + 1;
				npacket++;
				pipe_++;
				if (QOption_)
					process_qoption_after_send () ;
	                        if (qs_approved_ == 1) {
	                                double delay = (double) t_rtt_ * tcp_tick_ / cwnd_;
	                                delsnd_timer_.resched(delay);
	                                return;
	                        }
			}
		} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
			/*
			 * Set a delayed send timeout.
			 * This is only for the simulator,to add some
			 * randomization if speficied.
			 */
			delsnd_timer_.resched(Random::uniform(overhead_));
			return;
		}
		if (maxburst && npacket == maxburst)
			break;
	} /* while */
}


///// 
// bwe_computation()
void FusionTcpAgent::bwe_computation(Packet *pkt) {
	
       	hdr_tcp *tcph = hdr_tcp::access(pkt);
	double fr_now = Scheduler::instance().clock();
	hdr_flags *fh = hdr_flags::access(pkt);
	
        	// last_ack_ indicates the ack no. of the ack received _before_
	// the current one 
	
	// START BWE COMPUTATION
    // Idea: cumulative ACKs acking more than 2 packets count for 1 packet
	//   since DUPACKs have already been accounted for
	int cumul_ack = tcph->seqno_ - last_ack_;
	int cumul_ack1 = cumul_ack; //used for queueing time estimation
	myseqno_ = tcph->seqno_;

	if (cumul_ack > 1) {

	  /* check if current ACK ACKs fewer or same number of segments than */
	  /* expected: if so, the missing ones were already accounted for by */
	  /* DUPACKs, and current ACK only counts as 1 */
	  if (unaccounted_ >= cumul_ack) {
	    unaccounted_-=cumul_ack;
	    cumul_ack=1;
	  } else
	  /* check if current ACK ACKs more segments than expected: if so,   */
	  /* part of them were already accounted for by DUPACKs; the rest    */

	  /* are cumulatively ACKed by present ACK. Make present ACK count   */
	  /* as the unacknowledged ACKs in excess*/
	  if (unaccounted_ < cumul_ack) {
	    cumul_ack-=unaccounted_;
	    unaccounted_=0;
	  }
	}

  /* if cumul_ack=0, the current ACK is clearly a DUPACK and should */
	/* count 1 */
	if (cumul_ack == 0) {
	  unaccounted_++;
	  cumul_ack=1;
	}

  /* safety check; if the previous steps are followed exactly,      */
	/* cumul_ack should not be >2 unless some strage events occur     */
	/* (e.g., an ACK is dropped on the way back and the following one */
	/* appears to ACK more than its due)                              */

	if (cumul_ack > 2) {
	  cumul_ack = 2;
	  }



	nackpack_ += cumul_ack;
	last_seq_ += cumul_ack;
	//qest_ = cwnd_ - (current_bwe_ * min_rtt_estimate) / (8.0 * (double)size_);

	current_ts_=tcph->ts();
	current_echoed_ts_=tcph->ts_echo();

	double rtt_estimate = t_rtt_ * tcp_tick_;
        
        if ((rtt_estimate < min_rtt_estimate)&&(rtt_estimate > 0)) {
	        min_rtt_estimate = rtt_estimate;
		qest_=0;
 		last_echoed_ts_=current_echoed_ts_;
 		last_ts_=current_ts_;
		}
         
		qest_ = qest_ + (current_ts_ - last_ts_) - (current_echoed_ts_ - last_echoed_ts_);
		last_echoed_ts_ = current_echoed_ts_;
 		last_ts_ = current_ts_;


	int acked_size = size_ * 8 * cumul_ack;
	double ack_interv = fr_now - lastackrx_;
	double sample_BE;
	double last_tmp_BE;
        double sample_RE;
	double last_tmp_RE;
	int idle_intervals;
       

	  // compute how many intervals of length tau_/2 went by since we
	  // received the last ACK. For each tau_/2 interval without ACK, feed
          // a zero-bandwidth sample to the filter.
	  double ack_interv_BE = ack_interv;
	  idle_intervals = (int)(ack_interv_BE / tau_*2.0);
	  //	  printf("idle_intervals = %d (%f,%f)=%f\n", idle_intervals, ack_interv, tau_,
	  //				ack_interv / tau_);
	  //		printf("idle_intervals = %d, ratio= %f\n",idle_intervals, ack_interv / tau_);
	  
	  ack_interv_BE -= tau_ /2.0 * idle_intervals;
	  
	  //if ( (ack_interv < 0.01) && (idle_intervals == 0) ){
	  //	printf("TCP-W error: (ack_interv < 0.01) && (idle_intervals == 0)\n");
	  //	printf("time=%lf, last_ack=%lf, ack_interv=%lf\n", fr_now, lastackrx_, ack_interv);
	  //	//exit(0);
	  //}	
	  
	  if ( (ack_interv_BE < 0.01) && (idle_intervals > 0) ) {
	  // ack_interv was a multiple of tau_/2 or the remainder is too small (less than 10ms), so 
	  // we consider tau_ / 2 as the last interval
	  	ack_interv_BE = tau_ / 2.0;
		idle_intervals -= 1; // we do not count the last tau_/2 interval
	  }
	  
	  sample_BE = acked_size / ack_interv_BE;
	  
	  if (idle_intervals > 0) { // feed the filter
		for (int i=0; i<idle_intervals; i++) {
			current_BE_ = current_BE_ * 3.0 / 5.0  + last_BE_sample_/5.0;
			last_BE_sample_ = 0.0;
		}
	  }
	  
	  last_tmp_BE = current_BE_; // we need it just for the printf...
	  current_BE_ = current_BE_ * (2.0*tau_-ack_interv_BE) /
		           (2.0*tau_+ack_interv_BE) +
		           ack_interv_BE*(sample_BE + last_BE_sample_)/(2.0*tau_+ack_interv_BE);

	  last_BE_sample_ = sample_BE;

	  if (current_BE_ < 0) {
			printf("TCP-W error: current_BE_ < 0\n");
			printf("time: %f, last_tmp_BE=%f\n", fr_now, last_tmp_BE);
			printf("current_BE_%f, ack_interv_BE=%f, sample_BE=%f, last_BE_sample_=%f\n",
					current_BE_, ack_interv_BE, sample_BE, last_BE_sample_);
			exit(0);
	  }
	 


	total_size_ = total_size_ + acked_size;
	total_time_ = total_time_ + ack_interv;

	if (((rtt_estimate  > 0)&&(total_time_ > rtt_estimate))) {

        sample_RE = total_size_ / total_time_;

	double m=(sample_RE - last_RE_sample_) / pow(total_time_,interp_type_);

	double sample_RE_new = sample_RE;
	double sample_RE_old = last_RE_sample_;
	double ack_delta = ack_interv;


	int Num_cicli;
	if (total_time_ < tau_/4.0) {
		Num_cicli = 0;

		ack_interv = total_time_;
		}
		else {
		Num_cicli = (int)(floor(((4.0*total_time_)/tau_)));
		ack_interv = total_time_-0.25*tau_*((double)(Num_cicli));
		}


		int i1;
  	for (i1=0;i1<(Num_cicli); i1++) 	{

		//if (m<0) {
			//sample_bwe=sample_bwe_old+m*pow((i1*tau_/4.0),interp_type_);
		//	}

		current_RE_ = current_RE_*(7.0/9.0)+(sample_RE+last_RE_sample_)/9.0;
		last_RE_sample_ = sample_RE;
		}

	       	if (ack_interv>0) {
		current_RE_ = current_RE_ * (2.0*tau_-ack_interv)/(2.0*tau_+ack_interv) +
		ack_interv*(sample_RE_new+last_RE_sample_)/(2.0*tau_+ack_interv);
		last_RE_sample_ = sample_RE_new;
		}
        
	total_time_ = 0.0;
	total_size_ = 0.0;
	}


double sstemp=(((current_RE_*(min_rtt_estimate))/((double)(size_*8.0))));



#ifdef MYDEBUG
	hdr_ip *iph = hdr_ip::access(pkt);  
  	char *src_portaddr = Address::instance().print_portaddr(iph->sport());
	printf("sc%s: ack. no. %d at time %f, bwe=%f, cwnd = %d, ssthresh_ = %d\n",
	      src_portaddr, tcph->seqno_, fr_now, current_BE_/1000000,
	      (int)cwnd_, (int)ssthresh_);
	printf("sc%s: now = %f, acked_size = %d, rxdiff = %f, last_ack_ = %d\n",
	         src_portaddr, fr_now, acked_size, (fr_now - lastackrx_), last_ack_);
	printf("sc%s: unaccounted_ = %d, fr_a_= %f, min_rtt_estimate = %f\n", 
			     src_portaddr, unaccounted_, fr_a_, min_rtt_estimate);
#endif
#ifdef MYDEBUG_RTT
	double f = t_rtt_ * tcp_tick_;
	printf("source %s: %f cwnd=%d	      bwe=%f	  rtt=%f\n", 
	      src_portaddr, fr_now, (int)cwnd_, current_BE_/1000000, f);     
#endif	
#ifdef MYREPORT	
	hdr_ip *iph = hdr_ip::access(pkt);  
	char *src_portaddr = Address::instance().print_portaddr(iph->src());
	printf("%s    %f      %d      %f      %d\n", 
	      src_portaddr, fr_now, (int)cwnd_, current_BE_/1000000,
	      (int)ssthresh_);        
#endif		

	lastackrx_ = fr_now;
}


/////
// recv()
void FusionTcpAgent::recv(Packet *pkt, Handler* h)
{
	// START BWE COMPUTATION
	bwe_computation(pkt);
	//double cwndapp,sstreshapp;
	//cwndapp=cwnd_;
	//sstreshapp=ssthresh_;

	hdr_tcp *tcph = hdr_tcp::access(pkt);
	int valid_ack = 0;

        if (qs_approved_ == 1 && tcph->seqno() > last_ack_)
		endQuickStart();
        if (qs_requested_ == 1)
                processQuickStart(pkt);
#ifdef notdef
	if (pkt->type_ != PT_ACK) {
		Tcl::instance().evalf("%s error \"received non-ack\"",
				      name());
		Packet::free(pkt);
		return;
	}
#endif
        /* W.N.: check if this is from a previous incarnation */
        if (tcph->ts() < lastreset_) {
                // Remove packet and do nothing
                Packet::free(pkt);
                return;
        }
	++nackpack_;
	int ecnecho = hdr_flags::access(pkt)->ecnecho();
	if (ecnecho && ecn_)
		ecn(tcph->seqno());
        if (tcph->seqno() >= last_ack_)
		// Check if ACK is valid.  Suggestion by Mark Allman.
		valid_ack = 1;
	/*
	 * If DSACK is being used, check for DSACK blocks here.
	 * Possibilities:  Check for unnecessary Fast Retransmits.
	 */
	if (!fastrecov_) {
		/* normal... not fast recovery */
		if ((int)tcph->seqno() > last_ack_) {
			/*
			 * regular ACK not in fast recovery... normal
			 */
			firstpartial_ = 0;
			recv_newack_helper(pkt);
			timeout_ = FALSE;
			scb_->ClearScoreBoard();
			if (last_ack_ == 0 && delay_growth_) {
				cwnd_ = initial_window();
			}
		} else if ((int)tcph->seqno() < last_ack_) {
			/*NOTHING*/
		} else if (timeout_ == FALSE) {
			if (tcph->seqno() != last_ack_) {
				fprintf(stderr, "pkt seq %d should be %d\n" ,
					tcph->seqno(), last_ack_);
				abort();
			}
			scb_->UpdateScoreBoard (highest_ack_, tcph);
			/*
		 	 * Check for a duplicate ACK.
			 * Check that the SACK block actually
			 *  acknowledges new data.
 			 */
 		        if(scb_->CheckUpdate()) {
 			 	if (++dupacks_ == numdupacks(cwnd_)) {
 					/*
 					 * Assume we dropped just one packet.
 					 * Retransmit last ack + 1
 					 * and try to resume the sequence.
 					 */
 				   	dupack_action();
 				} else if (dupacks_ < numdupacks(cwnd_) && singledup_ ) {
					send_one();
 				}
			}
			if (sfrto_enabled_ && frto_ == 2) {
				/*
				 * SACK-based F-RTO: If SACK only acknowledges
				 * data that was transmitted before RTO and
				 * not acknowledged earlier,
				 * the timeout was spurious.
				 */
				if (scb_->IsChanged() &&
				    !is_sacked(tcph, recover_, maxseq_)) {
					spurious_timeout();
				} else {
					t_seqno_ = highest_ack_ + 1;
					cwnd_ = frto_;
					frto_ = 0;
					dupacks_ = 0;
				}
			}
		}
        	if (valid_ack || aggressive_maxburst_)
			if (dupacks_ == 0)
				send_much(FALSE, 0, maxburst_);
	} else {
		/* we are in fast recovery */
		--pipe_;
		if ((int)tcph->seqno() >= recover_) {
			/* ACK indicates fast recovery is over */
			recover_ = 0;
			fastrecov_ = FALSE;
			newack(pkt);
			/* if the connection is done, call finish() */
			if ((highest_ack_ >= curseq_-1) && !closed_) {
				closed_ = 1;
				finish();
			}
			timeout_ = FALSE;
			scb_->ClearScoreBoard();

			/* New window: W/2 - K or W/2? */
		} else if ((int)tcph->seqno() > highest_ack_) {
			/* Not out of fast recovery yet.
			 * Update highest_ack_, but not last_ack_. */
			highest_ack_ = (int)tcph->seqno();
			scb_->UpdateScoreBoard (highest_ack_, tcph);
			if (partial_ack_) {
			  /* partial_ack_ is needed to guarantee that */
			  /*  a new packet is sent in response to a   */
			  /*  partial ack.                            */
				partial_ack_action();
				++pipe_;
				if (firstpartial_ == 0) {
					newtimer(pkt);
					t_backoff_ = 1;
					firstpartial_ = 1;
				}
			} else {
				--pipe_;
				newtimer(pkt);
				t_backoff_ = 1;
 			 /* If this partial ACK is from a retransmitted pkt,*/
 			 /* then we decrement pipe_ again, so that we never */
 			 /* do worse than slow-start.  If this partial ACK  */
 			 /* was instead from the original packet, reordered,*/
 			 /* then this might be too aggressive. */
			}
		} else if (timeout_ == FALSE) {
			/* got another dup ack */
			scb_->UpdateScoreBoard (highest_ack_, tcph);
 		        if(scb_->CheckUpdate()) {
 				if (dupacks_ > 0)
 			        	dupacks_++;
 			}
		}
        	if (valid_ack || aggressive_maxburst_)
			send_much(FALSE, 0, maxburst_);
	}

	Packet::free(pkt);
#ifdef notyet
//	if (trace_)
//		plot();
#endif
}



/////////////////// Added by MV
// these where originally in TcpAgent()

/////
// slowdown()
void
FusionTcpAgent::slowdown(int how)
{
	double win, halfwin, decreasewin;
	int slowstart = 0;
	double fr_now = Scheduler::instance().clock();
	// we are in slowstart for sure if cwnd < ssthresh
	if (cwnd_ < ssthresh_)
		slowstart = 1;
	// we are in slowstart - need to trace this event
	trace_event("SLOW_START");

        if (precision_reduce_) {
		halfwin = windowd() / 2;
                if (wnd_option_ == 6) {
                        /* binomial controls */
                        decreasewin = windowd() - (1.0-decrease_num_)*pow(windowd(),l_parameter_);
                } else
	 		decreasewin = decrease_num_ * windowd();
		win = windowd();
	} else  {
		int temp;
		temp = (int)(window() / 2);
		halfwin = (double) temp;
                if (wnd_option_ == 6) {
                        /* binomial controls */
                        temp = (int)(window() - (1.0-decrease_num_)*pow(window(),l_parameter_));
                } else
	 		temp = (int)(decrease_num_ * window());
		decreasewin = (double) temp;
		win = (double) window();
	}
	if (how & CLOSE_SSTHRESH_HALF)
		// For the first decrease, decrease by half
		// even for non-standard values of decrease_num_.
		if (first_decrease_ == 1 || slowstart ||
			last_cwnd_action_ == CWND_ACTION_TIMEOUT) {
			// Do we really want halfwin instead of decreasewin
			// after a timeout?
			ssthresh_ = (int) halfwin;
		} else {
			ssthresh_ = (int) decreasewin;
		}
        else if (how & THREE_QUARTER_SSTHRESH)
		if (ssthresh_ < 3*cwnd_/4)
			ssthresh_  = (int)(3*cwnd_/4);
	if (how & CLOSE_CWND_HALF)
		// For the first decrease, decrease by half
		// even for non-standard values of decrease_num_.
		if (first_decrease_ == 1 || slowstart || decrease_num_ == 0.5) {
			cwnd_ = halfwin;
		} else cwnd_ = decreasewin;
        else if (how & CWND_HALF_WITH_MIN) {
		// We have not thought about how non-standard TCPs, with
		// non-standard values of decrease_num_, should respond
		// after quiescent periods.
                cwnd_ = decreasewin;
                if (cwnd_ < 1)
                        cwnd_ = 1;
	}
	///
	else if (how & CLOSE_FASTER) {
    	// TCP Westwood
	// this might be critical what with the coarseness of the timer;
    	// keep in mind that TCP computes the timeout as
    	//              (#of ticks) * (tick_duration)
    	// We need to do away with the coarseness...

	double rtt_estimate = t_rtt_ * tcp_tick_;
       
	if ((rtt_estimate <= min_rtt_estimate)&&(rtt_estimate > 0)) {
		min_rtt_estimate = rtt_estimate;
	}
	double sstemp = cwnd_/2.0; 
	
	if (sstemp < 2) sstemp = 2;
	ssthresh_ = (int)(sstemp);
	cwnd_ = 2;
	reno_cwnd=2.;               
	dwnd_ =2.;

	}

	else if (how & CLOSE_CWND_RESTART)
		cwnd_ = int(wnd_restart_);
	else if (how & CLOSE_CWND_INIT)
		cwnd_ = int(wnd_init_);
	else if (how & CLOSE_CWND_ONE)
		cwnd_ = 1;
	else if (how & CLOSE_CWND_HALF_WAY) {
		// cwnd_ = win - (win - W_used)/2 ;
		cwnd_ = W_used + decrease_num_ * (win - W_used);
                if (cwnd_ < 1)
                        cwnd_ = 1;
	}
	if (ssthresh_ < 2)
		ssthresh_ = 2;
	if (how & (CLOSE_CWND_HALF|CLOSE_CWND_RESTART|CLOSE_CWND_INIT|CLOSE_CWND_ONE))
		cong_action_ = TRUE;

	fcnt_ = count_ = 0;
	if (first_decrease_ == 1)
		first_decrease_ = 0;
}

/////
// newack()
/*
 * Process a packet that acks previously unacknowleged data.
 */


void FusionTcpAgent::newack(Packet* pkt)
{
	hdr_tcp *tcph = hdr_tcp::access(pkt);
	myseqno_ = tcph->seqno_;
	//call parent newack

	TcpAgent::newack(pkt);
}




void FusionTcpAgent::opencwnd()
{

        double rtt_estimate = t_rtt_*tcp_tick_;
           
	if (( rtt_estimate<min_rtt_estimate)&&(rtt_estimate>0)){
		min_rtt_estimate=rtt_estimate;
	}
       
	if(Rmax<rtt_estimate){
		Rmax = rtt_estimate;
	}
        
	double buffer = cwnd_ * (rtt_estimate - min_rtt_estimate)/
			rtt_estimate;
	
        double ctcp_g= 41.*(1.-(min_rtt_estimate/Rmax));
	if(ctcp_g<3) ctcp_g = 3;
       
	double increment;
	if (cwnd_ < ssthresh_) {
		/* slow-start (exponential) */
		cwnd_ += 1;
		if (wnd_option_ == 2) {
			reno_cwnd = cwnd_;
		}
    

	} else {
		/* linear */
		double f;
		switch (wnd_option_) {

		case 0:
                       
                     /* TCP-Fusion */  

			double a_w;
			double Winc;
			Winc = (current_BE_ / 1000000) / Sfactor;
			a_w = current_RE_ * Dmin / 12000000.;
                      
			if (a_w < 1) a_w = 1.; 
			if (Winc < 1) Winc = 1.;
			if (buffer < (a_w)) {
				increment = Winc/cwnd_; 
                    	} else if (buffer > (3*a_w)){
                           	increment = ((-cwnd_ * (rtt_estimate - min_rtt_estimate) /
					    rtt_estimate) + a_w) / cwnd_;
                     	} else {
                          	increment = 0; 
                       	}
                    
                 	cwnd_ += increment;
                     	reno_cwnd += 1. / cwnd_;
               
                      	if (cwnd_ < reno_cwnd) {
                          	cwnd_ = reno_cwnd;
                    
                      	}

            	break;


                case 1:

                        /* TCP-Adaptive Reno */
                  
                      	double Wmax;
                    	double a, b, C, d;

                        if (RTTmax < min_rtt_estimate) {
				RTTmax = (min_rtt_estimate + 0.001);
			}

			Wmax = current_BE_ / 10000000.;
                        if (Wmax < 1) Wmax = 1.;
                    	C = (rtt_estimate - min_rtt_estimate) / (RTTmax - min_rtt_estimate);
                       	a = 2. * Wmax * (1. / 10. - (1. / 10. + 1.) / exp(10.));
                       	b = 1. - 2. * Wmax * (1. / 10. - (1. / 10. + 0.5) / exp(10.));
                       	d = a * C + b;
                        
                      	if (Wmax < 1) {
                        	Wmax = 1.;
                        }
                     	if (d > 1) {d = 1.;}
                      	increment = (Wmax / exp(10. * C) + d) / cwnd_;
                      
                     	cwnd_ += increment;
                     	reno_cwnd += 1. / cwnd_;
                       	if (cwnd_ < reno_cwnd) cwnd_= reno_cwnd;

            	break;

                  
		case 2:

                        /* Compound TCP */

                    	if (buffer < ctcp_g) {
                        	increment = 0.125 * (pow(cwnd_,0.8)) - 1.;
                       	} else {
                        	increment = -buffer;
                       	}
                        
			dwnd_ += increment / cwnd_;
                       	if (dwnd_ <= 0) dwnd_ = 0;
                       	reno_cwnd += 1. / cwnd_;
                        
			cwnd_ = dwnd_ + reno_cwnd;
                        
                        break;

		case 3:

                        /* TCP-Adaptive Reno (modified ver.) */
                  
                    	double alpha, beta, Clevel;

                        if (RTTmax < min_rtt_estimate) {
				RTTmax = (min_rtt_estimate + 0.001);
			}

			alpha = 50;
			beta = 0.1;
                    	Clevel = (rtt_estimate - min_rtt_estimate) / (RTTmax - min_rtt_estimate);
                        
                      	increment = (alpha * current_BE_ / current_RE_ * rtt_estimate * exp(Clevel)) -
				    (beta * cwnd_ * Clevel);
                      
                     	cwnd_ += increment / cwnd_;
                     	reno_cwnd += 1. / cwnd_;
                       	if (cwnd_ < reno_cwnd) cwnd_= reno_cwnd;

            	break;
               
		default:
#ifdef notdef
			/*XXX*/
			error("illegal window option %d", wnd_option_);
#endif
			abort();
                 
		}
	}
	// if maxcwnd_ is set (nonzero), make it the cwnd limit
	if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
		cwnd_ = maxcwnd_;

	return;
}




void FusionTcpAgent::recv_newack_helper(Packet *pkt) {
	//hdr_tcp *tcph = hdr_tcp::access(pkt);
	newack(pkt);
        if (qs_window_ && highest_ack_ >= qs_window_) {
                // All segments in the QS window have been acknowledged.
                // We can exit the Quick-Start phase.
                qs_window_ = 0;
        }
	if (!ect_ || !hdr_flags::access(pkt)->ecnecho() ||
		(old_ecn_ && ecn_burst_)) {
	//	 If "old_ecn", this is not the first ACK carrying ECN-Echo
	//	 * after a period of ACKs without ECN-Echo.
	//	 * Therefore, open the congestion window. 
	//	 if control option is set, and the sender is not
	//		 window limited, then do not increase the window size 
		
		if (!control_increase_ || 
		   (control_increase_ && (network_limited() == 1))) 
	      		opencwnd();
	}
	if (ect_) {
		if (!hdr_flags::access(pkt)->ecnecho())
			ecn_backoff_ = 0;
		if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho())
			ecn_burst_ = TRUE;
		else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho())
			ecn_burst_ = FALSE;
	}
	if (!ect_ && hdr_flags::access(pkt)->ecnecho() &&
		!hdr_flags::access(pkt)->cong_action())
		ect_ = 1;
	// if the connection is done, call finish() 
	if ((highest_ack_ >= curseq_-1) && !closed_) {
		closed_ = 1;
		finish();
	}
	if (QOption_ && curseq_ == highest_ack_ +1) {
		cancel_rtx_timer();
	}
	if (frto_ == 1) {
	//	
	//	 * New ack after RTO. If F-RTO is enabled, try to transmit new
	//	 * previously unsent segments.
	//	 * If there are no new data or receiver window limits the
	//	 * transmission, revert to traditional recovery.
	//	 
		if (recover_ + 1 >= highest_ack_ + wnd_ ||
		    recover_ + 1 >= curseq_) {
			frto_ = 0;
 		} else if (highest_ack_ == recover_) {
 	//		
 	//		 * F-RTO step 2a) RTO retransmission fixes whole
	//		 * window => cancel F-RTO
 	//		 
 			frto_ = 0;
		} else {
			t_seqno_ = recover_ + 1;
			frto_ = 2;
		}
	} else if (frto_ == 2) {
	//	
	//	 * Second new ack after RTO. If F-RTO is enabled, RTO can be
	//	 * declared spurious
	//	 
		spurious_timeout();
	}
}

    

///// 
// delay_bind_dispatch()
//Westwood binds
int
FusionTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{

	if (delay_bind(varName, localName, "lastackno_", &lastackno_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "lastackrx_", &lastackrx_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "fr_alpha_", &fr_alpha_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "tau_", &tau_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "mss_", &mss_, tracer)) return TCL_OK;

	if (delay_bind(varName, localName, "unaccounted_", &unaccounted_, tracer)) return TCL_OK;
        if (delay_bind(varName, localName, "fr_a_", &fr_a_, tracer)) return TCL_OK;
        if (delay_bind(varName, localName, "min_rtt_estimate", &min_rtt_estimate, tracer)) return TCL_OK;
  	if (delay_bind(varName, localName, "myseqno_", &myseqno_, tracer)) return TCL_OK;
        if (delay_bind(varName, localName, "rtt_estimate", &rtt_estimate, tracer )) return TCL_OK;        

        if (delay_bind(varName, localName, "reno_cwnd",&reno_cwnd, tracer )) return TCL_OK;
        if (delay_bind(varName, localName, "RTTmax",&RTTmax, tracer )) return TCL_OK;
        if (delay_bind(varName, localName, "dwnd_",&dwnd_, tracer )) return TCL_OK;
        if (delay_bind(varName, localName, "Rmax",&Rmax, tracer )) return TCL_OK;
	if (delay_bind(varName, localName, "current_BE_", &current_BE_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "current_RE_", &current_RE_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "last_BE_sample_", &last_BE_sample_, tracer)) return TCL_OK;
	if (delay_bind(varName, localName, "last_RE_sample_", &last_RE_sample_, tracer)) return TCL_OK;

        if (delay_bind(varName, localName, "Dmin",&Dmin, tracer )) return TCL_OK;
        if (delay_bind(varName, localName, "Sfactor",&Sfactor, tracer )) return TCL_OK;
        // these where originally in NewRenoTcpAgent()
//	if (delay_bind(varName, localName, "newreno_changes_", &newreno_changes_, tracer)) return TCL_OK;
//	if (delay_bind(varName, localName, "newreno_changes1_", &newreno_changes1_, tracer)) return TCL_OK;
//	if (delay_bind(varName, localName, "exit_recovery_fix_", &exit_recovery_fix_, tracer)) return TCL_OK;
//	if (delay_bind(varName, localName, "partial_window_deflation_", &partial_window_deflation_, tracer)) return TCL_OK;

        return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
}

/* tickoff is the time since the clock last ticked when
 *  the packet we are using to compute the RTT was sent
 */

/* t_rtt_ is the number of ticks that have occurred so far,
 * starting from the tick BEFORE the packet was sent
 */

