
/* TCP-HyblaS.cc v7.1 (22 April 2004)
 *
 * THIS SOFTWRE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This software is developed by Rosario Firrincieli
 * ARCES-University of Bologna, Italy.
 * rfirrincieli@arces.unibo.it
 *
 * TCP-HyblaS is builded over the SACK TCP Agent; comments in the original SACK script (tcp-sack1.cc) are left 
 * unaltered. Detailed description of all the added non-standard procedures
 * can be found in the authors' publications. 
 */


 /* The modification of Tom Kelly 8/02 were accepted: Made scoreboard a general interface to allow
  *                  easy swapping of scoreboard algorithms.  
  */


#include "tcp-hyblaS.h"
#define TRUE    1
#define FALSE   0
#define RECOVER_DUPACK  1
#define RECOVER_TIMEOUT 2
#define RECOVER_QUENCH  3

#define RTT_REF_ 0.025 /* RTT_REF_ is the reference Round Trip Time in seconds;
			* Hybla will try to transmit at the same speed of a reference connection 
			* whose RTT is equal to RTT_REF_
			*/
#ifndef MAX
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#endif



static class HyblaSTcpClass : public TclClass {
public:
	HyblaSTcpClass() : TclClass("Agent/TCP/HyblaS") {}
	TclObject* create(int, const char*const*) {
		return (new HyblaSTcpAgent());
	}
} class_hyblaS;


HyblaSTcpAgent::HyblaSTcpAgent(): newreno_changes_(1), fastrecov_(FALSE),
  pipe_(-1), newreno_changes1_(0), acked_(0), firstpartial_(0),
  partial_window_deflation_(0), exit_recovery_fix_(1), SS_state_(1),  SS_first_(1),
  ACK_size_(40), TCP_size_(1024), pkt_count_(0), k_thresh_(0.5), PKT_SPREADING_(2), bdp_(0),
  k_cwnd_(10.0), bdp_fix_(1)

{
	bind("newreno_changes_", &newreno_changes_);
	bind("newreno_changes1_", &newreno_changes1_);
	bind("PKT_SPREADING_", &PKT_SPREADING_);
	bind("exit_recovery_fix_", &exit_recovery_fix_);
	bind("partial_window_deflation_", &partial_window_deflation_);
	bind("bdp_fix_",&bdp_fix_);
	scb_ = new ScoreBoard(new ScoreBoardNode[SBSIZE],SBSIZE);
}

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


void HyblaSTcpAgent::reset ()		/* unaltered with respect to the original version */
{
	scb_->ClearScoreBoard();
	TcpAgent::reset ();
}


void HyblaSTcpAgent::partialnewack(Packet* pkt)		/* minor modifications with respect to the original version */
{
	hdr_tcp *tcph = hdr_tcp::access(pkt);
	if (partial_window_deflation_) {
		// Do partial window deflation before resetting last_ack_
		unsigned int deflate = 0; // Should initialize it?? - haoboy
		if (tcph->seqno() > last_ack_) // assertion
			deflate = tcph->seqno() - last_ack_;
		else
		  	//printf("False call to partialnewack:  deflate %u \
last_ack_ %d\n", deflate, last_ack_);
		if (dupwnd_ > deflate)
			dupwnd_ -= (deflate - 1);
		else {
			cwnd_ -= (deflate - dupwnd_);
			// Leave dupwnd_ > 0 to flag "fast recovery" phase
			dupwnd_ = 1;
		}
	}
	last_ack_ = tcph->seqno();
	highest_ack_ = last_ack_;
	if (t_seqno_ < last_ack_ + 1)
		t_seqno_ = last_ack_ + 1;
	if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
		rtt_active_ = 0;
		t_backoff_ = 1;
	}
}

void HyblaSTcpAgent::partialnewack_helper(Packet* pkt)		/* minor modifications with respect to the original version */	
{
	if (!newreno_changes1_ || firstpartial_ == 0) {
		firstpartial_ = 1;
		/* For newreno_changes1_,
		 * only reset the retransmit timer for the first
		 * partial ACK, so that, in the worst case, we
		 * don't have to wait for one packet retransmitted
		 * per RTT.
		 */
		newtimer(pkt);
	}
	partialnewack(pkt);
	cout << "tempo " << Scheduler::instance().clock() <<  " last_ack_ " << last_ack_ << endl; //pippo;
	output(last_ack_ + 1, 0);
}

int
HyblaSTcpAgent::allow_fast_retransmit(int /* last_cwnd_action_*/)
{
	return 0;
}

void
HyblaSTcpAgent::dupack_action()			/* minor modifications with respect to the original version */
{
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);
		/*
		 * There are three possibilities:
		 * (1) pipe_ = int(cwnd_) - numdupacks_;
		 * (2) pipe_ = window() - numdupacks_;
		 * (3) pipe_ = maxseq_ - highest_ack_ - numdupacks_;
		 * equation (2) takes into account the receiver's
		 * advertised window, and equation (3) takes into
		 * account a data-limited sender.
		 */
		pipe_ = maxseq_ - highest_ack_ - numdupacks_;
		//pipe_ = int(cwnd_) - numdupacks_;
		fastrecov_ = TRUE;
		scb_->MarkRetran(highest_ack_+1);
		output(last_ack_ + 1, TCP_REASON_DUPACK);
		return;
	}

	if (bug_fix_) {
		/*
		 * The line below, for "bug_fix_" true, avoids
		 * problems with multiple fast retransmits in one
		 * window of data.
		 */
		return;
	}

sack_action:
	// we are now going into fast_recovery and will trace that event
	// printf("%f Fast_Recovery_starts_with_cwnd %i\n",Scheduler::instance().clock(), int(cwnd_));
	
	trace_event("FAST_RECOVERY");
	SS_state_=0;   			 /* used in the packet spreading tecnique */
	
	/* Start of monitoring of the first SS phase, skipped if SS_first_=1 */
	 
	  if (SS_first_ == 0)
	  {
	  	cout << "Connection=" << fid_ << " lenght first SS=" << Scheduler::instance().clock() - firstsent_ << " cwnd=" << cwnd_ << " ssthresh=" << ssthresh_ << "\n";
	 	SS_first_=1;
	  }
	 
	 /* End of monitoring of the first SS phase */
	
	recover_ = maxseq_;
	last_cwnd_action_ = CWND_ACTION_DUPACK;
	if (oldCode_) {
	 	pipe_ = int(cwnd_) - numdupacks_;
	} else {
                pipe_ = maxseq_ - highest_ack_ - numdupacks_;
	}
	slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
	reset_rtx_timer(1,0);
	fastrecov_ = TRUE;
	scb_->MarkRetran(highest_ack_+1);
	output(last_ack_ + 1, TCP_REASON_DUPACK);	// from top
	/*
	 * If dynamically adjusting numdupacks_, record information
	 *  at this point.
	 */
	return;
}


void HyblaSTcpAgent::recv(Packet *pkt, Handler*)		/* major modifications with respect to the original version */
{
	hdr_tcp *tcph = hdr_tcp::access(pkt);
	
	pkt_count_ = 0; 					/* used only if Progressive packet spreading tecnique is enabled */

	/* Use first packet to calculate the RTT  --contributed by Allman */

	if (++acked_ == 1) {
		basertt_ = Scheduler::instance().clock() - firstsent_;
		
		/* the setting of the minimum value of rho equal to 1, guarantees
		 * the standard behaviour in the case of RTT < RTT_REF_
		 */
		
		rho_ = MAX(1,double(basertt_ / RTT_REF_));
		
		/* set maxrto = minrto_ in order to prevent timeout backoff algorithm divergence
		 * due to lossy channel. Note that this constraint can be relaxed if
		 * timestamps are used
		 */
		
		maxrto_ = 1;
		
		/* now the congestion window parameters could be setted to the right values */
		if (newreno_changes_ != 0)
			wnd_init_ = MAX(2,(int)floor(rho_)); 	/* if wnd_init_=1 (RTT=RTT_REF_) the bdp estimation could not be done. Thus a minimum value of
								 * 2 packets is set in the case of the bdp estimation is active
								 */
		else
			wnd_init_ = (int)floor(rho_);  	   	/* following the Hybla algorithm */

		//wnd_ = (int)(floor(rho_) * 64);  	
		wnd_=65535;
		//maxcwnd_ = (int)(floor(rho_) * 64);		/* set the advertised window and the maxcwnd to a value high enough;
		maxcwnd_=65535;											// if the ssthresh estimation is active they will be properly reset 

		if (newreno_changes_ != 0)			
			ssthresh_ = (int)floor(rho_);		/* a conservative value for ssthresh is set until the bdp estimation is done */
		else
			ssthresh_ = maxcwnd_;		        /* if the ssthresh estimation is disabled the ssthresh is set to the maxcwnd */
		slow_start_restart_ = (int)floor(rho_); 	/* cwnd value after the time out */
		

	}
	
	/* bandwidth delay product (bdp) estimation is performed by using ACKs number 2 and 3 
	 * (slightly different from the one of Allman). 
	 * then ssthresh is set to a value proportional to the esimated bdp;
	 * note that if a packet spreading technique is enabled the first two packet 
	 * must be sent together in order to performe the bdp estimation
	 */
	
	else if (acked_ == 2)
		ack2_ = Scheduler::instance().clock();
	else if (acked_ == 3) {
		ack3_ = Scheduler::instance().clock();
		
		/* estimate the Bandwidth Delay Product (packets) and calculate the new ssthresh (packets) */
		if (newreno_changes_ != 0) {
			bdp_ = basertt_ * (1 / (ack3_ - ack2_));
			maxcwnd_ = MAX(maxcwnd_, (int)floor(k_cwnd_ * bdp_));	/* to prevent sender limitation due to a bad dbp estimation */
			wnd_ = MAX(wnd_, (int)floor(k_cwnd_ * bdp_));		/* to prevent sender limitation due to a bad dbp estimation */
			new_ssthresh_ = (int)floor(k_thresh_ * bdp_);		
			ssthresh_ = MIN(new_ssthresh_,maxcwnd_);		/* ssthresh is now estimated */

			/* Print out some characteristics of the connection */
			cout << "Connection " << fid_ << " ,BDP " << (int)bdp_ << "\n";
		}
	
		/* Print out some characteristics of the connection */

		switch (PKT_SPREADING_) {
			case 0:
				cout << "Connection " << fid_ << " ,Packet spreading disabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			case 1:
				cout << "Connection " << fid_ << " ,Uniform packet spreading enabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			case 2:
				cout << "Connection " << fid_ << " ,Progressive packet spreading enabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			case 3:
				cout << "Connection " << fid_ << " ,Empirical packet spreading enabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			case 4:
				cout << "Connection " << fid_ << " ,Modified Uniform packet spreading enabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			case 5:
				cout << "Connection " << fid_ << " ,FTCP packet spreading enabled " << " ,RTT measured " << basertt_;
				cout << " ,rho " << rho_ << "\n";
				break;
			default: 
				cout << "	ERROR: PKT SPREADING OPTION IS WRONG!" << "\n";
				exit(1);
		}
		
	}
#ifdef notdef
	if (pkt->type_ != PT_ACK) {
		Tcl::instance().evalf("%s error \"received non-ack\"",
				      name());
		Packet::free(pkt);
		return;
	}
#endif
	++nackpack_;
	int ecnecho = hdr_flags::access(pkt)->ecnecho();
	if (ecnecho && ecn_)
		ecn(tcph->seqno());
	/*
	 * 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
			 */
			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_) {
 					/*
 					 * Assume we dropped just one packet.
 					 * Retransmit last ack + 1
 					 * and try to resume the sequence.
 					 */
 				   	dupack_action();
 				} else if (dupacks_ < numdupacks_ && singledup_ ) {
 				         send_one();
 				}
			}
		}
		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 */
			//printf("%f Fast_Recovery_ends\n",Scheduler::instance().clock());
			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_) {
			// Partial ACK! 
			/* Not out of fast recovery yet.
			 * Update highest_ack_, but not last_ack_. */
			--pipe_;
			/* 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. */
			highest_ack_ = (int)tcph->seqno();
			scb_->UpdateScoreBoard (highest_ack_, tcph);
			t_backoff_ = 1;
			newtimer(pkt);
		} else if (timeout_ == FALSE) {
			/* got another dup ack */
			scb_->UpdateScoreBoard (highest_ack_, tcph);
 		        if(scb_->CheckUpdate()) 
				{
 				if (dupacks_ > 0)
 			        	dupacks_++;
 				}
		}
		send_much(FALSE, 0, maxburst_);
	}

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

}



void HyblaSTcpAgent::recv_newack_helper(Packet *pkt) {		/* minor modifications with respect to the original version */	
	//hdr_tcp *tcph = hdr_tcp::access(pkt);
	newack(pkt);
	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)))
	      		new_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();
	}
}


void HyblaSTcpAgent::new_opencwnd()		/* this function implement the Hybla congestion window alghorithm! */		
{
	/* Until the bandwidth delay product is estimated, the standard cwnd increased algorithm is followed*/
	if (last_ack_ == 0) 
		return; 
	
	if (bdp_== 0 && newreno_changes_ != 0)
	{	
		if (cwnd_ < ssthresh_) { /* we are in the Slow-Start phase */
			SS_state_=1;
			cwnd_ = MIN(cwnd_ + 1, double(ssthresh_));
		
			/* if the spreading tecnique is enabled, the ssthresh must be considered
			* as the starting point for the CA phase (SS_state_=0) in order to calculate the right delay
			*/
		
			if (PKT_SPREADING_ != 0 && cwnd_ == double(ssthresh_)) 
				SS_state_=0;

		} else { /* we are in the Congestion-Avoidance phase */
		
			SS_state_=0;
			cwnd_ = MIN(cwnd_ + increase_num_ / cwnd_, double(maxcwnd_));
		}
	
#ifdef DEBUG_HYBLA
		printf("Open cwnd at %f, cwnd=%f, SS_state=%d, BDP IS STILL NOT AVAILABLE\n",Scheduler::instance().clock(),(float)cwnd_,(int)SS_state_);	
#endif
		return;
	}
	
	/* now we can apply the Hybla algorithm */

	if (cwnd_ < ssthresh_) {
	
		/* we are in the Slow-Start phase */
		SS_state_=1;
		cwnd_ = MIN(cwnd_ + (pow(2,rho_)-1), double(ssthresh_));
		
		/* if the spreading tecnique is enabled, the ssthresh must be considered
		 * as the starting point for the CA phase (SS_state_=0) in order to calculate the right delay
		 */
		
		if (PKT_SPREADING_ != 0 && cwnd_ == double(ssthresh_)) 
			SS_state_=0;

	} else {
		
		/* we are in the Congestion-Avoidance phase */
		SS_state_=0;
		if (bdp_fix_) {
			if (cwnd_ < bdp_) {
				cwnd_ = MIN(cwnd_ + increase_num_ * pow(rho_,2) / cwnd_, double(maxcwnd_));
			} else {
			// no more than the standard if the BDP value has reached
                        	cwnd_ = MIN(cwnd_ + increase_num_ / cwnd_, double(maxcwnd_));
			}
		} else {
			cwnd_ = MIN(cwnd_ + increase_num_ * pow(rho_,2) / cwnd_, double(maxcwnd_));
		}
	}
	
	if (cwnd_ >= double(ssthresh_)){
		SS_state_=0;
		
		/* this part monitors the first Slow Start length
		 * it will be ignored if SS_first_ is initialized to 1
		 */
		if (SS_first_ == 0 && bdp_ > 0){
			cout << "Connection=" << fid_ << " lenght first SS=" << Scheduler::instance().clock() - firstsent_ << " cwnd=" << cwnd_ << " ssthresh=" << ssthresh_ << "\n";
			SS_first_=1;                                                                                     }
		/* End of monitoring of the first Slow Start length */
	}                                  			
	return;
}

void HyblaSTcpAgent::timeout(int tno)		/* unaltered with respect to the original version */
{
	if (tno == TCP_TIMER_RTX) {
		/*
		 * IF DSACK and dynamic adjustment of numdupacks_,
		 *  check whether a smaller value of numdupacks_
		 *  would have prevented this retransmit timeout.
		 * If DSACK and detection of premature retransmit
		 *  timeouts, then save some info here.
		 */
		dupacks_ = 0;
		fastrecov_ = FALSE;
		timeout_ = TRUE;
		if (highest_ack_ > last_ack_)
			last_ack_ = highest_ack_;
#ifdef DEBUGSACK1A
		printf ("timeout. highest_ack: %d seqno: %d\n",
			highest_ack_, t_seqno_);
#endif
		recover_ = maxseq_;
		scb_->ClearScoreBoard();
	}
	TcpAgent::timeout(tno);
}

void HyblaSTcpAgent::send_much(int force, int reason, int maxburst)		/* major modifications with respect to the original version */
{
	register int found, npacket = 0;
	int win = window();
	int xmit_seqno;
	//printf("Send_much %f , pipe=%d, cwnd=%d",Scheduler::instance().clock(),pipe_,(int)cwnd_);
	if (t_seqno_ == 0)
		firstsent_ = Scheduler::instance().clock();
	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",
			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",
						ixmit_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_++;
				
				/* the subsequent code regerds the implementation of packet spreading */
							
				if (PKT_SPREADING_ != 0 ) {
					
					/* set maxburst to 1 in order to send packets properly spaced
					 * however if bdp estimation is active 
					 * it is necessary to send the first two packets together
					 */
					
					if (newreno_changes_ != 0 && t_seqno_ < 3)
						maxburst = 2;
					else 
						maxburst = 1;
					
					/* this is only for the Progressive packet spreading technique*/
					if (PKT_SPREADING_ == 2)
						pkt_count_++; 
				
					/* finally set departure time for the next packet according to the chosen spreading tecnique */
					delsnd_timer_.resched(del_calc());
				} else
					/* simulate the machine computation delay */
					delsnd_timer_.resched(Random::uniform(overhead_));
										   
			}
		}
	        if (maxburst && npacket == maxburst)
		break;
	} /* end while */
}

void HyblaSTcpAgent::plot()	
{
#ifdef notyet
	double t = Scheduler::instance().clock();
	sprintf(trace_->buffer(), "t %g %d rtt %g\n",
		t, class_, t_rtt_ * tcp_tick_);
	trace_->dump();
	sprintf(trace_->buffer(), "t %g %d dev %g\n",
		t, class_, t_rttvar_ * tcp_tick_);
	trace_->dump();
	sprintf(trace_->buffer(), "t %g %d win %f\n", t, class_, cwnd_);
	trace_->dump();
	sprintf(trace_->buffer(), "t %g %d bck %d\n", t, class_, t_backoff_);
	trace_->dump();
#endif

}

double HyblaSTcpAgent::del_calc()		/* used to calculate the interpacket delay given a certain packet
						 * spreading tecnique */

{
	/* here the proper value of the interpacket delay is calculated 
	 * depending on the chosen spreading technique */
	
	double d = 0;
        double W_next = 0;
	double W_next_SS = 0;
	double W_next_CA = 0;

	if (cwnd_ == 1) 
		return d = Random::uniform(overhead_);

	switch (PKT_SPREADING_) {
				
		case 1 :
			/* Uniform packet spreading */
			if (SS_state_ == 1) {
					 /* calculate the Slow Start interpacket delay taking into account the ssthresh value.
					  * W_next is the estimation of the cwnd value at the end of the current Round Trip Time 
					  */
					W_next = rho_ * pow(2,rho_*ceil(1/rho_*log(cwnd_/rho_)/log(2.0)));
				        
					if (W_next <= ssthresh_) {
						d = basertt_ / W_next;
					 	} else {
						 	/* Here we are in the special RTT where SS->CA */
				    	         	W_next = ssthresh_ + pow(rho_,2);
					         	d= basertt_ / W_next;
						} 
			} else {
				/* calculate the Congestion Avoidance delay */
				W_next = ssthresh_ + ceil((cwnd_ - ssthresh_)/pow(rho_,2))*pow(rho_,2);
				d = basertt_ / MIN(W_next,maxcwnd_);
		        }
			break;
		case 2:	
			/* Progressive packet spreading */
			if (SS_state_ == 1) { 
				 /* We are in Slow Start phase and check if we will remain in this phase
				  * during the next RTT; the first RTT is treated in a special manner
				  */ 
				if (cwnd_ == wnd_init_) {
					 /* The Congestion Window is increased for the first time */
					 W_next_SS = rho_ + pow(2,rho_)-1 + pkt_count_ * (pow(2,rho_)-1);
					 d = basertt_/rho_ * log((W_next_SS - (pow(2,rho_)-1))/(W_next_SS-2*(pow(2,rho_)-1)))/log(2.0);
					 
				 } else { 
					 /* we are in the Slow Start phase, after the first increase
					  * we check if the next value of CWND is bigger than the Slow Start threshold
					  * W_next_SS is the Wj+k in the SS phase (see related publications)
					  */
					 
					 W_next_SS = pow(2,rho_) * cwnd_ - pow(pow(2,rho_)-1,2) + pkt_count_*(pow(2,rho_)-1);
					 
					 if (W_next_SS < ssthresh_) {	 
					 	d= basertt_/rho_ * log((W_next_SS - (pow(2,rho_)-1))/(W_next_SS - 2*(pow(2,rho_)-1)))/log(2.0);	 
				 	 } else {
						d = basertt_ / (cwnd_ + pkt_count_);
					 }
				 }
			} else {
				/* we are now in Congestion Avoidance phase */
				 
				/*		W_next_CA = cwnd_ + pow(rho_,2);
		 		 *		
		 		 *		double NextW =  W_next_CA;
		 		 *		double k = pkt_count_;
		 		 *		
				 *		if (k != 1)
		 		 *			while (k != 0){
				 *				NextW = NextW + pow(rho_,2) / NextW;
				 *				k--;
				 *			}
				 *		d = basertt_ / MIN(NextW,maxcwnd_);
		 		 */		
				
				/* This is a good approximation for CA */
				 d = basertt_ / (cwnd_ + pkt_count_);
				
			};
			break;
		case 3:
			
			/* Aggarwal proposal */
			d = basertt_ / cwnd_; 
			break;


		case 4: 
			/* modified Uniform packet spreading: Uniform in SS, rtt/int(w) in CA */
			if (SS_state_ == 1) {
				/* calculate the Slow Start interpacket delay taking into account the ssthresh value.
                                 * W_next is the estimation of the cwnd value at the end of the current Round Trip Time
				 */
			         W_next = rho_ * pow(2,rho_*ceil(1/rho_*log(cwnd_/rho_)/log(2.0)));
					
				 if (W_next <= ssthresh_) {
					 d = basertt_ / W_next;
				 } else {
					 /* here we are in the special RTT where SS->CA */
				         W_next = ssthresh_ + pow(rho_,2);
				         d= basertt_ / W_next;
				 } 
			} else {
				/* calculate the Congestion Avoidance delay */
				d = basertt_ / int(cwnd_);
		        }
			break;
			
		case 5 :
			/* FTCP in SS, rtt/w in CA */
			if (SS_state_ == 1) {
					 /* calculate the Slow Start interpacket delay taking into account the ssthresh value.
					  * W_next is the estimation of the cwnd value at the end of the current Round Trip Time
					  */
			                 
					W_next = rho_ * pow(2,rho_*ceil(1/rho_*log(cwnd_/rho_)/log(2.0)));
					
				        if (W_next <= ssthresh_) {
						d = basertt_ / W_next;
				 	} else {
						/* here we are in the special RTT where SS->CA */
				    	        W_next = ssthresh_ + pow(rho_,2);
					        d= basertt_ / W_next;
			                } 
			} else {
				/* calculate the Congestion Avoidance delay */
				d = basertt_ / cwnd_;
		        }
			break;
			
	}   //End of SWITCH
	return d;
}
	

	
