//===================================================== file = ALR_time.c =====
//=  A CSIM simulation of an adaptive service time M/M/1 queue                =
//=    - Implements ALR dual-threshold policy with timeout based oscillation  =
//=      damping                                                              =
//=============================================================================
//=  Notes:                                                                   =
//=   1. Can either generate Poisson arrivals or read arrivals from trace     =
//=   2. Buffer size can be set in either packets or bytes                    =
//=   3. The following parameters are given in the command line               =
//=       - Time to switch data rates (in seconds)                            =
//=       - Initial time in high rate before switching to low rate (seconds)  =
//=       - Minimum time needed to stay in low rate (seconds)                 =
//=       - Target utilization for GEN_TYPE == POISSON. This input is         =
//=         required but ignored when GEN_TYPE == FILE.                       =
//=       - Trace file with arrivals (only for GEN_TYPE == FILE )             =
//=                                                                           =
//=  To use generated Poisson arrivals                                        =
//=   1. Set GEN_TYPE == POISSON                                              =
//=   2. Arrival (lambda) and low service rate (mu) are set in main()         =
//=   3. High service rate is mu * MAX_RATE (the rate multiplier)             =
//=                                                                           =
//=  To use arrivals read-in from traces                                      =
//=   1. Set GEN_TYPE == FILE                                                 =
//=   2. Set FILE_FORMAT to appropriate file format                           =
//=   3. If trace does not have packet sizes, set PKT_SIZE to required value  =
//=   4. Set DATA_RATE to the required value (in bits per second)             =
//=                                                                           =
//=  To use packet based buffers:                                             =
//=   1. Uncomment #define BUFFER_PKT                                         =
//=   2. Set THRESH_MIN_PKT and THRESH_MAX_PKT                                =
//=                                                                           =
//=  To use byte based buffers:                                               =
//=   1. Comment out #define BUFFER_PKT                                       =
//=   2. Set BUFFER_SIZE to maximum buffer size (in bytes)                    =
//=   3. Set THRESH_MIN_KBYTE and THRESH_MAX_KBYTE (in kilo bytes)            =
//=                                                                           =
//=---------------------------------------------------------------------------=
//= Example input:                                                            =
//=   Packet based buffers: THRESH_MIN_PKT = 1, THRESH_MAX_PKT = 30           =
//=   GEN_TYPE == POISSON                                                     =
//=   T_switch = 0.0, T_timeOut = 0.010, T_minLow = 0.010,                    =
//=   Target_Util = 0.40 (40% utilization)                                    =
//=   mu = 8333.3333 (assuming mean - not fixed - 1500 byte packet size)      =
//=   MAX_ARRIVALS = 10.0e6                                                   =
//=   MAX_T_TIMEOUT = 100s                                                    =
//=---------------------------------------------------------------------------=
//= Example execution:                                                        =
//=   === BEGIN SIMULATION ===                                                =
//=   ============================================ ALR_time.c =======         =
//=   == *** CSIM simulation of an adaptive service time queue *** ==         =
//=   ===============================================================         =
//=   = >>> TIMEOUT-THRESHOLD POLICY                                          =
//=   = >>> ADAPTIVE TIMEOUT ENABLED                                          =
//=   = >>> Poisson                                                           =
//=   = lambda             = 33333.333200 cust/sec                            =
//=   = mu                 = 8333.333300 cust/sec                             =
//=   = Target_Util        = 40.000000 %                                      =
//=   = SIM_TIME           = 10000.000000 sec                                 =
//=   = T_switch           = 0.000000 sec                                     =
//=   = DATA_RATE (low)    = 100.00 Mb/s                                      =
//=   = MAX_RATE           = 10                                               =
//=   = Thresh_max         = 30 pkts                                          =
//=   = Thresh_min         = 1 pkts                                           =
//=   = T_timeOut (Orig)   = 0.010000 sec                                     =
//=   = MAX_T_TIMEOUT      = 100.000000 sec                                   =
//=   = T_minLow           = 0.010000 sec                                     =
//=   = P_FIX              = 0.000000 Watts                                   =
//=   = P_LOW              = 1.500000 Watts                                   =
//=   = P_HIGH             = 4.000000 Watts                                   =
//=   ===============================================================         =
//=   = Total CPU time     = 29.047000 sec                                    =
//=   = Total sim time     = 300.137620 sec                                   =
//=   = Total completions  = 10000001 cust                                    =
//=   =--------------------------------------------------------------         =
//=   = Server utilization = 39.979013 %                                      =
//=   = Mean num in system = 0.668038 cust                                    =
//=   = Mean response time = 0.000020 sec 0.020050 ms                         =
//=   = Mean service time  = 0.000012 sec 0.011999 ms                         =
//=   = Mean throughput    = 33318.052542 cust/sec                            =
//=   = Max queue length   = 46 pkts                                          =
//=   = Total service time = 119.992059 sec                                   =
//=   = Ideal low time     = 300.137620 sec                                   =
//=   = Avg burst duration = 0.000000 sec                                     =
//=   =--------------------------------------------------------------         =
//=   = Time in low rate      = 0.020939 sec   0.01 %                         =
//=   = Time in high rate     = 300.116681 sec         99.99 %                =
//=   = Avg time in low rate  = 0.001396 sec                                  =
//=   = Avg time in high rate = 20.007779 sec                                 =
//=   = Num rate switches     = 29                                            =
//=   =--------------------------------------------------------------         =
//=   = alpha metric       = 1.000044                                         =
//=   = gamma metric       = 1.000044                                         =
//=   ===============================================================         =
//=                                                                           =
//=---------------------------------------------------------------------------=
//=  Build: bcc32 -w-pro ALR_time.c csim19.obj csim19.lib                     =
//=---------------------------------------------------------------------------=
//=  Execute: ALR_time T_switch T_timeOut T_minLow Target_Util < in.dat       =
//=           T_switch    - time to switch data rates (seconds)               =
//=           T_timeOut   - initial time in high rate before switching value (seconds) =
//=           T_minLow    - minimum time needed to stay in low rate (seconds)=
//=           Target_Util - target utilization (used in GEN_TYPE == POISSON)  =
//=           in.dat      - input trace file (used in GEN_TYPE == FILE)       =
//=---------------------------------------------------------------------------=
//=  Author: Chamara Gunaratne                                                =
//=          University of South Florida                                      =
//=          Email: pgunarat@csee.usf.edu                                     =
//=---------------------------------------------------------------------------=
//=  History: KJC (06/14/05) - Genesis (from adaptive5.c)                     =
//=               Aug 25, 2005 - Modified from adaptive5.c                    =
//=               Sep 26, 2005 - Modified from adaptive6.c                    =
//=               Sep 26, 2005 - Added byte based buffers                     =
//=               Oct 18, 2005 - Changed variable names                       =
//=               Dec 11, 2005 - Modified from adaptive7.c                    =
//=               Dec 11, 2005 - Added low threshold as well as high          =
//=               Mar 18, 2006 - Removed a lot of junk and cleaned up code    =
//=               Jul 28, 2006 - Modified from adaptive8.c                    =
//=               Jul 28, 2006 - Added timeout based oscillation damping      =
//=               Aug 28, 2006 - Added BURST file format                      =
//=               Dec 25, 2006 - Renamed ALR_time.c from adaptive10.c         =
//=               Jan 13, 2007 - Some minor modifications                     =
//=============================================================================
//----- Includes --------------------------------------------------------------
#include <stdio.h>              // Needed for printf()
#include <stdlib.h>             // needed for atof()
#include <math.h>               // needed for atof()
#include <assert.h>             // Needed for assert() macro
#include "csim.h"               // Needed for CSIM18 stuff

//----- Defines ---------------------------------------------------------------
#define FALSE                0  // Boolean false
#define TRUE                 1  // Boolean true

#define POISSON              0  // Poisson generator
#define FILE                 1  // Read in from trace file

#define USF                  0  // read in USF trace files - file format 
#define PSU                  1  // read in PSU trace files - file format
#define TIME_ONLY            2  // read in trace files with timestamp only (s)
#define BURST                3  // read in trace files for single burst

#define BUFFER_PKT              // Switch buffer to packet mode from byte mode
#define ENABLE_ADAPTIVE_TIMEOUT // Enable/disable adaptive increase of T_timeOut

#define FILE_FORMAT        USF  // time - s, packet size - bytes
//#define FILE_FORMAT        PSU  // time - ms, packet size - zeroes
//#define FILE_FORMAT  TIME_ONLY  // time - s, packet size - NA
//#define FILE_FORMAT      BURST  // time - s, packet size - bytes, packet type

//#define GEN_TYPE       POISSON  // Generator type
#define GEN_TYPE          FILE  // Generator type

#define SIM_TIME        10.0e3  // Total simulation time in seconds
#define MAX_ARRIVALS    10.0e6  // Maximum number of arrivals (for Poisson)
#define PKT_SIZE          1518  // pkt size for traces without pkt size
#define DATA_RATE         10e6  // low service rate of queue in bits/s
#define MAX_RATE            10  // Rate multiplier
#define BUFFER_SIZE      32768  // buffer size in bytes - 32KB
#define MAX_T_TIMEOUT      100  // Maximum T_timeOut value in seconds

#define THRESH_MIN_PKT      15  // Min threshold (k1) in packets (must be > 0)
#define THRESH_MAX_PKT      30  // Max threshold (k2) in packets 
#define THRESH_MIN_KBYTE     0  // Min threshold (k1) in kilo bytes
#define THRESH_MAX_KBYTE    32  // Max threshold (k2) in kilo bytes

#define RATE_SAMPLE_TIME 0.200  // rate sampling interval for printing rate

#define U_THRESH          0.05  // Low utilization threshold
#define P_FIX              0.0  // Power consumption fixed
#define P_LOW              1.5  // Power consumption low data rate
#define P_HIGH             4.0  // Power consumption high data rate

//----- Globals ---------------------------------------------------------------
FACILITY Server;                // CSIM facility for server
EVENT    Done;                  // CSIM event for trace is done
TABLE    Table_rate1;           // CSIM table for time in low rate
TABLE    Table_rateMax;         // CSIM table for time in high rate
TABLE    Table_delay;           // CSIM table for response times
TABLE    Table_burst_time;      // CSIM table for recording burst duration

long int Rate_switch_count;     // Rate switch counter
int      Rate_index;            // Rate index
double   Rate_switch_time;      // Time of last rate switch
int      Rate_switch_flag;      // Indicate that rate switch was done
double   Last_low_to_high_switch_time;  // Last low-to-high switch time
double   Last_high_to_low_switch_time;  // Last high-to-low switch time
int      Util_switch_flag;      // Utilization switch flag
long int Max_queue_length;      // Maximum recorded queue length
double   Total_serv_time;       // Total time spent servicing arrivals
long int Buffer_size;           // Current buffer size - in bytes
double   Ideal_low_time;        // Ideal low time with no delay impact
double   T_timeOut_orig;        // Original time out value

long int Thresh_min;            // Minimum threshold level (byte or pkt)
long int Thresh_max;            // Maximum threshold level (byte or pkt)

// these variables are accepted from the command line
double   T_switch;              // Time to switch between rates (sec)
double   Target_Util;           // Targeted utilization level (Poisson only)
double   T_timeOut;             // Timeout interval after low-to-high switch
double   T_minLow;              // Minimum time in low rate 

//----- Prototypes ------------------------------------------------------------
void gen_poisson(double lambda, double mu);   // Generator
void gen_file();                              // Generator
void queue1(double service_time, double pkt_size, int pkt_type, double start_time); // Queue
void switch_to_low();           // Switch from high to low rate
void print_rate(void);          // periodically sample and print the data rate

//=============================================================================
//==  Main program                                                           ==
//=============================================================================
void sim(int argc, char *argv[])
{
  double   lambda;       // Mean arrival rate (cust/sec)
  double   mu;           // Mean service rate (cust/sec)
  double   e_norm;       // Energy used if only high data rate
  double   e_save;       // Energy used in savings mode
  double   alpha_metric; // Singh energy metric (E/Es)
  double   gamma_metric; // Green energy metric (E/Es)(D/Ds)
  
  // sanity check
  if (argc != 5) {
    printf("ERROR: Program missing parameter \n");
    exit(1);
    }  

  // update T_switch and other parameters from command line
  T_switch    = (double)atof(argv[1]);
  T_timeOut   = (double)atof(argv[2]);
  T_minLow    = (double)atof(argv[3]);
  Target_Util = (double)atof(argv[4]);
  
  // debug
  //printf("T_switch: %f T_timeOut: %f T_minLow: %f Target_Util: %f \n", \
    T_switch, T_timeOut, T_minLow, Target_Util);

  // Set the maximum and minimum buffer thresholds
  #ifdef BUFFER_PKT   // if counting buffer in packets
  // Set the minimum threshold
  Thresh_min = THRESH_MIN_PKT;
  // Set the maximum threshold
  Thresh_max = THRESH_MAX_PKT;
  #else                 // if counting buffer in bytes
  // Set the minimum threshold
  Thresh_min = (int)((double)THRESH_MIN_KBYTE * 1024.0);
  // Set the maximum threshold
  Thresh_max = (int)((double)THRESH_MAX_KBYTE * 1024.0);
  // sanity check
  assert(Thresh_max <= BUFFER_SIZE);
  #endif
  
  // sanity check - check that max is < min 
  assert(Thresh_min < Thresh_max);
 
  // Create the simulation
  create("sim");

  // CSIM initializations
  Server = facility("Server for single-server queue");
  Table_rate1 = table("Rate index 1 time table");
  Table_rateMax = table("Rate index RATE_MAX time table");
  Table_delay = table("Response time delays");
  Table_burst_time = table("Burst durations");
  Done = event("Done");
  max_processes((long)100000 );
  
  // Variable initializations
  Rate_index = 1;  // start in low rate 
  Rate_switch_count = 0;
  Rate_switch_time = 0.0;
  Rate_switch_flag = FALSE;
  Last_low_to_high_switch_time = 0.0;
  Last_high_to_low_switch_time = 0.0;
  Max_queue_length = 0;
  Total_serv_time = 0.0;
  Buffer_size = 0;
  Ideal_low_time = 0.0;
  T_timeOut_orig = T_timeOut;

  // Parameter initializaitons - for Poisson
  mu = 8333.3333;   // For 1500 byte packets
  // lambda as a percentage of mu
  lambda = (double)mu * (double)MAX_RATE * (double)Target_Util; 
  // lambda as a percentage of 1 Gb/s data rate with 1500 byte pkts - no mu
  //lambda = (double)83333.3333 * (double)Target_Util;
  
  // Output begin-of-simulation banner
  printf("=== BEGIN SIMULATION === \n");

  // Initiate the rate printing process (for graphing data only)
  //print_rate();
  // Initate the high-to-low rate switching process
  switch_to_low();
  
  // Initiate appropriate generate function
  if (GEN_TYPE == POISSON)
    gen_poisson(lambda, mu);
  else if (GEN_TYPE == FILE)
    gen_file();

  // Hold for simulation time
  if (GEN_TYPE == POISSON)
    //hold(SIM_TIME);
    wait(Done);
  else if (GEN_TYPE == FILE)
    wait(Done);

  // Make last update to rate tables
  if (Rate_index == 1)
    record((clock - Rate_switch_time), Table_rate1);
  else if (Rate_index == MAX_RATE)
    record((clock - Rate_switch_time), Table_rateMax);

  // Compute Singh energy metric
  e_norm = clock * (P_FIX + P_HIGH);
  e_save = clock * P_FIX;
  e_save = e_save + (table_sum(Table_rate1) * P_LOW);
  e_save = e_save + (table_sum(Table_rateMax) * P_HIGH);
  alpha_metric = e_norm / e_save;

  // Compute Green energy metric  - not used in this program
  /*if (resp(Server) <= D_BOUND)
    gamma_metric = alpha_metric;
  else
    gamma_metric = alpha_metric * (D_BOUND / resp(Server));*/
  gamma_metric = alpha_metric;

  // Output results
  printf("============================================ ALR_time.c ======= \n");
  printf("== *** CSIM simulation of an adaptive service time queue *** == \n");
  printf("=============================================================== \n");
  printf("= >>> TIMEOUT-THRESHOLD POLICY       \n");
  #ifdef ENABLE_ADAPTIVE_TIMEOUT
    printf("= >>> ADAPTIVE TIMEOUT ENABLED       \n");
  #else
    printf("= >>> ADAPTIVE TIMEOUT DISABLED      \n");
  #endif  
  if (GEN_TYPE == POISSON)
  {
    printf("= >>> Poisson                      \n");
    printf("= lambda             = %f cust/sec \n", lambda);
    printf("= mu                 = %f cust/sec \n", mu);
    printf("= Target_Util        = %f %% \n", (double)Target_Util * 100);
  }
  if (GEN_TYPE == FILE)
  {
    printf("= >>> FILE                         \n");
  }
  printf("= SIM_TIME           = %f sec      \n", SIM_TIME);
  printf("= T_switch           = %f sec      \n", T_switch);
  printf("= DATA_RATE (low)    = %.2f Mb/s   \n", (double)DATA_RATE/1e6);  
  printf("= MAX_RATE           = %d          \n", MAX_RATE);  
  #ifdef BUFFER_PKT
  printf("= Thresh_max         = %d pkts     \n", Thresh_max);
  printf("= Thresh_min         = %d pkts     \n", Thresh_min);
  #else
  printf("= BUFFER_SIZE        = %d KB       \n", (BUFFER_SIZE/1024) );  
  printf("= Thresh_max         = %d KB       \n", (Thresh_max/1024) );
  printf("= Thresh_min         = %d KB       \n", (Thresh_min/1024) );
  #endif
  printf("= T_timeOut (Orig)   = %f sec      \n", (double)T_timeOut_orig);
  printf("= MAX_T_TIMEOUT      = %f sec      \n", (double)MAX_T_TIMEOUT);
  printf("= T_minLow           = %f sec      \n", (double)T_minLow);
  printf("= P_FIX              = %f Watts    \n", P_FIX);
  printf("= P_LOW              = %f Watts    \n", P_LOW);
  printf("= P_HIGH             = %f Watts    \n", P_HIGH);
  printf("=============================================================== \n");
  printf("= Total CPU time     = %f sec      \n", cputime());
  printf("= Total sim time     = %f sec      \n", clock);
  printf("= Total completions  = %d cust     \n", completions(Server));
  printf("=-------------------------------------------------------------- \n");
  printf("= Server utilization = %f %%       \n", 100.0 * util(Server));
  printf("= Mean num in system = %f cust     \n", qlen(Server));
  printf("= Mean response time = %f sec %f ms\n", resp(Server), resp(Server) * 1000);
  printf("= Mean service time  = %f sec %f ms\n", serv(Server), serv(Server) * 1000);
  printf("= Mean throughput    = %f cust/sec \n", tput(Server));
  #ifdef BUFFER_PKT  
  printf("= Max queue length   = %d pkts     \n", Max_queue_length);
  #else  
  printf("= Max queue length   = %d bytes    \n", Max_queue_length);
  #endif  
  printf("= Total service time = %f sec      \n", Total_serv_time);
  printf("= Ideal low time     = %f sec      \n", Ideal_low_time);  
  printf("= Avg burst duration = %f sec      \n", table_mean(Table_burst_time) );
  printf("=-------------------------------------------------------------- \n");
  printf("= Time in low rate      = %f sec \t %.2f %% \n", \
            table_sum(Table_rate1), table_sum(Table_rate1) * 100.0/clock);
  printf("= Time in high rate     = %f sec \t %.2f %% \n", \
            table_sum(Table_rateMax), table_sum(Table_rateMax) * 100.0/clock);
  printf("= Avg time in low rate  = %f sec   \n", table_mean(Table_rate1) );
  printf("= Avg time in high rate = %f sec   \n", table_mean(Table_rateMax) );
  printf("= Num rate switches     = %d       \n", Rate_switch_count);
  printf("=-------------------------------------------------------------- \n");
  printf("= alpha metric       = %f          \n", alpha_metric);
  printf("= gamma metric       = %f          \n", gamma_metric);
  printf("=============================================================== \n");
}

//=============================================================================
//==  Process to generate Poisson customers                                  ==
//=============================================================================
void gen_poisson(double lambda, double mu)
{
  int       out_id;                 // To (destination) outport number
  double    z;                      // Random number between 0.0 and 1.0
  double    inter_arrival_time;     // Inter arrival time
  double    service_time;           // Service time for this customer
  int       pkt_size = 0;           // pkt size
  double    trace_time = 0.0;       // total time in trace
  double    temp1;  
  unsigned long int   counter = 0;  // count up to exit 
  
  create("gen_poisson");
  
  // Packet generation loop
  while(TRUE)
  {
    // Hold for interarrival time
    inter_arrival_time = expntl(1.0 / lambda);
    hold(inter_arrival_time);

    // increment the running trace time value
    trace_time = trace_time + inter_arrival_time;

    // Compute ideal low time
    temp1 = inter_arrival_time - ((double)T_switch * 2.0);
    if (temp1 > 0)
      Ideal_low_time = Ideal_low_time + temp1;

    // service time is exponentially distributed
    service_time = exponential(1.0 / mu);
    
    // if generating service times using average packet sizes
    //pkt_size = (int)expntl((double)1500.00);
    //service_time = (pkt_size * 8) / DATA_RATE;
    
    // increment the input buffer size
    Buffer_size = Buffer_size + (long int)pkt_size;

    // debug
    //printf("clock: %f enqueuing packet. pkt_size: %d Buffer_size: %d \n", \
           clock, pkt_size, (int)Buffer_size );
    
    // enqueue packet
    queue1(service_time, (double)pkt_size, (int)1, (double)clock);
    
    // debug
    //printf("clock: %f trace_time: %f pkt_size: %d service_time: %f\n", \
           clock, trace_time, pkt_size, service_time);
    
    counter = counter + 1;
    if ((double)counter > (double)MAX_ARRIVALS)
      break;
  }
  set(Done);
}

//=============================================================================
//==  Process to read trace file and generate customers                      ==
//=============================================================================
void gen_file()
{
  char      seps[]   = " \t,";    // possible token separators
  char      *token;               // temporary token holder
  char      instring[1000];       // for reading in data from stdin

  double    service_time;         // Service time for this customer
  double    inter_arrival_time;   // pkt inter arrival time
  double    pkt_size;             // pkt size
  int       pkt_type = 0;         // Packet type in burst
  double    trace_time = 0.0;     // total time in trace
  double    temp1;                // Temporary variable

  create("gen_file");

  // Loop forever to create customers
  while(1)
  {
    // read in the data line from file
    gets(instring);
    // if end-of-file, exit loop
    if (feof(stdin)) 
      break;
    // check for comments
    if ((instring[0] == '#') || (instring[0] == '&'))
      continue;
    
    // for traces with arrival time in seconds and pkt size in bytes
    if (FILE_FORMAT == USF)
    {
      token               = (char *)strtok(instring, seps); 
      inter_arrival_time  = (double)atof(token);
      token               = (char *)strtok(NULL, seps); 
      pkt_size            = (double)atof(token);
    }
    // For Singh's traces - arrival time is in us, no pkt size
    else if (FILE_FORMAT == PSU)
    {
      token               = (char *)strtok(instring, seps); 
      inter_arrival_time  = (double)atof(token) / 1e6;  
      pkt_size            = PKT_SIZE;
    }
    // For traces with arrival time only is seconds, no pkt size
    else if (FILE_FORMAT == TIME_ONLY)
    {
      token               = (char *)strtok(instring, seps); 
      inter_arrival_time  = (double)atof(token);  
      pkt_size            = (double)PKT_SIZE;
    }
    // for traces with arrival time in seconds, pkt size in bytes and pkt type
    else if (FILE_FORMAT == BURST)
    {
      token               = (char *)strtok(instring, seps); 
      inter_arrival_time  = (double)atof(token);
      token               = (char *)strtok(NULL, seps); 
      pkt_size            = (double)atof(token);
      token               = (char *)strtok(NULL, seps); 
      pkt_type            = (int)atoi(token);
    }

    // sanity check - sometimes negative numbers exist in delta times
    if (inter_arrival_time < 0)
      inter_arrival_time = 0.0;
   
    // Compute ideal low time
    temp1 = inter_arrival_time - ((double)T_switch * 2.0);
    if (temp1 > 0)
      Ideal_low_time = Ideal_low_time + temp1;

    // increment the running trace time value
    trace_time = trace_time + inter_arrival_time;
    // calculate the service time for packet at 'low' data rate
    service_time = (pkt_size * 8) / DATA_RATE;

    // hold for arrival time 
    hold(inter_arrival_time);
    // increment the input buffer size
    Buffer_size = Buffer_size + (long int)pkt_size;

    // enqueue packet
    queue1(service_time, pkt_size, pkt_type, (double)clock);
    
    // debug
    //printf("clock: %.3f trace_time: %.3f inter_arrival_time: %.6f pkt_size: %.1f pkt_type: %d service_time: %.10f\n", \
           clock, trace_time, inter_arrival_time, pkt_size, pkt_type, service_time);
  }
  set(Done);
}

//=============================================================================
//==  Process for single server queue with adaptive rate change              ==
//=============================================================================
void queue1(double service_time, double pkt_size, int pkt_type, double start_time)
{
  double t_queue;                     // for computing queueing time
  double t_response;                  // for computing response time
  static double t_last_depart = 0.0;  // for computing inter-departure CoV
  static double t_burst_start = 0.0;  // for computing burst duration
  
  create("queue1");
 
  // update max queue length
  #ifdef BUFFER_PKT
  if (qlength(Server) > Max_queue_length)
    Max_queue_length = qlength(Server);
  #else  
  if (Buffer_size > Max_queue_length)
    Max_queue_length = Buffer_size;
  #endif

  // identify the beginning of a burst
  if ((FILE_FORMAT == BURST) && (pkt_type == 0))
    t_burst_start = clock;

  // debug
  //printf("clock: %f enqueuing packet. pkt_size: %d Buffer_size: %d \n", clock,\
        (int)pkt_size, (int)Buffer_size );

  // Reserve the server
  reserve(Server);
  
  // Check for need for a switch delay if rate has been decreased
  if (Rate_switch_flag == TRUE)
  {
    assert(Rate_index == 1);
    // hold for any leftover T_switch time
    if ((clock - Rate_switch_time) <= T_switch)
      hold( T_switch - (clock - Rate_switch_time) );
    Rate_switch_flag = FALSE;
  }
  
  // Increase the rate if queue length is at threshold (>=)
  #ifdef BUFFER_PKT
  if ( (qlength(Server) >= (Thresh_max - 1) ) && (Rate_index == 1))
  #else
  if ( (Buffer_size >= Thresh_max) && (Rate_index == 1))
  #endif
  {
    // debug
    //printf("clock: %f --- changed to higher data rate. qlength(Server): %d Low rate time: %.10f \n", \
          clock, (int)qlength(Server), (clock - Rate_switch_time) );
          
    // hack - this assert triggers on equal - it should trigger on < only
    if (clock > T_switch)
      assert( (clock - Rate_switch_time) >= (T_switch * 0.999999) );
    record((clock - Rate_switch_time), Table_rate1);
    Rate_index = MAX_RATE;
    Rate_switch_time = clock;
    Rate_switch_count++;
    Last_low_to_high_switch_time = clock;

    // If adaptive timeout is enabled
    #ifdef ENABLE_ADAPTIVE_TIMEOUT
    // increase the T_timeOut value if not enough time in low rate
    if ( (clock - Last_high_to_low_switch_time) < T_minLow)
      T_timeOut = T_timeOut * 2;
    else
      T_timeOut = T_timeOut_orig;

    // debug
    //printf("clock: %f ---  T_timeOut: %f T_minLow: %f low time: %f \n", \
        clock, T_timeOut, T_minLow, (clock - Last_high_to_low_switch_time));
    
    // bounds check
    if (T_timeOut > (double)MAX_T_TIMEOUT) 
      T_timeOut = (double)MAX_T_TIMEOUT;

    // debug
    //printf("clock: %f ---  T_timeOut: %f \n", clock, T_timeOut );
    #endif  // -- end of adaptive timeout mechanism
      
    // hold for rate switching time
    hold(T_switch);
  } // -- end of low-to-high rate switch

  // compute the queueing time
  t_queue = (double)clock - start_time;
  // Service the customer
  hold(service_time / Rate_index);
  // compute the response time (queueing time + processing time)
  t_response = (double)clock - start_time;
  // record the delay time in table for intermediate calculations
  tabulate(Table_delay, t_response);

  // print the data for calculating 99% delay and inter-departure CoV
  //printf("t_queue: %.10f t_response: %.10f \n", t_queue, t_response);
  //printf("t_inter_packet: %.10f \n", (clock - t_last_depart) );

  // update last departure time - for calculating inter-departure CoV
  t_last_depart = clock;
  
  // decrement the input buffer and sanity check
  Buffer_size = Buffer_size - (int)pkt_size;
  assert(Buffer_size >= 0);

  // debug
  //printf("clock: %f service_time: %f Rate_index: %d \n", clock, \
         service_time, Rate_index);
  //printf("clock: %f dequeueed packet. pkt_size: %d Buffer_size: %d \n", \
        clock, (int)pkt_size, (int)Buffer_size);

  // compute total service time
  Total_serv_time = Total_serv_time + (service_time / Rate_index);

  // identify and record the duration of a burst 
  if ((FILE_FORMAT == BURST) && (pkt_type == 2))
    tabulate(Table_burst_time, (clock - t_burst_start) );

  // Release the server
  release(Server);
}

//=============================================================================
//== Process for switching data rate from high to low                        ==
//=============================================================================
void switch_to_low()
{
  int num_in_sys;
  
  create("switch_to_low");
 
  // Queue length sampling loop
  while(1)
  {
    // hold for checking interval
    hold((double)0.001);    
    
    // compute the number in the system
    num_in_sys = qlength(Server) + num_busy(Server);
    
    // Reduce the rate if buffer size is less and timeout time has elapsed
    #ifdef BUFFER_PKT
    if ( (Rate_index == MAX_RATE) && (num_in_sys < Thresh_min) )
    #else
    if ( (Rate_index == MAX_RATE) && (Buffer_size <= Thresh_min) )
    #endif
    {
      if ( (clock - Last_low_to_high_switch_time) > T_timeOut )
      {
        record((clock - Rate_switch_time), Table_rateMax);
        Rate_index = 1;
        Rate_switch_time = clock;
        Rate_switch_count++;
        Rate_switch_flag = TRUE;
        Last_high_to_low_switch_time = clock;
        // debug
        //printf("clock: %f - changed to lower rate. Buffer_size: %d num_in_sys: %d \n", \
            clock, Buffer_size, num_in_sys);
      }
    }
  } // -- end of while loop
}

//=============================================================================
//== Process for printing the data rate (for graphing purposes)              ==
//=============================================================================
void print_rate(void)
{
  create("print_rate");

  // rate printing loop
  while(1)
  {
    // Hold for a RATE_SAMPLE_TIME
    hold(RATE_SAMPLE_TIME);
    if (Rate_index == MAX_RATE)
      printf("%f %f %f print_rate\n", clock, (double)0, (double)1.0);
    else if (Rate_index == 1)
      printf("%f %f %f print_rate\n", clock, (double)1.0, (double)0);

  }
}

