//===================================================== file = ALR_util.c =====
//=  A CSIM simulation of an adaptive service time M/M/1 queue                =
//=    - Implements ALR utilization-threshold policy                          =
//=============================================================================
//=  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)                            =
//=       - Time period for utilization monitoring (in 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)             =
//=   4. Set DATA_RATE and corresponding PKT_SIZE                             =
//=                                                                           =
//=  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_util = 0.010, Target_Util = 0.09 (9% utilization)    =
//=   mu = 8333.3333                                                          =
//=   DATA_RATE = 100e6 (Set low data rate to 100 Mb/s)                       =
//=   PKT_SIZE = 1500 (corresponds to mu = 8333.33 at 100 Mb/s)               =
//=   MAX_ARRIVALS = 10.0e6                                                   =
//=---------------------------------------------------------------------------=
//= Example execution:                                                        =
//=   === BEGIN SIMULATION ===                                                =
//=   ============================================ ALR_util.c =======         =
//=   == *** CSIM simulation of an adaptive service time queue *** ==         =
//=   ===============================================================         =
//=   = >>> Poisson                                                           =
//=   = lambda             = 7499.999970 cust/sec                             =
//=   = mu                 = 8333.333300 cust/sec                             =
//=   = Target_Util        = 9.00 %                                           =
//=   = >>> UTIL THRESHOLD POLICY                                             =
//=   = MAX_RATE           = 10                                               =
//=   = T_switch           = 0.000000 sec                                     =
//=   = T_util             = 0.010000 sec                                     =
//=   = U_THRESH           = 5.000000 %                                       =
//=   = Thresh_max         = 30 pkts                                          =
//=   = Thresh_min         = 1 pkts                                           =
//=   = P_FIX              = 0.000000 Watts                                   =
//=   = P_LOW              = 1.500000 Watts                                   =
//=   = P_HIGH             = 4.000000 Watts                                   =
//=   ===============================================================         =
//=   = Total CPU time     = 21.015000 sec                                    =
//=   = Total sim time     = 1333.856748 sec                                  =
//=   = Total completions  = 10000000 cust                                    =
//=   =--------------------------------------------------------------         =
//=   = Server utilization = 9.397180 %                                       =
//=   = Mean num in system = 0.116709 cust                                    =
//=   = Mean response time = 0.000016 sec 0.015567 ms                         =
//=   = Mean service time  = 0.000013 sec 0.012534 ms                         =
//=   = Mean throughput    = 7497.056946 cust/sec                             =
//=   = Max queue length   = 30 pkts                                          =
//=   = Total service time = 125.344908 sec                                   =
//=   = Ideal low time     = 1333.856748 sec                                  =
//=   = Avg burst duration = 0.000000 sec                                     =
//=   =--------------------------------------------------------------         =
//=   = Time in low rate     = 6.65 sec        0.50 %                         =
//=   = Mean low rate time   = 1.663396 sec                                   =
//=   = Time in high rate    = 1327.20 sec     99.50 %                        =
//=   = Mean high rate time  = 331.800791 sec                                 =
//=   = Num rate switches    = 7                                              =
//=   =--------------------------------------------------------------         =
//=   = Total num samples  = 133385                                           =
//=   = Num samples under  = 3                                                =
//=   = Pct samples under  = 0.00 %                                           =
//=   =--------------------------------------------------------------         =
//=   = alpha metric       = 1.003127                                         =
//=   = gamma metric       = 1.003127                                         =
//=   ===============================================================         =
//=                                                                           =
//=---------------------------------------------------------------------------=
//=  Build: bcc32 -w-pro ALR_util.c csim19.obj csim19.lib                     =
//=---------------------------------------------------------------------------=
//=  Execute: ALR_util T_switch T_util Target_Util < in.dat                   =
//=           T_switch    - time to switch data rates (seconds)               =
//=           T_util      - time period for monitoring utilization (seconds)  =
//=           Target_Util - target utilization - range is (0,1)               =
//=                       - (used in GEN_TYPE == POISSON )                    =
//=           in.dat      - input trace file (GEN_TYPE==FILE only)            =
//=---------------------------------------------------------------------------=
//=  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                       =
//=               Mar 18, 2006 - Added dual-thresh policy and cleaned up junk =
//=               Aug 28, 2006 - Added BURST file format                      =
//=               Dec 25, 2006 - Renamed ALR_util.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  // Delta time in seconds, packet size in bytes
#define PSU                  1  // Delat time in microseconds, packet size zero
#define TIME_ONLY            2  // Delta time in seconds, no packet size
#define BURST                3  // Similar to USF with burst begin, end marker

#define BUFFER_PKT              // Switch buffer to packet mode from byte mode

#define FILE_FORMAT        USF  // data file format
//#define FILE_FORMAT        PSU  // data file format
//#define FILE_FORMAT  TIME_ONLY  // data file format
//#define FILE_FORMAT      BURST  // data file format

//#define GEN_TYPE       POISSON  // Generator type
#define GEN_TYPE          FILE  // Generator type

#define SIM_TIME         1.0e7  // 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 data rate of queue in bits/s
#define MAX_RATE            10  // Rate multiplier
#define BUFFER_SIZE      32768  // buffer size in bytes - 32KB

#define THRESH_MIN_PKT      15  // Min threshold (k1) in packets
#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.001  // 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

int      Rate_index;            // Rate index
double   Rate_switch_time;      // Time of last rate switch
int      Util_switch_flag;      // Utilization switch flag
long int Buffer_size;           // Current buffer size - in bytes

double   Sample_byte_count;     // Number of bytes counted during T_util interval

int      Rate_switch_count;     // Rate switch counter
long int Max_queue_length;      // Maximum recorded queue length
double   Ideal_low_time;        // Ideal low time with no delay impact
double   Total_serv_time;       // Total time spent servicing arrivals
long int Total_num_samples;     // Total number of utilization samples taken
long int Num_samples_under;     // Number of samples under threshold

// these variables are accepted from the command line
double   T_switch;              // Time to switch between rates (sec)
double   T_util;                // Utilization sampling interval (for dropping data rate)
double   Target_Util;           // Targeted utilization level (Poisson only)

long int Thresh_min;            // Minimum threshold value
long int Thresh_max;            // Maximum threshold value

//----- 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 sample_util();             // Compute utilization and change 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
  assert(argc == 4);
  
  // update T_switch from command line
  T_switch = (double)atof(argv[1]);
  T_util = (double)atof(argv[2]);
  Target_Util = (double)atof(argv[3]);
  
  // debug
  //printf("T_switch: %f D_BOUND: %f THRESH_DYNA: %d T_util: %f S_DELAY: %f Target_Util: %f \n", \
    T_switch, D_BOUND, THRESH_DYNA, T_util, S_DELAY, 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);
  
  // debug
  //printf("Thresh_min: %d Thresh_max: %d \n", Thresh_min, Thresh_max );

  // Create the simulation
  create("sim");

  // CSIM and variable 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 );
  
  Rate_index = 1;  // start in low rate
  Rate_switch_count = 0;
  Rate_switch_time = 0.0;
  Util_switch_flag = FALSE;
  Max_queue_length = 0;
  Total_serv_time = 0.0;
  Buffer_size = 0;
  Ideal_low_time = 0.0;
  Sample_byte_count = 0.0;

  Total_num_samples = 0;
  Num_samples_under = 0;

  // Parameter initializaitons - for Poisson
  mu = 8333.3333;
  // Give lambda as a percentage of max rate of arrivals
  lambda = (double)mu * (double)MAX_RATE * (double)Target_Util; 
  // Give lambda as pkts/ms at 1000 Mb/s at required utilization level
  //lambda = (double)83333.3333 * (double)Target_Util;

  // Output begin-of-simulation banner
  printf("=== BEGIN SIMULATION === \n");

  // Initiate the utilization sample process
  sample_util();
  
  // Initiate the rate printing process (for graphing data only)
  //print_rate();
  
  // 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_util.c ======= \n");
  printf("== *** CSIM simulation of an adaptive service time queue *** == \n");
  printf("=============================================================== \n");
  if (GEN_TYPE == POISSON)
  {
    printf("= >>> Poisson                      \n");
    printf("= lambda             = %f cust/sec \n", lambda);
    printf("= mu                 = %f cust/sec \n", mu);
    printf("= Target_Util        = %.2f %%     \n", ((double)Target_Util * 100) );
  }
  else if (GEN_TYPE == FILE)
  {
    printf("= >>> FILE                         \n");
    printf("= DATA_RATE (low)    = %.2f Mb/s   \n", (double)DATA_RATE/1e6);    
  }
  printf("= >>> UTIL THRESHOLD POLICY         \n");
  printf("= MAX_RATE           = %d          \n", MAX_RATE);
  printf("= T_switch           = %f sec      \n", T_switch);
  printf("= T_util             = %f sec      \n", T_util);  
  printf("= U_THRESH           = %f %%       \n", 100.0 * U_THRESH);  
  #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("= 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  = %ld cust    \n", (unsigned long int)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     = %.2f sec\t %.2f %% \n", \
        table_sum(Table_rate1), (table_sum(Table_rate1) * 100.0 / clock));
  printf("= Mean low rate time   = %f sec        \n", table_mean(Table_rate1));
  printf("= Time in high rate    = %.2f sec\t %.2f %% \n", \
        table_sum(Table_rateMax), (table_sum(Table_rateMax) * 100.0 / clock));
  printf("= Mean high rate time  = %f sec        \n", table_mean(Table_rateMax));
  printf("= Num rate switches    = %d            \n", Rate_switch_count);
  printf("=-------------------------------------------------------------- \n");
  #ifndef NO_UTIL_THRESH
  printf("= Total num samples  = %d          \n", Total_num_samples);
  printf("= Num samples under  = %d          \n", Num_samples_under);
  printf("= Pct samples under  = %.2f %%     \n", \
      (double)Num_samples_under / (double)Total_num_samples * 100);
  #endif
  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 = 1500;      // pkt size
  double    trace_time = 0.0;     // total time in trace
  double    temp1;
  unsigned long int   counter = 0;  
  
  create("gen_poisson");

  // Packet generation loop
  while(TRUE)
  {
    // Hold for desired interarrival time
    inter_arrival_time = expntl(1.0 / lambda);
    hold(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;
      
    // increment the running trace time value
    trace_time = trace_time + inter_arrival_time;
    
    // 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 = ((double)pkt_size * 8.0) / (double)DATA_RATE;

    // if generating service times using fixed packet sizes
    pkt_size = (int)PKT_SIZE;
    service_time = ((double)pkt_size * 8.0) / (double)DATA_RATE;

    // increment the input buffer size
    Buffer_size = Buffer_size + (long int)pkt_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 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 arrival_time: %.6f pkt_size: %.1f service_time: %f Buffer_size: %d \n", \
           clock, trace_time, inter_arrival_time, pkt_size, \
           service_time, Buffer_size);
  }
  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 time for FILE_TYPE == BURST
  
  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
  
  // Update byte count during T_util time period
  Sample_byte_count = Sample_byte_count + (double)pkt_size;

  // debug
  //printf("clock: %f enqueuing packet. Buffer_size: %d \n", clock, (int)Buffer_size );

  // identify the beginning of a burst
  if ((FILE_FORMAT == BURST) && (pkt_type == 0))
    t_burst_start = clock;

  // Reserve the server
  reserve(Server);

  // Check for need for a switch delay if rate has been decreased
  if (Util_switch_flag == TRUE)
  {
    // debug
    //printf("clock: %f - rate change to lower rate detected \n", clock);
    
    assert(Rate_index == 1);
    if ((clock - Rate_switch_time) <= T_switch)
      hold( T_switch - (clock - Rate_switch_time) );
    Util_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 - changing to higher data rate. qlength(Server): %d \n", \
      clock, (int)qlength(Server) );
    //printf("clock: %f - changing to higher data rate. Buffer_size: %d \n", \
      clock, Buffer_size );
    
    // this assert triggers on equal - it should trigger on < only
    //  -- 0.999999999 is a hack to stop this assert triggering on equal
    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++;
    hold(T_switch);
  }
  
  // compute the queueing time
  t_queue = (double)clock - start_time;
  // Service the customer
  hold(service_time / Rate_index);
  // compute the response time (waiting 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 CoV of departures
  //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
  t_last_depart = clock;

  // decrement the buffer and sanity check 
  Buffer_size = Buffer_size - pkt_size;
  assert(Buffer_size >= 0);

  // debug
  //printf("clock: %f dequeueed packet. Buffer_size: %d \n", \
        clock, (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 sampling utilization                                        ==
//=============================================================================
void sample_util()
{
  double    max_byte_count;       // Maximum bytes during T_util
  int       num_in_sys;           // Number of packets in the system

  create("sample_util");

  // compute max_byte_count during T_util
  max_byte_count = ( (double)DATA_RATE * (double)T_util * (double)MAX_RATE ) / (double)8.0;
  max_byte_count = max_byte_count * (double)U_THRESH;
  // compute max packet count during T_util
  
  // debug
  //printf("max_byte_count: %f \n", max_byte_count );
  
  // Utilization sampling loop
  while(1)
  {
    hold(T_util);
    
    // debug
    //printf("clock: %f Sample_byte_count: %f max_byte_count: %f Buffer_size: %d Thresh_min: %d \n", \
        clock, Sample_byte_count, max_byte_count, (int)Buffer_size, (int)Thresh_min );
    
    // compute the number in the system
    num_in_sys = qlength(Server) + num_busy(Server);
    
    // Update the number of samples under threshold and total number of samples
    if (Sample_byte_count <= max_byte_count)
      Num_samples_under = Num_samples_under + 1;
    Total_num_samples = Total_num_samples + 1;
    
    // Reduce the rate if utilization is low 
    #ifdef BUFFER_PKT
    if ( (Sample_byte_count <= max_byte_count) && (Rate_index == MAX_RATE) && (num_in_sys < Thresh_min) )
    #else
    if ( (Sample_byte_count <= max_byte_count) && (Rate_index == MAX_RATE) && (Buffer_size <= Thresh_min) )
    #endif
    {
      record((clock - Rate_switch_time), Table_rateMax);
      Rate_index = 1;
      Rate_switch_time = clock;
      Rate_switch_count++;
      Util_switch_flag = TRUE;
      // debug
      //printf("clock: %f - changed to lower rate. Buffer_size: %d num_in_sys: %d \n", \
          clock, Buffer_size, num_in_sys);
    }
    // Initialize sample count back to zero
    Sample_byte_count = 0.0;
  } // -- 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);

  }
}

