//================================================== file = syntraf1a.c =====
//=  Program to generate synthetic packet traffic                           =
//=   - Bounded Pareto or Cauchy burst size and exponential interburst time =
//=   - Modification of syntraf1.c to take target utilization as an input   =
//===========================================================================
//=  Notes: 1) Writes to a user specified output file                       =
//=             - File format is <interarrival_time packet_size>            =
//=         2) Bounded Pareto pdf is (k is lower bound, p is upper bound):  =
//=             - pdf is f(x) = ((a*k^a) / (1 - (k/p)^a))*x^(-a-1)          =
//=         3) See M. Crovella, M. Harchol-Balter, and C. Murta, "Task      =
//=            Assignment in a Distributed System: Improving Performance    =
//=            by Unbalancing Load," BUCS-TR-1997-018, October 1997.        =
//=         4) Cauchy distribution based upon:                              =
//=             A. Field, U. Harder and P. Harrison, “Network Traffic       =
//=             Behaviour in Switched Ethernet Systems”, Performance        =
//=             Evaluation, Vol. 58, No. 2, pp. 243-260, November 2004      =
//=            - Truncated cauchy distribution not used ( constant C in     =
//=              above publication sums to 1 for 1GB+ maximum values        =
//=            - Location parameter assumed to be zero                      =
//=            - Uses the positive half Cauchy pdf only                     =
//=         5) If using empirical distribution, need to code in the         =
//=            distribution or distribution function in main loop.          =
//=-------------------------------------------------------------------------=
//= Example user input:                                                     =
//=                                                                         =
//= C:\ResearchWork\Link Speed Related\ALR\Traffic models>syntraf1a         =
//= ---------------------------------------------- syntraf1a.c -----        =
//= -  Program to generate synthetic packet traffic                -        =
//= ----------------------------------------------------------------        =
//= Enter output file name =============================> temp.dat          =
//= Random number seed (greater than 0) ================> 4523              =
//= Burst size distribution: Cauchy [C], Pareto [P] ====> P                 =
//= Use utilization [U] or mean inter-burst time [T] ===> U                 =
//= Target utilization (%) =============================> 10                =
//= Pareto alpha value =================================> 1.1               =
//= Pareto k value (min burst size in bytes) ===========> 102400            =
//= Pareto p value (max burst size in bytes) ===========> 1073741824        =
//= Packet length gen method: Fixed [F], Empirical [E] => F                 =
//= Min packet length (bytes) ==========================> 64                =
//= Max (fixed) packet length (bytes) ==================> 1518              =
//= Burst intensity (% of data rate) ===================> 80                =
//= Link data rate (bits per second) ===================> 1000000000        =
//= Number of packets to generate ======================> 100000000         =
//= ----------------------------------------------------------------        =
//= -  Generating 100000000 packets...                                      =
//= -   - Min burst size      = 0.097656 MB                                 =
//= -   - Max burst size      = 1.000000 GB                                 =
//= -   - Mean burst size     = 0.648613 MB                                 =
//= -   - Variance burst size = 49148.689405 GB                             =
//= -   - Mean burst time     = 0.006801 sec                                =
//= -   - Mean intergap time  = 0.047608 sec                                =
//= ----------------------------------------------------------------        =
//= ----------------------------------------------------------------        =
//= -  Summary statistics...                                                =
//= -   - Total time of trace    = 12229.956794 sec                         =
//= -   - Minimum size burst     =   102401 bytes                           =
//= -   - Maximum size burst     = 1034026725 bytes                         =
//= -   - Total number of bursts =   225771                                 =
//= -   - Total number of bytes  = 151629386081 bytes                       =
//= -   - Utilization            = 9.918556 %                               =
//= ----------------------------------------------------------------        =
//=                                                                         =
//=-------------------------------------------------------------------------=
//= Example output file (first 10 entries of "output.dat" for above):       =
//=                                                                         =
//= 0.002687461388101   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//= 0.000015180000000   1518                                                =
//=                                                                         =
//=-------------------------------------------------------------------------=
//=  Build: bcc32 syntraf1a.c                                               =
//=-------------------------------------------------------------------------=
//=  Execute: syntraf1a                                                     =
//=-------------------------------------------------------------------------=
//=  Author: Ken Christensen                                                =
//=          University of South Florida                                    =
//=          WWW: http://www.csee.usf.edu/~christen                         =
//=          Email: christen@csee.usf.edu                                   =
//=-------------------------------------------------------------------------=
//=  History: KJC (10/11/05) - Genesis (from syntraf1.c)                    =
//=   Chamara - Mar 10, 2006 - Included inter-packet gap & burst intensity  =
//=   Chamara - Mar 10, 2006 - Included empirical packet distribution       =
//=   Chamara - Mar 25, 2006 - Included Cauchy distributed burst sizes      =
//=   Chamara - Apr 12, 2006 - Included mean inter-gap time from cmd line   =
//=   Chamara - Apr 12, 2006 - Last modified date                           =
//===========================================================================
//----- Include files -------------------------------------------------------
#include <stdio.h>              // Needed for printf()
#include <stdlib.h>             // Needed for exit() and ato*()
#include <math.h>               // Needed for log() and pow()
#include <assert.h>             // Needed for assert() macro

//----- Function prototypes -------------------------------------------------
double bpareto(double a, double k, double p); // Returns a bounded Pareto rv
double expon(double mean_value);              // Returns an exponential rv
double cauchy(double scale_parameter);        // Returns a cauchy rv
double rand_val(int seed);                    // Jain's RNG

//===== Main program ========================================================
void main(void)
{
  char     in_string[80];       // Input string
  FILE     *fp;                 // File pointer to output file
  double   target_util = 0.0;   // Target utilization (%)
  char     burst_dist;          // Burst size distribution
  char     util_or_mean;        // Inter-burst time - utilization or mean
  double   a;                   // Bounded Pareto alpha value
  double   k;                   // Bounded Pareto k value (bytes)
  double   p;                   // Bounded Pareto p value (bytes)
  double   location_parameter;  // Cauchy location parameter (median)
  double   scale_parameter;     // Cauchy scale parameter (median)
  char     pktlen_gen_type;     // Packet length generation method
  int      min_pktlen;          // Minimum packet length (bytes)
  int      max_pktlen;          // Maximum packet length (bytes)
  double   intensity;           // Burst intensity (% of data rate)
  double   data_rate;           // Link data rate (bit/sec)
  int      num_pkt;             // Number of packets to generate
  
  double   mean_burst_size;     // Computed Pareto mean burst size
  double   var_burst_size;      // Computed Pareto variance burst size
  double   mean_burst_time;     // Computed Pareto mean burst time
  double   intergap_time;       // Computed exponential burst gap time (sec)
  double   burst_size;          // Computed burst size
  double   expon_rv;            // Exponential random variable
  double   time;                // Current time
  double   time_last;           // Last time
  double   min_burst;           // Minimum measured burst size
  double   max_burst;           // Maximum measured burst size
  double   sum_bytes;           // Sum of bytes
  double   sum_bursts;          // Sum of bursts
  int      burst_len;           // Number of packets in a burst
  int      last_pktlen;         // Length of last packet in a burst (bytes)
  int      pktlen;              // Packet length (bytes)

  int      i;                   // Loop counter
  double   temp1;               // temporary variable
  double   z;                   // Random number holder

  // Output banner
  printf("---------------------------------------------- syntraf1a.c ----- \n");
  printf("-  Program to generate synthetic packet traffic                - \n");
  printf("---------------------------------------------------------------- \n");

  // Prompt for output filename and then create/open the file
  printf("Enter output file name =============================> ");
  scanf("%s", in_string);
  fp = fopen(in_string, "w");
  if (fp == NULL)
  {
    printf("ERROR in creating output file (%s) \n", in_string);
    exit(1);
  }

  // Prompt for random number seed and then use it
  printf("Random number seed (greater than 0) ================> ");
  scanf("%s", in_string);
  rand_val(atoi(in_string));

  // Prompt for burst size distribution - Cauchy or Pareto
  printf("Burst size distribution: Cauchy [C], Pareto [P] ====> ");
  scanf("%s", in_string);
  burst_dist = in_string[0];

  if ( burst_dist == 'P' )
  {
    // Prompt for utilization or mean inter-burst time - to set inter-burst time
    printf("Use utilization [U] or mean inter-burst time [T] ===> ");
    scanf("%s", in_string);
    util_or_mean = in_string[0];
    
    if ( util_or_mean == 'U' )
    {
      // Prompt for target utilization
      printf("Target utilization (%) =============================> ");
      scanf("%s", in_string);
      target_util = atof(in_string);
    }
    else if ( util_or_mean == 'T' )
    {
      // Prompt for inter-burst gap time 
      printf("Inter-burst gap time (sec) =========================> ");
      scanf("%s", in_string);
      intergap_time = atof(in_string);
    }
    else
    {
      printf("ERROR: Please input U or T only: %c \n", util_or_mean);
      exit(1);
    }

    // Prompt for Pareto alpha value
    printf("Pareto alpha value =================================> ");
    scanf("%s", in_string);
    a = atof(in_string);
  
    // Prompt for Pareto k value
    printf("Pareto k value (min burst size in bytes) ===========> ");
    scanf("%s", in_string);
    k = atof(in_string);
  
    // Prompt for Pareto p value
    printf("Pareto p value (max burst size in bytes) ===========> ");
    scanf("%s", in_string);
    p = atof(in_string);
  }
  else if ( burst_dist == 'C' )
  {
    // Prompt for Cauchy scale parameter
    printf("Cauchy scale parameter =============================> ");
    scanf("%s", in_string);
    scale_parameter = atof(in_string);

    // Prompt for inter-burst gap time - cauchy has no mean
    printf("Inter-burst gap time (sec) =========================> ");
    scanf("%s", in_string);
    intergap_time = atof(in_string);
  }
  else
  {
    printf("ERROR: Please input P or C only: %c \n", burst_dist);
    exit(1);
  }

  // Prompt for packet length generation method - if 'E', need to give pdf
  printf("Packet length gen method: Fixed [F], Empirical [E] => ");
  scanf("%s", in_string);
  pktlen_gen_type = in_string[0];

  if (( pktlen_gen_type != 'F' ) && ( pktlen_gen_type != 'E' ))
  {
    printf("ERROR: Please input F or E only \n");
    exit(1);
  }
  
  // Prompt for minimum packet length (for last packet in a burst)
  printf("Min packet length (bytes) ==========================> ");
  scanf("%s", in_string);
  min_pktlen = atoi(in_string);

  // Prompt for maximum packet length (used for a burst)
  printf("Max (fixed) packet length (bytes) ==================> ");
  scanf("%s", in_string);
  max_pktlen = atoi(in_string);

  // Prompt for burst intensity 
  printf("Burst intensity (%% of data rate) ===================> ");
  scanf("%s", in_string);
  intensity = atof(in_string);

  // Prompt for link data rate
  printf("Link data rate (bits per second) ===================> ");
  scanf("%s", in_string);
  data_rate = atof(in_string);

  // Prompt for number of packets to generate
  printf("Number of packets to generate ======================> ");
  scanf("%s", in_string);
  num_pkt = atoi(in_string);
  
  // sanity check
  if ( ((double)target_util / (double)intensity) > 1 )
  {
    printf("Cannot generate %.2f %% utilization at %.2f %% intensity! \n", target_util, intensity);
    exit(1);
  }

  // If the burst size distribution is Pareto, compute the inter-gap time
  if ( burst_dist == 'P' )
  {
    // Compute the needed mean burst gap time for the given target utilization
    mean_burst_size = (a*(pow(k,a))*((pow(k,(1-a))) - (pow(p,(1-a)))))/((a-1)*(1-pow((k/p),a)));
    var_burst_size = ((a*(pow(k,a))*((pow(k,(2-a))) - (pow(p,(2-a)))))/((a-2)*(1-(pow(k/p,a))))) - pow(mean_burst_size, 2.0);
    
    // compute the mean burst time with reduced intensity
    mean_burst_time = (8.0 * mean_burst_size) / (data_rate * (intensity / 100.0) );
    
    // If using utilization to compute the intergap time
    if ( util_or_mean == 'U' )
    {
      // compute the burst time at 100% intensity with 0 inter-packet gap
      temp1 = (8.0 * mean_burst_size) / data_rate;
      // compute the inter burst (inter gap) time
      intergap_time = (temp1 * (1.0 - (target_util / 100.0) )) / (target_util / 100.0);
      // subtract the extra time caused by reduced intensity and inter packet gap
      intergap_time = intergap_time - (mean_burst_time - temp1);
    }
  }

  //Output message and generate samples
  printf("---------------------------------------------------------------- \n");
  printf("-  Generating %d packets... \n", num_pkt);
  printf("-   - Min burst size      = %f MB    \n", (k / (double)(1024.0 * 1024.0)) );
  printf("-   - Max burst size      = %f GB    \n", (p / (double)(1024.0 * 1024.0 * 1024.0)) );
  printf("-   - Mean burst size     = %f MB    \n", mean_burst_size / (double)(1024.0 * 1024.0) );
  printf("-   - Variance burst size = %f GB    \n", var_burst_size / (double)(1024.0 * 1024.0 * 1024.0) );
  printf("-   - Mean burst time     = %f sec   \n", mean_burst_time);
  printf("-   - Mean intergap time  = %f sec   \n", intergap_time);
  printf("---------------------------------------------------------------- \n");

  // initialize summary stat variables
  time = time_last = sum_bytes = sum_bursts = 0.0;
  min_burst = 1.0e10;
  max_burst = 1.0e1;
  
  // Generate synthentic traffic - loop until num_pkt packets are generated
  while(1)
  {
    // get next burst size
    if ( burst_dist == 'P' )
      burst_size = bpareto(a, k, p);
    else
      burst_size = cauchy(scale_parameter);
    
    // get next inter burst time
    expon_rv = expon(intergap_time);
    
    // sanity checks
    if ( burst_dist == 'P' )
      assert((burst_size >= k) && (burst_size <= p));
      
    assert(expon_rv >= 0.0);
    
    if (burst_size < 0.0)
      exit(0);

    // Update minimum and maximum burst sizes
    if (burst_size < min_burst) min_burst = burst_size;
    if (burst_size > max_burst) max_burst = burst_size;
    
    // debug
    //printf("burst_size: %f inter_burst time: %f \n", burst_size, expon_rv);

    // Determine the interburst time
    time = time + expon_rv;

    // Generate the burst
    while(1)
    {
      if ( pktlen_gen_type == 'E' )
      {
        // Use an empirical distribution to generate the packet length
        z = rand_val(0);
        if (z < 0.57)         pktlen = 100;
        else if (z < 0.609)   pktlen = 200;
        else if (z < 0.614)   pktlen = 300;
        else if (z < 0.634)   pktlen = 400;
        else if (z < 0.642)   pktlen = 500;
        else if (z < 0.648)   pktlen = 600;
        else if (z < 0.651)   pktlen = 700;
        else if (z < 0.652)   pktlen = 800;
        else if (z < 0.653)   pktlen = 900;
        else if (z < 0.654)   pktlen = 1000;
        else if (z < 0.657)   pktlen = 1100;
        else if (z < 0.657)   pktlen = 1200;
        else if (z < 0.657)   pktlen = 1300;
        else if (z < 0.669)   pktlen = 1400;
        else if (z <= 1.000)  pktlen = 1500;
      }
      else
        // If using fixed sized packets
        pktlen = max_pktlen;
      
      // if burst remainder is less than packet length, set pktlen to remainder
      if (burst_size < (double)pktlen)
        pktlen = (int)burst_size;
      
      // check that packet size is at least min_pktlen
      if (pktlen < min_pktlen)
        pktlen = min_pktlen;
       
      // reduce the remaining burst size
      burst_size = burst_size - (double)pktlen;
      
      // increment the number of bytes
      sum_bytes = sum_bytes + pktlen;
      
      // update the time with packet transmit time
      time = time + ((8.0 * pktlen) / (data_rate * (intensity / 100.0) ) );
      
      // print the inter-arrival time + pkt length
      fprintf(fp,"%.15f   %d \n", (time - time_last), pktlen);

      // debug
      //printf("--- %.15f %.15f %d \n", (time - time_last), time_last, pktlen);
      
      // update the last packet time
      time_last = time;
      
      // decrement the number of packets (to exit)
      num_pkt--;
      
      // check for exit conditions
      if (burst_size <= 1.0) break;
      if (num_pkt <= 0) break;
    }
    
    // update the number of bursts
    sum_bursts++;
    
    // check for exit conditions
    if (num_pkt <= 0) break;
  }

  // Output trace summary statistics and close the output file
  printf("---------------------------------------------------------------- \n");
  printf("-  Summary statistics... \n");
  printf("-   - Total time of trace    = %f sec      \n", time);
  printf("-   - Minimum size burst     = %8.0f bytes \n", min_burst);
  printf("-   - Maximum size burst     = %8.0f bytes \n", max_burst);
  printf("-   - Total number of bursts = %8.0f       \n", sum_bursts);
  printf("-   - Total number of bytes  = %8.0f bytes \n", sum_bytes);
  printf("-   - Utilization            = %f %%       \n",
    100.0 * ((8.0 * sum_bytes) / time) / data_rate);
  printf("---------------------------------------------------------------- \n");
  fclose(fp);
}

//===========================================================================
//=  Function to generate bounded Pareto distributed RVs using              =
//=    - Input:  a, k, and p                                                =
//=    - Output: Returns with bounded Pareto RV                             =
//===========================================================================
double bpareto(double a, double k, double p)
{
  double z;          // Uniform random number from 0 to 1
  double pb_rv;      // Computed bounded Pareto value to be returned

  // Pull a uniform random number (0.0 < z < 1.0)
  do
  {
    z = rand_val(0);
  }
  while ((z == 0.0) || (z == 1.0));

  // Generate the bounded Pareto rv using the inversion method
  pb_rv = -(z * pow(p,a) - z * pow(k, a) - pow(p,a)) / (pow(p, a) * pow(k,a));
  pb_rv = pow(pb_rv, (-1.0 / a));

  return(pb_rv);
}

//===========================================================================
//=  Function to generate exponentially distributed random variables        =
//=    - Input:  mean_value                                                 =
//=    - Output: Returns with exponentially distributed random variable     =
//===========================================================================
double expon(double mean_value)
{
  double z;          // Uniform random number (0 < z < 1)
  double exp_rv;     // Computed exponential value to be returned

  // Pull a uniform random number (0.0 < z < 1.0)
  do
  {
    z = rand_val(0);
  }
  while ((z == 0.0) || (z == 1.0));

  // Compute exponential random variable using inversion method
  exp_rv = -mean_value * log(z);

  return(exp_rv);
}

//===========================================================================
//=  Function to generate Cauchy distributed random variables               =
//=    - Input:  scale_parameter                                            =
//=    - Output: Returns with Cauchy distributed random variable            =
//===========================================================================
double cauchy(double scale_parameter)
{
  double z;           // Uniform random number (0 < z < 1)
  double cauchy_rv;   // Computed exponential value to be returned
  double phi;         // Hold the value of phi (3.148 ... )

  // Pull a uniform random number (0.0 < z < 1.0)
  do
  {
    z = rand_val(0);
  }
  while ((z == 0.0) || (z == 1.0));
  
  // Compute phi
  phi = (double)22.0 / (double)7.0;

  // Compute cauchy random variable using inversion method
  cauchy_rv = scale_parameter * tan( (phi * z) / 2.0 );

  return(cauchy_rv);
}


//=========================================================================
//= Multiplicative LCG for generating uniform(0.0, 1.0) random numbers    =
//=   - x_n = 7^5*x_(n-1)mod(2^31 - 1)                                    =
//=   - With x seeded to 1 the 10000th x value should be 1043618065       =
//=   - From R. Jain, "The Art of Computer Systems Performance Analysis," =
//=     John Wiley & Sons, 1991. (Page 443, Figure 26.2)                  =
//=========================================================================
double rand_val(int seed)
{
  const long  a =      16807;  // Multiplier
  const long  m = 2147483647;  // Modulus
  const long  q =     127773;  // m div a
  const long  r =       2836;  // m mod a
  static long x;               // Random int value
  long        x_div_q;         // x divided by q
  long        x_mod_q;         // x modulo q
  long        x_new;           // New x value

  // Set the seed if argument is greater than zero and return zero
  if (seed > 0)
  {
    x = seed;
    return(0.0);
  }

  // RNG using integer arithmetic
  x_div_q = x / q;
  x_mod_q = x % q;
  x_new = (a * x_mod_q) - (r * x_div_q);
  if (x_new > 0)
    x = x_new;
  else
    x = x_new + m;

  // Return a random value between 0.0 and 1.0
  return((double) x / m);
}
