/*											/*
 * Copyright (c) 2000 Nortel Networks							 * Copyright (c) 2000 Nortel Networks
 * All rights reserved.									 * All rights reserved.
 * 											 * 
 * Redistribution and use in source and binary forms, with or without			 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions			 * modification, are permitted provided that the following conditions
 * are met:										 * are met:
 * 1. Redistributions of source code must retain the above copyright			 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.			 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright			 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the		 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.		 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software		 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:					 *    must display the following acknowledgement:
 *      This product includes software developed by Nortel Networks.			 *      This product includes software developed by Nortel Networks.
 * 4. The name of the Nortel Networks may not be used					 * 4. The name of the Nortel Networks may not be used
 *    to endorse or promote products derived from this software without			 *    to endorse or promote products derived from this software without
 *    specific prior written permission.						 *    specific prior written permission.
 * 											 * 
 * THIS SOFTWARE IS PROVIDED BY NORTEL AND CONTRIBUTORS ``AS IS'' AND			 * THIS SOFTWARE IS PROVIDED BY NORTEL AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE		 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE		 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL NORTEL OR CONTRIBUTORS BE LIABLE			 * ARE DISCLAIMED.  IN NO EVENT SHALL NORTEL OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL		 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS		 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)		 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT		 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY		 * 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		 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.										 * SUCH DAMAGE.
 *											 *
 * Developed by: Farhan Shallwani, Jeremy Ethridge					 * Developed by: Farhan Shallwani, Jeremy Ethridge
 *               Peter Pieda, and Mandeep Baines					 *               Peter Pieda, and Mandeep Baines
 * Maintainer: Peter Pieda <ppieda@nortelnetworks.com>					 * Maintainer: Peter Pieda <ppieda@nortelnetworks.com>
 */											 */

#include <stdio.h>									#include <stdio.h>
#include "ip.h"										#include "ip.h"
#include "dsred.h"									#include "dsred.h"
#include "delay.h"									#include "delay.h"
#include "random.h"									#include "random.h"
#include "flags.h"									#include "flags.h"
#include "tcp.h"									#include "tcp.h"
#include "dsredq.h"									#include "dsredq.h"


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
dsREDClass declaration. 								dsREDClass declaration. 
    Links the new class in the TCL heirarchy.  See "Notes And Documentation for 	    Links the new class in the TCL heirarchy.  See "Notes And Documentation for 
ns-2."											ns-2."
------------------------------------------------------------------------------*/	------------------------------------------------------------------------------*/
static class dsREDClass : public TclClass {						static class dsREDClass : public TclClass {
public:											public:
	dsREDClass() : TclClass("Queue/dsRED") {}						dsREDClass() : TclClass("Queue/dsRED") {}
	TclObject* create(int, const char*const*) {						TclObject* create(int, const char*const*) {
		return (new dsREDQueue);								return (new dsREDQueue);
	}											}
} class_dsred;										} class_dsred;


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
dsREDQueue() Constructor.								dsREDQueue() Constructor.
    Initializes the queue.  Note that the default value assigned to numQueues 		    Initializes the queue.  Note that the default value assigned to numQueues 
in tcl/lib/ns-default.tcl must be no greater than MAX_QUEUES (the physical 		in tcl/lib/ns-default.tcl must be no greater than MAX_QUEUES (the physical 
queue array size).									queue array size).
------------------------------------------------------------------------------*/	------------------------------------------------------------------------------*/
dsREDQueue::dsREDQueue() : de_drop_(NULL), link_(NULL), channel_(0)  {		      |	dsREDQueue::dsREDQueue() : de_drop_(NULL), link_(NULL)   {
  bind("numQueues_", &numQueues_);							  bind("numQueues_", &numQueues_);
  bind_bool("ecn_", &ecn_);								  bind_bool("ecn_", &ecn_);
  int i;										  int i;
										      |	  
  ds = NULL;  									      <
  numPrec = MAX_PREC;									  numPrec = MAX_PREC;
  schedMode = schedModeRR;								  schedMode = schedModeRR;
  											  
  for(i=0;i<MAX_QUEUES;i++){								  for(i=0;i<MAX_QUEUES;i++){
    queueMaxRate[i] = 0;								    queueMaxRate[i] = 0;
    queueWeight[i]=1;									    queueWeight[i]=1;
  }											  }
  											  
  queuesDone = MAX_QUEUES;								  queuesDone = MAX_QUEUES;
  phbEntries = 0;		// Number of entries in PHB table			  phbEntries = 0;		// Number of entries in PHB table
										      |	  
  reset();										  reset();
}											}

// RED queues initilization								// RED queues initilization
void dsREDQueue::reset() {								void dsREDQueue::reset() {
  int i;										  int i;
										      |	  
  qToDq = 0;		// q to be dequed, initialized to 0			      |	  qToDq = 0;		// q to be dequed, initialized to 0	
										      |	  
  for(i=0;i<MAX_QUEUES;i++){								  for(i=0;i<MAX_QUEUES;i++){
    queueAvgRate[i] = 0.0;								    queueAvgRate[i] = 0.0;
    queueArrTime[i] = 0.0;								    queueArrTime[i] = 0.0;
    slicecount[i]=0;									    slicecount[i]=0;
    pktcount[i]=0;									    pktcount[i]=0;
    wirrTemp[i]=0;									    wirrTemp[i]=0;
    wirrqDone[i]=0;									    wirrqDone[i]=0;
  }											  }
										      |	  
  clearDetailedStats();								      <
										      <
  stats.drops = 0;									  stats.drops = 0;
  stats.edrops = 0;									  stats.edrops = 0;
  stats.pkts = 0;									  stats.pkts = 0;
										      |	  
  for(i=0;i<MAX_CP;i++){								  for(i=0;i<MAX_CP;i++){
    stats.drops_CP[i]=0;								    stats.drops_CP[i]=0;
    stats.edrops_CP[i]=0;								    stats.edrops_CP[i]=0;
    stats.pkts_CP[i]=0;									    stats.pkts_CP[i]=0;
  }											  }
										      |	  
  for (i = 0; i < MAX_QUEUES; i++)							  for (i = 0; i < MAX_QUEUES; i++)
    redq_[i].qlim = limit();								    redq_[i].qlim = limit();
										      |	  
  // Compute the "packet time constant" if we know the					  // Compute the "packet time constant" if we know the
  // link bandwidth.  The ptc is the max number of (avg sized)				  // link bandwidth.  The ptc is the max number of (avg sized)
  // pkts per second which can be placed on the link.					  // pkts per second which can be placed on the link.
  if (link_)										  if (link_)
    for (int i = 0; i < MAX_QUEUES; i++)						    for (int i = 0; i < MAX_QUEUES; i++)
      redq_[i].setPTC(link_->bandwidth());						      redq_[i].setPTC(link_->bandwidth());
										      |	  
  Queue::reset();									  Queue::reset();
}											}


/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void edrop(Packet* pkt)									void edrop(Packet* pkt)
    This method is used so that flowmonitor can monitor early drops.			    This method is used so that flowmonitor can monitor early drops.
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
void dsREDQueue::edrop(Packet* p)							void dsREDQueue::edrop(Packet* p)
{											{

	if (de_drop_ != 0){									if (de_drop_ != 0){
		de_drop_->recv(p);									de_drop_->recv(p);
	}											}
	else {											else {
		drop(p);										drop(p);
	}											}
}											}

/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void applyTSWMeter(int q_id, int pkt_size)						void applyTSWMeter(int q_id, int pkt_size)
Update the average rate for a physical Q (indicated by q_id).				Update the average rate for a physical Q (indicated by q_id).
Pre: policy's variables avgRate, arrivalTime, and winLen hold valid values;		Pre: policy's variables avgRate, arrivalTime, and winLen hold valid values;
  pkt_size specifies the bytes just dequeued (0 means no packet dequeued).		  pkt_size specifies the bytes just dequeued (0 means no packet dequeued).
Post: Adjusts policy's TSW state variables avgRate and arrivalTime		      |	Post: Adjusts policy's TSW state variables avgRate and arrivalTime 
  (also called tFront) according to the bytes sent.					  (also called tFront) according to the bytes sent.
Note: See the paper "Explicit Allocation of Best effor Delivery Service" (David		Note: See the paper "Explicit Allocation of Best effor Delivery Service" (David
  Clark and Wenjia Fang), Section 3.3, for a description of the TSW Tagger.		  Clark and Wenjia Fang), Section 3.3, for a description of the TSW Tagger.
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
void dsREDQueue::applyTSWMeter(int q_id, int pkt_size) {				void dsREDQueue::applyTSWMeter(int q_id, int pkt_size) {
  double now, bytesInTSW, newBytes;							  double now, bytesInTSW, newBytes;
  double winLen = 1.0;									  double winLen = 1.0;

  bytesInTSW = queueAvgRate[q_id] * winLen;						  bytesInTSW = queueAvgRate[q_id] * winLen;

  // Modified by xuanc(xuanc@isi.edu) Oct 18, 2001,				      |	  // Modified by xuanc(xuanc@isi.edu) Oct 18, 2001, 
  // referring to the patch contributed by					      |	  // referring to the patch contributed by 
  // Sergio Andreozzi <sergio.andreozzi@lut.fi>						  // Sergio Andreozzi <sergio.andreozzi@lut.fi>
  newBytes = bytesInTSW + pkt_size;							  newBytes = bytesInTSW + pkt_size;

  // Calculate the average rate (SW)							  // Calculate the average rate (SW)
  now = Scheduler::instance().clock();							  now = Scheduler::instance().clock();
  queueAvgRate[q_id] = newBytes / (now - queueArrTime[q_id] + winLen);			  queueAvgRate[q_id] = newBytes / (now - queueArrTime[q_id] + winLen);
  queueArrTime[q_id] = now;								  queueArrTime[q_id] = now;
}											}


/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void enque(Packet* pkt)								      |	void enque(Packet* pkt) 
    The following method outlines the enquing mechanism for a Diffserv router.		    The following method outlines the enquing mechanism for a Diffserv router.
This method is not used by the inheriting classes; it only serves as an		      |	This method is not used by the inheriting classes; it only serves as an 
outline.										outline.
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
void dsREDQueue::enque(Packet* pkt) {							void dsREDQueue::enque(Packet* pkt) {
  int codePt, eq_id, prec;								  int codePt, eq_id, prec;
  hdr_ip* iph = hdr_ip::access(pkt);							  hdr_ip* iph = hdr_ip::access(pkt);
  //extracting the marking done by the edge router					  //extracting the marking done by the edge router
  codePt = iph->prio();								      |	  codePt = iph->prio();	
  int ecn = 0;										  int ecn = 0;
										      |	  
  //looking up queue and prec numbers for that codept					  //looking up queue and prec numbers for that codept
  lookupPHBTable(codePt, &eq_id, &prec);					      |	  lookupPHBTable(codePt, &eq_id, &prec);	

  // code added for ECN support								  // code added for ECN support
  //hdr_flags* hf = (hdr_flags*)(pkt->access(off_flags_));				  //hdr_flags* hf = (hdr_flags*)(pkt->access(off_flags_));
  // Changed for the latest version instead of 2.1b6					  // Changed for the latest version instead of 2.1b6
  hdr_flags* hf = hdr_flags::access(pkt);						  hdr_flags* hf = hdr_flags::access(pkt);

  if (ecn_ && hf->ect()) ecn = 1;							  if (ecn_ && hf->ect()) ecn = 1;
										      |	  
  stats.pkts_CP[codePt]++;								  stats.pkts_CP[codePt]++;
  stats.pkts++;										  stats.pkts++;
										      |	  
  struct detailedStatType *dsp = NULL;						      <
  int pkt_size = (hdr_cmn::access(pkt))->size(); 				      <
  { // stat block								      <
    // get a ptr to the stats block for the packet				      <
    int flow_id = iph->fid_;							      <
    dsp = getDS(flow_id, codePt);						      <
										      <
    // finally log the packet arrival 						      <
    if (dsp) {									      <
      dsp->p_arr++;								      <
      dsp->b_arr += pkt_size;							      <
    }										      <
  }										      <
										      <
										      <
  switch(redq_[eq_id].enque(pkt, prec, ecn)) {						  switch(redq_[eq_id].enque(pkt, prec, ecn)) {
  case PKT_ENQUEUED:									  case PKT_ENQUEUED:
    /*   let q(i) to be the size of the queue, packet(i) experiences when it is to be <
										      <
         we want to calculate (sum(q(i), 0, n-1) / n) for the queueing delay indicati <
         in fact gives the number of avg bytes each packets sees in queue. With the b <
         queueing delay can easily be calculated.				      <
         									      <
         However, we cannot have a large array to hold the qsize each packet sees. We <
         way to calculate this value						      <
         									      <
         q_delay(n)   = 1/n     * sum(q(i), 0, n-1)				      <
         q_delay(n+1) = 1/(n+1) * sum(q(i), 0, n)				      <
                      = 1/(n+1) * (q(n) + sum(q(i), 0, n-1)) 			      <
                      = 1/(n+1) * (q(n) + n * q_delay(n))			      <
                      								      <
         we're calculating this below  						      <
     */										      <
    if (dsp) {									      <
//      dsp->qdelay = ((dsp->qdelay * dsp->b_enq) + redq_[eq_id].bytes) / (dsp->b_enq <
      dsp->qdelay = ((dsp->qdelay * dsp->p_enq) + redq_[eq_id].bytes) / (dsp->p_enq + <
										      <
      dsp->p_enq++;								      <
      dsp->b_enq += pkt_size;							      <
    }										      <
    break;										    break;
  case PKT_DROPPED:									  case PKT_DROPPED:
    stats.drops_CP[codePt]++;								    stats.drops_CP[codePt]++;
    stats.drops++;									    stats.drops++;
    if (dsp) {									      <
      dsp->p_drop++;								      <
      dsp->b_drop += pkt_size;							      <
    }										      <
										      <
    drop(pkt);										    drop(pkt);
    break;									      |	         break;
  case PKT_EDROPPED:									  case PKT_EDROPPED:
    stats.edrops_CP[codePt]++;								    stats.edrops_CP[codePt]++;
    stats.edrops++;									    stats.edrops++;
    if (dsp) {									      <
      dsp->p_edrop++;								      <
      dsp->b_edrop += pkt_size;							      <
    }										      <
										      <
    edrop(pkt);										    edrop(pkt);
    break;										    break;
  case PKT_MARKED:									  case PKT_MARKED:
    if (dsp) {									      |	    hf->ce() = 1; 	// mark Congestion Experienced bit		
//    dsp->qdelay = ((dsp->qdelay * dsp->b_enq) + redq_[eq_id].bytes) / (dsp->b_enq + |	    break;			
      dsp->qdelay = ((dsp->qdelay * dsp->p_enq) + redq_[eq_id].bytes) / (dsp->p_enq + <
      										      <
      dsp->p_mark++;								      <
      dsp->b_mark += pkt_size;							      <
    										      <
      dsp->p_enq++;								      <
      dsp->b_enq += pkt_size;							      <
    }										      <
										      <
    hf->ce() = 1; 	// mark Congestion Experienced bit			      <
    break;									      <
  default:										  default:
    break;										    break;
  }											  }
}											}

// Dequing mechanism for both edge and core router.					// Dequing mechanism for both edge and core router.
Packet* dsREDQueue::deque() {								Packet* dsREDQueue::deque() {
  Packet *p = NULL;									  Packet *p = NULL;
  int queue, prec;									  int queue, prec;
  hdr_ip* iph;										  hdr_ip* iph;
  int fid;										  int fid;
  int dq_id;										  int dq_id;

  // Select queue to deque under the scheduling scheme specified.			  // Select queue to deque under the scheduling scheme specified.
  dq_id = selectQueueToDeque();								  dq_id = selectQueueToDeque();
										      |	  
  // Dequeue a packet from the underlying queue:					  // Dequeue a packet from the underlying queue:
  if (dq_id < numQueues_)							      |	  if (dq_id < numQueues_) 
    p = redq_[dq_id].deque();								    p = redq_[dq_id].deque();
										      |	  
  if (p) {									      |	  if (p) { 
    iph= hdr_ip::access(p);								    iph= hdr_ip::access(p);
    fid = iph->flowid()/32;								    fid = iph->flowid()/32;
    pktcount[dq_id]+=1;									    pktcount[dq_id]+=1;
    											    
    { // measurement block							      <
      struct detailedStatType *dsp = getDS(iph->fid_, iph->prio()); 		      <
      if (dsp) {     								      <
        dsp->p_deq++;								      <
        dsp->b_deq += (hdr_cmn::access(p))->size();				      <
      }										      <
    }										      <
										      <
    // update the average rate for pri-queue						    // update the average rate for pri-queue
    // Modified by xuanc(xuanc@isi.edu) Oct 18, 2001, 					    // Modified by xuanc(xuanc@isi.edu) Oct 18, 2001, 
    // referring to the patch contributed by 						    // referring to the patch contributed by 
    // Sergio Andreozzi <sergio.andreozzi@lut.fi>					    // Sergio Andreozzi <sergio.andreozzi@lut.fi>
    // When there is a packet dequeued, 						    // When there is a packet dequeued, 
    // update the average rate of each queue ()						    // update the average rate of each queue ()
    if (schedMode==schedModePRI) 							    if (schedMode==schedModePRI) 
      for (int i=0;i<numQueues_;i++) 							      for (int i=0;i<numQueues_;i++) 
	if (queueMaxRate[i]) 									if (queueMaxRate[i]) 
	  applyTSWMeter(i, (i == dq_id) ? hdr_cmn::access(p)->size() : 0); 			  applyTSWMeter(i, (i == dq_id) ? hdr_cmn::access(p)->size() : 0); 

    // Get the precedence level (or virtual queue id)					    // Get the precedence level (or virtual queue id)
    // for the packet dequeued.								    // for the packet dequeued.
    lookupPHBTable(getCodePt(p), &queue, &prec);					    lookupPHBTable(getCodePt(p), &queue, &prec);
										      |	    
    // decrement virtual queue length							    // decrement virtual queue length
    // Previously in updateREDStateVar, moved by xuanc (12/03/01)			    // Previously in updateREDStateVar, moved by xuanc (12/03/01)
    //redq_[dq_id].qParam_[prec].qlen--;						    //redq_[dq_id].qParam_[prec].qlen--;	
    redq_[dq_id].updateVREDLen(prec);						      |	    redq_[dq_id].updateVREDLen(prec);	
    // update state variables for that "virtual" queue					    // update state variables for that "virtual" queue
    redq_[dq_id].updateREDStateVar(prec);						    redq_[dq_id].updateREDStateVar(prec);
										      <
    redq_[dq_id].bio_boundary_check(prec);					      <
  }											  }
  											  
  // Return the dequed packet:								  // Return the dequed packet:	
  return(p);										  return(p);
}											}

//    Extracts the code point marking from packet header.				//    Extracts the code point marking from packet header.
int dsREDQueue::getCodePt(Packet *p) {							int dsREDQueue::getCodePt(Packet *p) {
  hdr_ip* iph = hdr_ip::access(p);							  hdr_ip* iph = hdr_ip::access(p);
  return(iph->prio());									  return(iph->prio());
}											}

// Reutrn the id of physical queue to be dequeued					// Reutrn the id of physical queue to be dequeued
int dsREDQueue::selectQueueToDeque() {							int dsREDQueue::selectQueueToDeque() {
  // If the queue to be dequed has no elements, 					  // If the queue to be dequed has no elements, 
  // look for the next queue in line							  // look for the next queue in line
  int i = 0;										  int i = 0;
  											  
  // Round-Robin									  // Round-Robin
  if(schedMode==schedModeRR){								  if(schedMode==schedModeRR){
    //printf("RR\n");									    //printf("RR\n");
    qToDq = ((qToDq + 1) % numQueues_);							    qToDq = ((qToDq + 1) % numQueues_);
    while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {			    while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {
      qToDq = ((qToDq + 1) % numQueues_);						      qToDq = ((qToDq + 1) % numQueues_);			
      i++;										      i++;
    }											    }
  } else if (schedMode==schedModeWRR) { // Weighted Round Robin				  } else if (schedMode==schedModeWRR) { // Weighted Round Robin
    if(wirrTemp[qToDq]<=0){								    if(wirrTemp[qToDq]<=0){
      qToDq = ((qToDq + 1) % numQueues_);						      qToDq = ((qToDq + 1) % numQueues_);
      wirrTemp[qToDq] = queueWeight[qToDq] - 1;						      wirrTemp[qToDq] = queueWeight[qToDq] - 1;
    } else {										    } else {
      wirrTemp[qToDq] = wirrTemp[qToDq] -1;						      wirrTemp[qToDq] = wirrTemp[qToDq] -1;
    }										      |	    }			
    while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {			    while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {
      wirrTemp[qToDq] = 0;								      wirrTemp[qToDq] = 0;
      qToDq = ((qToDq + 1) % numQueues_);						      qToDq = ((qToDq + 1) % numQueues_);
      wirrTemp[qToDq] = queueWeight[qToDq] - 1;						      wirrTemp[qToDq] = queueWeight[qToDq] - 1;
      i++;										      i++;
    }											    }
  } else if (schedMode==schedModeWIRR) {						  } else if (schedMode==schedModeWIRR) {
    qToDq = ((qToDq + 1) % numQueues_);							    qToDq = ((qToDq + 1) % numQueues_);
    while ((i<numQueues_) && ((redq_[qToDq].getRealLength()==0) || (wirrqDone[qToDq])	    while ((i<numQueues_) && ((redq_[qToDq].getRealLength()==0) || (wirrqDone[qToDq])
      if (!wirrqDone[qToDq]) {								      if (!wirrqDone[qToDq]) {
	queuesDone++;										queuesDone++;
	wirrqDone[qToDq]=1;									wirrqDone[qToDq]=1;
      }											      }
      qToDq = ((qToDq + 1) % numQueues_);						      qToDq = ((qToDq + 1) % numQueues_);
      i++;										      i++;
    }											    }
    											    
    if (wirrTemp[qToDq] == 1) {								    if (wirrTemp[qToDq] == 1) {
      queuesDone +=1;									      queuesDone +=1;
      wirrqDone[qToDq]=1;								      wirrqDone[qToDq]=1;
    }											    }
    wirrTemp[qToDq]-=1;									    wirrTemp[qToDq]-=1;
    if(queuesDone >= numQueues_) {							    if(queuesDone >= numQueues_) {
      queuesDone = 0;									      queuesDone = 0;
      for(i=0;i<numQueues_;i++) {							      for(i=0;i<numQueues_;i++) {
	wirrTemp[i] = queueWeight[i];								wirrTemp[i] = queueWeight[i];
	wirrqDone[i]=0;										wirrqDone[i]=0;
      }   										      }   	
    }											    }
  } else if (schedMode==schedModePRI) {							  } else if (schedMode==schedModePRI) {
    // Find the queue with highest priority, which satisfies:				    // Find the queue with highest priority, which satisfies:
    // 1. nozero queue length; and either						    // 1. nozero queue length; and either
    // 2.1. has no MaxRate specified; or						    // 2.1. has no MaxRate specified; or
    // 2.2. has MaxRate specified and 							    // 2.2. has MaxRate specified and 
    //          its average rate is not beyond that limit.				    //          its average rate is not beyond that limit.
    i = 0;										    i = 0;
    while (i < numQueues_ && 								    while (i < numQueues_ && 
	   (redq_[i].getRealLength() == 0 ||							   (redq_[i].getRealLength() == 0 ||
	    (queueMaxRate[i] && queueAvgRate[i]>queueMaxRate[i]))){				    (queueMaxRate[i] && queueAvgRate[i]>queueMaxRate[i]))){
      i++;										      i++;
    }											    }
    qToDq = i;										    qToDq = i;

    // If no queue satisfies the condition above,					    // If no queue satisfies the condition above,
    // find the Queue with highest priority,					      |	    // find the Queue with highest priority, 
    // which has packet to dequeue.							    // which has packet to dequeue.
    // NOTE: the high priority queue can still have its packet dequeued			    // NOTE: the high priority queue can still have its packet dequeued
    //       even if its average rate has beyond the MAX rate specified!		    //       even if its average rate has beyond the MAX rate specified!
    //       Ideally, a NO_PACKET_TO_DEQUEUE should be returned.			    //       Ideally, a NO_PACKET_TO_DEQUEUE should be returned.
    if (i == numQueues_) {								    if (i == numQueues_) {
      i = qToDq = 0;									      i = qToDq = 0;
      while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {			      while ((i < numQueues_) && (redq_[qToDq].getRealLength() == 0)) {
	qToDq = ((qToDq + 1) % numQueues_);							qToDq = ((qToDq + 1) % numQueues_);
	i++;											i++;
      }											      }
    }											    }
  }											  }
  return(qToDq);									  return(qToDq);
}										      |	}	

/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void lookupPHBTable(int codePt, int* queue, int* prec)					void lookupPHBTable(int codePt, int* queue, int* prec)
    Assigns the queue and prec parameters values corresponding to a given code	      |	    Assigns the queue and prec parameters values corresponding to a given code 
point.  The code point is assumed to be present in the PHB table.  If it is	      |	point.  The code point is assumed to be present in the PHB table.  If it is 
not, an error message is outputted and queue and prec are undefined.			not, an error message is outputted and queue and prec are undefined.
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
void dsREDQueue::lookupPHBTable(int codePt, int* queue, int* prec) {			void dsREDQueue::lookupPHBTable(int codePt, int* queue, int* prec) {
  for (int i = 0; i < phbEntries; i++) {						  for (int i = 0; i < phbEntries; i++) {
    if (phb_[i].codePt_ == codePt) {							    if (phb_[i].codePt_ == codePt) {
      *queue = phb_[i].queue_;								      *queue = phb_[i].queue_;
      *prec = phb_[i].prec_;								      *prec = phb_[i].prec_;
      return;										      return;
    }											    }
  }											  }
  printf("ERROR: No match found for code point %d in PHB Table.\n", codePt);		  printf("ERROR: No match found for code point %d in PHB Table.\n", codePt);
}											}

/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void addPHBEntry(int codePt, int queue, int prec)					void addPHBEntry(int codePt, int queue, int prec)
    Add a PHB table entry.  (Each entry maps a code point to a queue-precedence		    Add a PHB table entry.  (Each entry maps a code point to a queue-precedence
pair.)											pair.)
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
void dsREDQueue::addPHBEntry(int codePt, int queue, int prec) {				void dsREDQueue::addPHBEntry(int codePt, int queue, int prec) {
  if (phbEntries == MAX_CP) {								  if (phbEntries == MAX_CP) {
    printf("ERROR: PHB Table size limit exceeded.\n");					    printf("ERROR: PHB Table size limit exceeded.\n");
  } else {										  } else {
    phb_[phbEntries].codePt_ = codePt;							    phb_[phbEntries].codePt_ = codePt;
    phb_[phbEntries].queue_ = queue;							    phb_[phbEntries].queue_ = queue;
    phb_[phbEntries].prec_ = prec;							    phb_[phbEntries].prec_ = prec;
    stats.valid_CP[codePt] = 1;								    stats.valid_CP[codePt] = 1;
    phbEntries++;									    phbEntries++;
  }											  }
}											}

/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
void addPHBEntry(int codePt, int queue, int prec)					void addPHBEntry(int codePt, int queue, int prec)
    Add a PHB table entry.  (Each entry maps a code point to a queue-precedence		    Add a PHB table entry.  (Each entry maps a code point to a queue-precedence
pair.)											pair.)
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
double dsREDQueue::getStat(int argc, const char*const* argv) {				double dsREDQueue::getStat(int argc, const char*const* argv) {

	if (argc == 3) {									if (argc == 3) {
		if (strcmp(argv[2], "drops") == 0)							if (strcmp(argv[2], "drops") == 0)
         return (stats.drops*1.0);							         return (stats.drops*1.0);
		if (strcmp(argv[2], "edrops") == 0)							if (strcmp(argv[2], "edrops") == 0)
         return (stats.edrops*1.0);							         return (stats.edrops*1.0);
		if (strcmp(argv[2], "pkts") == 0)							if (strcmp(argv[2], "pkts") == 0)
         return (stats.pkts*1.0);							         return (stats.pkts*1.0);
   }											   }
	if (argc == 4) {									if (argc == 4) {
		if (strcmp(argv[2], "drops") == 0)							if (strcmp(argv[2], "drops") == 0)
         return (stats.drops_CP[atoi(argv[3])]*1.0);					         return (stats.drops_CP[atoi(argv[3])]*1.0);
		if (strcmp(argv[2], "edrops") == 0)							if (strcmp(argv[2], "edrops") == 0)
         return (stats.edrops_CP[atoi(argv[3])]*1.0);					         return (stats.edrops_CP[atoi(argv[3])]*1.0);
		if (strcmp(argv[2], "pkts") == 0)							if (strcmp(argv[2], "pkts") == 0)
         return (stats.pkts_CP[atoi(argv[3])]*1.0);					         return (stats.pkts_CP[atoi(argv[3])]*1.0);
	}											}
	return -1.0;										return -1.0;
}											}


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void setNumPrec(int prec)							      |	void setNumPrec(int prec) 
    Sets the current number of drop precendences.  The number of precedences is		    Sets the current number of drop precendences.  The number of precedences is
the number of virtual queues per physical queue.					the number of virtual queues per physical queue.
------------------------------------------------------------------------------*/	------------------------------------------------------------------------------*/
void dsREDQueue::setNumPrec(int prec) {							void dsREDQueue::setNumPrec(int prec) {
	int i;											int i;

	if (prec > MAX_PREC) {									if (prec > MAX_PREC) {
		printf("ERROR: Cannot declare more than %d prcedence levels (as defin			printf("ERROR: Cannot declare more than %d prcedence levels (as defin
	} else {										} else {
		numPrec = prec;										numPrec = prec;

		for (i = 0; i < MAX_QUEUES; i++)							for (i = 0; i < MAX_QUEUES; i++)
			redq_[i].numPrec = numPrec;								redq_[i].numPrec = numPrec;
	}											}
}											}

/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void setMREDMode(const char* mode)							void setMREDMode(const char* mode)
   sets up the average queue accounting mode.						   sets up the average queue accounting mode.
----------------------------------------------------------------------------*/		----------------------------------------------------------------------------*/
void dsREDQueue::setMREDMode(const char* mode, const char* queue) {			void dsREDQueue::setMREDMode(const char* mode, const char* queue) {
  int i;										  int i;
  mredModeType tempMode;								  mredModeType tempMode;
										      |	  
  if (strcmp(mode, "RIO-C") == 0)							  if (strcmp(mode, "RIO-C") == 0)
    tempMode = rio_c;									    tempMode = rio_c;
  else if (strcmp(mode, "RIO-D") == 0)							  else if (strcmp(mode, "RIO-D") == 0)
    tempMode = rio_d;									    tempMode = rio_d;
  else if (strcmp(mode, "WRED") == 0)							  else if (strcmp(mode, "WRED") == 0)
    tempMode = wred;									    tempMode = wred;
  else if (strcmp(mode, "DROP") == 0)							  else if (strcmp(mode, "DROP") == 0)
    tempMode = dropTail;								    tempMode = dropTail;
  else if (strcmp(mode, "BIO-AAI") == 0)					      <
    tempMode = bio_aai;								      <
  else if (strcmp(mode, "BIO-AAT") == 0)					      <
    tempMode = bio_aat;								      <
  else if (strcmp(mode, "BIO-SAI") == 0)					      <
    tempMode = bio_sai;								      <
  else if (strcmp(mode, "BIO-SAT") == 0)					      <
    tempMode = bio_sat;								      <
  else if (strcmp(mode, "BIO-ASI") == 0)					      <
    tempMode = bio_asi;								      <
  else if (strcmp(mode, "BIO-AST") == 0)					      <
    tempMode = bio_ast;								      <
  else if (strcmp(mode, "BIO-SSI") == 0)					      <
    tempMode = bio_ssi;								      <
  else if (strcmp(mode, "BIO-SST") == 0)					      <
    tempMode = bio_sst;								      <
  else {										  else {
    printf("Error: MRED mode %s does not exist\n",mode);				    printf("Error: MRED mode %s does not exist\n",mode);
    return;										    return;
  }											  }
										      |	  
  if (!queue)										  if (!queue)
    for (i = 0; i < MAX_QUEUES; i++)							    for (i = 0; i < MAX_QUEUES; i++)
      redq_[i].mredMode = tempMode;							      redq_[i].mredMode = tempMode;
  else											  else
    redq_[atoi(queue)].mredMode = tempMode;						    redq_[atoi(queue)].mredMode = tempMode;
}											}


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void printPHBTable()									void printPHBTable()
    Prints the PHB Table, with one entry per line.					    Prints the PHB Table, with one entry per line.
------------------------------------------------------------------------------*/	------------------------------------------------------------------------------*/
void dsREDQueue::printPHBTable() {							void dsREDQueue::printPHBTable() {
   printf("PHB Table:\n");								   printf("PHB Table:\n");
   for (int i = 0; i < phbEntries; i++)							   for (int i = 0; i < phbEntries; i++)
      printf("Code Point %d is associated with Queue %d, Precedence %d\n", phb_[i].co	      printf("Code Point %d is associated with Queue %d, Precedence %d\n", phb_[i].co
   printf("\n");									   printf("\n");
}											}


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void printStats()									void printStats()
    An output method that may be altered to assist debugging.				    An output method that may be altered to assist debugging.
------------------------------------------------------------------------------*/	------------------------------------------------------------------------------*/
void dsREDQueue::printStats() {								void dsREDQueue::printStats() {
	printf("\nPackets Statistics\n");							printf("\nPackets Statistics\n");
	printf("=======================================\n");					printf("=======================================\n");
	printf(" CP  TotPkts   TxPkts   ldrops   edrops\n");					printf(" CP  TotPkts   TxPkts   ldrops   edrops\n");
	printf(" --  -------   ------   ------   ------\n");					printf(" --  -------   ------   ------   ------\n");
	printf("All %8ld %8ld %8ld %8ld\n",stats.pkts,stats.pkts-stats.drops-stats.ed		printf("All %8ld %8ld %8ld %8ld\n",stats.pkts,stats.pkts-stats.drops-stats.ed
	for (int i = 0; i < MAX_CP; i++)							for (int i = 0; i < MAX_CP; i++)
		if (stats.pkts_CP[i] != 0)								if (stats.pkts_CP[i] != 0)
			printf("%3d %8ld %8ld %8ld %8ld\n",i,stats.pkts_CP[i],stats.p				printf("%3d %8ld %8ld %8ld %8ld\n",i,stats.pkts_CP[i],stats.p

}											}


void dsREDQueue::printWRRcount() {							void dsREDQueue::printWRRcount() {
   int i;										   int i;
   for (i = 0; i < numQueues_; i++){							   for (i = 0; i < numQueues_; i++){
      printf("%d: %d %d %d.\n", i, slicecount[i],pktcount[i],queueWeight[i]);		      printf("%d: %d %d %d.\n", i, slicecount[i],pktcount[i],queueWeight[i]);
   }											   }
}											}


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void setSchedularMode(int schedtype)							void setSchedularMode(int schedtype)
   sets up the schedular mode.								   sets up the schedular mode.
----------------------------------------------------------------------------*/		----------------------------------------------------------------------------*/
void dsREDQueue::setSchedularMode(const char* schedtype) {				void dsREDQueue::setSchedularMode(const char* schedtype) {
	if (strcmp(schedtype, "RR") == 0)							if (strcmp(schedtype, "RR") == 0)
   	schedMode = schedModeRR;							   	schedMode = schedModeRR;
	else if (strcmp(schedtype, "WRR") == 0)							else if (strcmp(schedtype, "WRR") == 0)
		schedMode = schedModeWRR;								schedMode = schedModeWRR;
	else if (strcmp(schedtype, "WIRR") == 0)						else if (strcmp(schedtype, "WIRR") == 0)
		schedMode = schedModeWIRR;								schedMode = schedModeWIRR;
	else if (strcmp(schedtype, "PRI") == 0)							else if (strcmp(schedtype, "PRI") == 0)
		schedMode = schedModePRI;								schedMode = schedModePRI;
	else											else
		printf("Error: Scheduler type %s does not exist\n",schedtype);				printf("Error: Scheduler type %s does not exist\n",schedtype);
}											}


/*------------------------------------------------------------------------------	/*------------------------------------------------------------------------------
void addQueueWeights(int queueNum, int weight)						void addQueueWeights(int queueNum, int weight)
   An input method to set the individual Queue Weights.					   An input method to set the individual Queue Weights.
----------------------------------------------------------------------------*/		----------------------------------------------------------------------------*/
void dsREDQueue::addQueueWeights(int queueNum, int weight) {				void dsREDQueue::addQueueWeights(int queueNum, int weight) {
  if(queueNum < MAX_QUEUES){								  if(queueNum < MAX_QUEUES){
    queueWeight[queueNum]=weight;							    queueWeight[queueNum]=weight;
  } else {										  } else {
    printf("The queue number is out of range.\n");					    printf("The queue number is out of range.\n");
  }											  }
}											}

//Set the individual Queue Max Rates for Priority Queueing.				//Set the individual Queue Max Rates for Priority Queueing.
void dsREDQueue::addQueueRate(int queueNum, int rate) {					void dsREDQueue::addQueueRate(int queueNum, int rate) {
  if(queueNum < MAX_QUEUES){								  if(queueNum < MAX_QUEUES){
    // Convert to BYTE/SECOND								    // Convert to BYTE/SECOND
    queueMaxRate[queueNum]=(double)rate/8.0;						    queueMaxRate[queueNum]=(double)rate/8.0;
  } else {										  } else {
    printf("The queue number is out of range.\n");					    printf("The queue number is out of range.\n");
  }											  }
}											}

/*-----------------------------------------------------------------------------		/*-----------------------------------------------------------------------------
int command(int argc, const char*const* argv)						int command(int argc, const char*const* argv)
    Commands from the ns file are interpreted through this interface.			    Commands from the ns file are interpreted through this interface.
-----------------------------------------------------------------------------*/		-----------------------------------------------------------------------------*/
int dsREDQueue::command(int argc, const char*const* argv) {				int dsREDQueue::command(int argc, const char*const* argv) {
  if (strcmp(argv[1], "configQ") == 0) {						  if (strcmp(argv[1], "configQ") == 0) {
    // modification to set the parameter q_w by Thilo					    // modification to set the parameter q_w by Thilo
    redq_[atoi(argv[2])].config(atoi(argv[3]), argc, argv);				    redq_[atoi(argv[2])].config(atoi(argv[3]), argc, argv);
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "addPHBEntry") == 0) {						  if (strcmp(argv[1], "addPHBEntry") == 0) {
    addPHBEntry(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));				    addPHBEntry(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "meanPktSize") == 0) {						  if (strcmp(argv[1], "meanPktSize") == 0) {
    for (int i = 0; i < MAX_QUEUES; i++)						    for (int i = 0; i < MAX_QUEUES; i++)
      redq_[i].setMPS(atoi(argv[2]));							      redq_[i].setMPS(atoi(argv[2]));
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "setNumPrec") == 0) {						  if (strcmp(argv[1], "setNumPrec") == 0) {
    setNumPrec(atoi(argv[2]));								    setNumPrec(atoi(argv[2]));
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "getAverage") == 0) {						  if (strcmp(argv[1], "getAverage") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    tcl.resultf("%f", redq_[atoi(argv[2])].getWeightedLength());			    tcl.resultf("%f", redq_[atoi(argv[2])].getWeightedLength());
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "getMarkP") == 0) {					      <
    Tcl& tcl = Tcl::instance();							      <
    tcl.resultf("%f", redq_[atoi(argv[2])].getMarkP(atoi(argv[3])));		      <
    return(TCL_OK);								      <
  }										      <
  if (strcmp(argv[1], "getStat") == 0) {						  if (strcmp(argv[1], "getStat") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    tcl.resultf("%f", getStat(argc,argv));						    tcl.resultf("%f", getStat(argc,argv));
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "getCurrent") == 0) {						  if (strcmp(argv[1], "getCurrent") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    tcl.resultf("%f", redq_[atoi(argv[2])].getRealLength()*1.0);			    tcl.resultf("%f", redq_[atoi(argv[2])].getRealLength()*1.0);
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "printStats") == 0) {						  if (strcmp(argv[1], "printStats") == 0) {
    printStats();									    printStats();
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "printWRRcount") == 0) {						  if (strcmp(argv[1], "printWRRcount") == 0) {
    printWRRcount();									    printWRRcount();
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "printPHBTable") == 0) {						  if (strcmp(argv[1], "printPHBTable") == 0) {
    printPHBTable();									    printPHBTable();
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "link") == 0) {							  if (strcmp(argv[1], "link") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    LinkDelay* del = (LinkDelay*) TclObject::lookup(argv[2]);				    LinkDelay* del = (LinkDelay*) TclObject::lookup(argv[2]);
    if (del == 0) {									    if (del == 0) {
      tcl.resultf("RED: no LinkDelay object %s",					      tcl.resultf("RED: no LinkDelay object %s",
		  argv[2]);										  argv[2]);
      return(TCL_ERROR);								      return(TCL_ERROR);
    }											    }
    link_ = del;									    link_ = del;
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "early-drop-target") == 0) {					  if (strcmp(argv[1], "early-drop-target") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    NsObject* p = (NsObject*)TclObject::lookup(argv[2]);				    NsObject* p = (NsObject*)TclObject::lookup(argv[2]);
    if (p == 0) {									    if (p == 0) {
      tcl.resultf("no object %s", argv[2]);						      tcl.resultf("no object %s", argv[2]);
      return (TCL_ERROR);								      return (TCL_ERROR);
    }											    }
    de_drop_ = p;									    de_drop_ = p;
    return (TCL_OK);									    return (TCL_OK);
  }											  }
  if (strcmp(argv[1], "setSchedularMode") == 0) {					  if (strcmp(argv[1], "setSchedularMode") == 0) {
    setSchedularMode(argv[2]);								    setSchedularMode(argv[2]);
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "setMREDMode") == 0) {						  if (strcmp(argv[1], "setMREDMode") == 0) {
    if (argc == 3)									    if (argc == 3)
      setMREDMode(argv[2],0);								      setMREDMode(argv[2],0);
    else										    else
      setMREDMode(argv[2],argv[3]);							      setMREDMode(argv[2],argv[3]);
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "addQueueWeights") == 0) {					  if (strcmp(argv[1], "addQueueWeights") == 0) {
    addQueueWeights(atoi(argv[2]), atoi(argv[3]));					    addQueueWeights(atoi(argv[2]), atoi(argv[3]));
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  if (strcmp(argv[1], "addQueueRate") == 0) {						  if (strcmp(argv[1], "addQueueRate") == 0) {
    addQueueRate(atoi(argv[2]), atoi(argv[3]));						    addQueueRate(atoi(argv[2]), atoi(argv[3]));
    return(TCL_OK);									    return(TCL_OK);
  }											  }
  // Returns the weighted RED queue length for one virtual queue in packets		  // Returns the weighted RED queue length for one virtual queue in packets
  // Added by Thilo									  // Added by Thilo
  if (strcmp(argv[1], "getAverageV") == 0) {						  if (strcmp(argv[1], "getAverageV") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    tcl.resultf("%f",									    tcl.resultf("%f",
		redq_[atoi(argv[2])].getWeightedLength_v(atoi(argv[3])));				redq_[atoi(argv[2])].getWeightedLength_v(atoi(argv[3])));
    return(TCL_OK);									    return(TCL_OK);
  }										      |	  } 
  // Returns the length of one virtual queue, in packets			      |	  // Returns the length of one virtual queue, in packets 
  // Added by Thilo									  // Added by Thilo
  if (strcmp(argv[1], "getCurrentV") == 0) {						  if (strcmp(argv[1], "getCurrentV") == 0) {
    Tcl& tcl = Tcl::instance();								    Tcl& tcl = Tcl::instance();
    tcl.resultf("%f",									    tcl.resultf("%f",
		redq_[atoi(argv[2])].getRealLength_v(atoi(argv[3]))*1.0);				redq_[atoi(argv[2])].getRealLength_v(atoi(argv[3]))*1.0);
    return(TCL_OK);									    return(TCL_OK);
  }											  }

										      <
										      <
  if ((argc == 3) && (strcmp(argv[1], "attach") == 0)) {			      <
        int mode;								      <
        const char* id = argv[2];						      <
	Tcl& tcl = Tcl::instance();						      <
        									      <
        Tcl_Channel ch = Tcl_GetChannel(tcl.interp(), (char*)id,		      <
				  &mode);					      <
        if (ch == 0) {								      <
	        tcl.resultf("trace: can't attach %s for writing", id);		      <
	        return (TCL_ERROR);						      <
        }									      <
										      <
        channel_ = ch;								      <
        createDetailedStats();							      <
        return (TCL_OK);							      <
  }										      <
										      <
  if (strcmp(argv[1], "resetDetailedStats") == 0) {				      <
    clearDetailedStats();							      <
    return(TCL_OK);								      <
  }										      <
										      <
  if (strcmp(argv[1], "dumpDetailedStats") == 0) {				      <
    dumpDetailedStats();							      <
    return(TCL_OK);								      <
  }										      <
										      <
										      <
  return(Queue::command(argc, argv));							  return(Queue::command(argc, argv));
}											}


detailedStatType *dsREDQueue::getDS(int flow_id, int cp)			      <
{										      <
  if (ds == NULL) {								      <
    /* user have not enabled stats for this queue				      <
       so we do not need to keep statistics. 					      <
     */										      <
    return NULL;								      <
  } 										      <
   										      <
   										      <
  if (flow_id >= MAX_FLOWS) {							      <
    printf("***** Too large flow_id. There is no space on ds[][] to keep data for thi <
    return NULL;								      <
  }										      <
  if (cp >= MAX_CP) {								      <
    printf("***** Too large codePt. There is no space on ds[][] to keep data for this <
    return NULL;								      <
  }										      <
  										      <
  return ds + flow_id * MAX_CP + cp;						      <
}										      <
										      <
										      <
void dsREDQueue::createDetailedStats() {					      <
   if (ds == NULL) {								      <
      printf("Enabling detailed stats\n");					      <
      ds = (detailedStatType *) malloc(sizeof(detailedStatType) * MAX_CP * MAX_FLOWS) <
   }										      <
}   										      <
										      <
void dsREDQueue::clearDetailedStats() {						      <
   detailedStatType *dsp;							      <
										      <
   if (!ds)									      <
     return;									      <
										      <
   memset(ds, 0x00, sizeof(detailedStatType) * MAX_CP * MAX_FLOWS);		      <
   for (int i=0; i<MAX_FLOWS; i++)						      <
      for (int j=0; j<MAX_CP; j++) {						      <
         dsp = getDS(i, j);							      <
         if (dsp)								      <
           dsp->last_reset = Scheduler::instance().clock();			      <
      }										      <
}										      <
										      <
										      <
void dsREDQueue::dumpDetailedStats() {						      <
   struct detailedStatType *dsp;						      <
   struct detailedStatType ds;							      <
   char buff[1024*100];								      <
   char tmp[1024*10];								      <
										      <
   if (channel_ == 0)								      <
      return;									      <
										      <
   memset(&ds, 0x00, sizeof(ds));						      <
   for (int flow=0; flow<MAX_FLOWS; flow++) {					      <
      for (int cp=0; cp<MAX_CP; cp++) {						      <
         dsp = getDS(flow, cp);							      <
										      <
         if (dsp->p_arr != 0) 							      <
            ds.qdelay = ((ds.qdelay * ds.p_enq) + (dsp->qdelay * dsp->p_enq)) / (ds.p <
         ds.p_arr += dsp->p_arr;						      <
         ds.b_arr += dsp->b_arr;						      <
         ds.p_enq += dsp->p_enq;						      <
         ds.b_enq += dsp->b_enq;						      <
         ds.p_deq += dsp->p_deq;						      <
         ds.b_deq += dsp->b_deq;						      <
         ds.p_mark += dsp->p_mark;						      <
         ds.b_mark += dsp->b_mark;						      <
         ds.p_drop += dsp->p_drop;						      <
         ds.b_drop += dsp->b_drop;						      <
         ds.p_edrop += dsp->p_edrop;						      <
         ds.b_edrop += dsp->b_edrop;						      <
      }      									      <
   }										      <
										      <
   sprintf(buff, "%.1f\ta\ta\t%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t" <
         Scheduler::instance().clock(),						      <
         ds.qdelay,								      <
         ds.p_arr, ds.b_arr, 							      <
         ds.p_enq, ds.b_enq, 							      <
         ds.p_deq, ds.b_deq, 							      <
         ds.p_mark, ds.b_mark, 							      <
         ds.p_drop, ds.b_drop, 							      <
         ds.p_edrop, ds.b_edrop);						      <
										      <
										      <
   for (int flow=0; flow<MAX_FLOWS; flow++) {					      <
      memset(&ds, 0x00, sizeof(ds));						      <
      for (int cp=0; cp<MAX_CP; cp++) {						      <
         dsp = getDS(flow, cp);							      <
         									      <
         if (dsp->p_arr != 0) 							      <
            ds.qdelay = ((ds.qdelay * ds.p_enq) + (dsp->qdelay * dsp->p_enq)) / (ds.p <
         ds.p_arr += dsp->p_arr;						      <
         ds.b_arr += dsp->b_arr;						      <
         ds.p_enq += dsp->p_enq;						      <
         ds.b_enq += dsp->b_enq;						      <
         ds.p_deq += dsp->p_deq;						      <
         ds.b_deq += dsp->b_deq;						      <
         ds.p_mark += dsp->p_mark;						      <
         ds.b_mark += dsp->b_mark;						      <
         ds.p_drop += dsp->p_drop;						      <
         ds.b_drop += dsp->b_drop;						      <
         ds.p_edrop += dsp->p_edrop;						      <
         ds.b_edrop += dsp->b_edrop;						      <
      }      									      <
										      <
      if (ds.p_arr == 0)							      <
         continue;								      <
										      <
      sprintf(tmp, "%d\ta\t%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",   <
         flow, 									      <
         ds.qdelay,								      <
         ds.p_arr, ds.b_arr, 							      <
         ds.p_enq, ds.b_enq, 							      <
         ds.p_deq, ds.b_deq, 							      <
         ds.p_mark, ds.b_mark, 							      <
         ds.p_drop, ds.b_drop, 							      <
         ds.p_edrop, ds.b_edrop);						      <
      strcat(buff, tmp);							      <
   }										      <
										      <
   for (int cp=0; cp<MAX_CP; cp++) {						      <
      memset(&ds, 0x00, sizeof(ds));						      <
      for (int flow=0; flow<MAX_FLOWS; flow++) {				      <
         dsp = getDS(flow, cp);							      <
         									      <
         if (dsp->p_arr != 0) 							      <
            ds.qdelay = ((ds.qdelay * ds.p_enq) + (dsp->qdelay * dsp->p_enq)) / (ds.p <
         ds.p_arr += dsp->p_arr;						      <
         ds.b_arr += dsp->b_arr;						      <
         ds.p_enq += dsp->p_enq;						      <
         ds.b_enq += dsp->b_enq;						      <
         ds.p_deq += dsp->p_deq;						      <
         ds.b_deq += dsp->b_deq;						      <
         ds.p_mark += dsp->p_mark;						      <
         ds.b_mark += dsp->b_mark;						      <
         ds.p_drop += dsp->p_drop;						      <
         ds.b_drop += dsp->b_drop;						      <
         ds.p_edrop += dsp->p_edrop;						      <
         ds.b_edrop += dsp->b_edrop;						      <
      }      									      <
										      <
      if (ds.p_arr == 0)							      <
         continue;								      <
										      <
      sprintf(tmp, "a\t%d\t%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",   <
         cp,									      <
         ds.qdelay,								      <
         ds.p_arr, ds.b_arr, 							      <
         ds.p_enq, ds.b_enq, 							      <
         ds.p_deq, ds.b_deq, 							      <
         ds.p_mark, ds.b_mark, 							      <
         ds.p_drop, ds.b_drop, 							      <
         ds.p_edrop, ds.b_edrop);						      <
      strcat(buff, tmp);							      <
   }										      <
										      <
   for (int flow=0; flow<MAX_FLOWS; flow++) {					      <
      for (int cp=0; cp<MAX_CP; cp++) {						      <
         dsp = getDS(flow, cp);							      <
         									      <
         if (dsp->p_arr == 0)							      <
            continue;								      <
										      <
         sprintf(tmp, "%d\t%d\t%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t <
            flow, cp,								      <
            dsp->qdelay,							      <
            dsp->p_arr, dsp->b_arr, 						      <
            dsp->p_enq, dsp->b_enq, 						      <
            dsp->p_deq, dsp->b_deq, 						      <
            dsp->p_mark, dsp->b_mark, 						      <
            dsp->p_drop, dsp->b_drop, 						      <
            dsp->p_edrop, dsp->b_edrop);					      <
         strcat(buff, tmp);							      <
      }										      <
   }										      <
   										      <
   strcat(buff, "\n");								      <
   Tcl_Write(channel_, buff, strlen(buff));					      <
}										      <
										      <
