#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <string.h>

#define max(a,b)   (((a) > (b)) ? (a) : (b))

        /* Parameters, default simulation parameters */

        /* -d delegated.txt - the name of the combined RIR registry delegation file */
char *delegated_file = "delegated.txt" ;

        /* size of the allocation block (as a prefix length) */
int ianablock = 12 ;

        /* name of the RIR to track in terms of delegations */
char *selectrir = "arin" ;

        /* start point of V4 delegations (in units of months prior to today) */
int start_date = 37 ;

        /* percent probablity that the allocation is to a new RIR */
int new_alloc = 23 ;

        /* average amount of time until re-allocation (fraction of a year) */
float re_alloc = 0.9 ;

        /* address management algorithm */
int sparse = 2; 
char *algorithm[] = {"sequential","sparse","rate_sparse"} ;

        /* report - verbosity */ 
int verbosity = 0 ;
char *vbexp[] = {"terse","Summary with headers","Per-Allocation details","Per-Allocation plus running summary","V4 Delegation records + per allocation"};

/* new variables: */
/* size of subnet id field  - default is 16  - min is 0, max is 16 */
int subnet_length = 16 ;

/* size of interface identifier id  - default is 64 - min is 0, max is 64 */
int interface_length = 64 ;

/* HD Ratio  - default is 0.8 - min is 0.1, max is 0.99 */
double hd_ratio = 0.8 ;

/* NAT factor - default is 0 - min is 0, max is 100 */
double nat_factor = 0.0 ;

/* minimum allocation - default is 16 bits */
int minimum_allocation = 16 ;

/* IPv4 allocation density metric */
float ipv4_alloc = 0.8 ;

        /* Constants */

        /* minimum allocation unit (prefix length) */
int min_size ; 


        /* Counters */
        
        /* allocation failure count */
int failures = 0 ;

        /* fragmented allocation coiunt */
int frags =0 ;

       
        /* global vars */

        /* current working date */
float ddate ;

        /* record processing counter */
int maxseq = 0 ;

int v4dist[33] ;
int v6dist[65];

        /* structure for each LIR */
struct lir {
  int lir_num ;                       /* index number */
  float rate ;                        /* current allocation run rate */
  int ready ;                         /* selection flag for window selection */
  unsigned long long slash32 ;                       /* current allocated address counter */
  unsigned long long  next_amount ;                   /* preferred next allocation size */
  unsigned long long deficit ;                       /* used if over-allocation was necessary */
  float next_date ;                   /* date for next allocation */
  unsigned long long  last_allocation_size ;          /* last allocation size */
  float first_allocation_date ;       /* first allocation date - used for rate calculation */
  int frag_count ;                    /* number of fragmentary address allocations */
  struct address *ablock ;            /* current active address block */
  struct lir *nxt ;                   /* linked list */
} *lirs = 0, **lirps;

        /* structure for each address allocation window */
struct address
{
  unsigned long long start_addr ;          /* start address */
  unsigned long mask_size ;           /* prefix mask size */
  unsigned long long block_size ;          /* block size (/32 units) */
  unsigned long long allocated ;           /* amount allocated in the window */
  unsigned long long free ;                /* amount free in the window */
  struct lir *block_owner ;           /* pointer to owning LIR */
  struct address *nxt ;               /* linkled list */
} *address = 0 ;

        /* function templates */        
extern int read_delegated();
extern void init_addresses();
extern void run_delegations();
extern void pool_status(double) ;
extern double prefix(unsigned long long) ;
extern double random_age() ;
extern void usage() ;
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;
extern int getopt(int argc, char * const *argv, const char *optstring);
extern int allocate(int, struct lir *,unsigned long long, double);
extern int linear_allocate(int, struct lir *,unsigned long long,double);
extern int sparse_allocate(int, struct lir *,unsigned long long,double);
extern int rate_sparse_allocate(int,struct lir *,unsigned long long,double);
extern int add_frags(int,struct lir *,unsigned long long,double);

        /* HD ratio tralslation table - Amount of end customers per prefix allocation point, starting at a /32 */
/*
unsigned long int hdsizes[24] = {7131,12416,21618,37640,65536,114104,198668,345901,602248,1048576,
  1825676,3178688,5534417,9635980,16777216,29210829,50859008,88550676,154175683,268435456, 467373274,
  813744135,1416810830,2466810933UL};
*/
double hdsizes[24];

        /* dynamic arrays used to store selected delegation sequence for replay into V6 management system */
float *dates;
unsigned int *allocs;
char **records ;

        /* Main block */
main(int argc, char **argv) 
{
  int ch ;
  double n, max ;
  int i, j ;
  int print_table = 0 ;

  check_date(&start_date);
  opterr = 0 ;
  while ((ch = getopt(argc,argv,"a:d:h:l:i:m:n:p:r:s:tv:z: ")) != -1)
    switch (ch) {
      case 'a':
        if (!strncmp(optarg,algorithm[1],strlen(optarg))) sparse = 1 ;
        else if (!strncmp(optarg,algorithm[2],strlen(optarg))) sparse = 2 ;
        else if (!strncmp(optarg,algorithm[0],strlen(optarg))) sparse = 0 ;
        else usage() ;
        break ;
      case 'd':
        delegated_file = (char *) strdup(optarg) ;
        break ;
      case 'h':
	hd_ratio = atof(optarg);
        if ((hd_ratio <= 0.0) || (hd_ratio > 100.0)) usage() ;
        break ;
      case 'i':
        ianablock = atoi(optarg) ;
        if ((ianablock < 2) || (ianablock > 32)) usage() ;
        break ;
      case 'l':
        subnet_length = atoi(optarg) ;
        if ((subnet_length < 0) || (subnet_length > 32)) usage() ;
        break ;
      case 'm':
	start_date = atoi(optarg) ;
        if (!check_date(&start_date)) usage() ;
        break ;
      case 'n':
	nat_factor = atof(optarg) ;
        if ((nat_factor < 0.0) || (nat_factor > 10.0)) usage() ;
        break ;
      case 'p':
        new_alloc = atoi(optarg)  ;
        if ((new_alloc < 1) || (new_alloc > 99)) usage() ;
        break ;
      case 'r':
        re_alloc = (float) (atoi(optarg) / 12.0) ;
	if ((re_alloc < 0.1) || (re_alloc > 4.0)) usage() ;
        break ;
      case 's':
        selectrir = (char *) strdup(optarg) ;
        if (strcmp(selectrir,"all") && strcmp(selectrir,"afrinic") && strcmp(selectrir,"arin") && strcmp(selectrir,"apnic") && strcmp(selectrir,"ripencc") && strcmp(selectrir,"lacnic"))
          usage() ;
	break;
      case 't':
        print_table = 1 ;
        break ;
      case 'v':
        verbosity = atoi(optarg) ;
        break ;
      case 'z':
        minimum_allocation = atoi(optarg) ;
        if ((minimum_allocation < 2) || (minimum_allocation > 32)) usage() ;
        break ;
      case '?':
      default:
        usage();
      }

  min_size = 128 - interface_length - subnet_length - minimum_allocation ;

  if (verbosity > 0) {
    printf("Address Management algorithm: -a %s\n",algorithm[sparse]) ;
    printf("IPv4 Delegation Log File:     -d %s\n",delegated_file) ;
    printf("HD-Ratio:                     -h %4.2f\n",hd_ratio) ;
    printf("Address Block Size:           -i /%d\n",ianablock) ;
    printf("Subnet ID Length:             -l %d\n",subnet_length);
    printf("Start Date for simulation:    -m %d\n",start_date) ;
    printf("NAT factor:                   -n %4.2f\n",nat_factor) ;
    printf("Probability of new LIR (%%):   -p %d\n",new_alloc);
    printf("Allocation lifetime (yrs):    -r %5.2f \n",re_alloc) ;
    printf("Selected RIR                  -s %s\n",selectrir) ;
    printf("Verbosity Level:              -v %d   %s\n",verbosity,vbexp[verbosity]) ;
    printf("Minimum Allocation Size:      -z %d\n",minimum_allocation) ;
    }

  if ((hd_ratio > 0.0) && (hd_ratio < 1.0)) {
    for (i = 0 ; i < 24 ; ++i) {
      hdsizes[i] = pow(pow(2.0,((double) (i + minimum_allocation))),hd_ratio) ;
      }
    }
  else if (hd_ratio > 1.0) {
    int step ;
    int exp ;
    double density_percentage ;

    step = (int) hd_ratio ;
    density_percentage = hd_ratio - ((int) hd_ratio) ;
    for (i = 0 ; i < 24 ; ++i) {
      exp = ((i + minimum_allocation - 2) / step) + 1 ;
      hdsizes[i] = pow(2.0,((double) (i + minimum_allocation))) * pow(density_percentage,(double) exp) ;
      }
    }

  for (i = 0 ; i < 32 ; ++i) v4dist[i] = 0 ;

  for (i = 0 ; i < 64 ; ++i) v6dist[i] = 0 ;
  if (print_table) {
    for (print_table = 24 ; print_table > 6 ; --print_table) {
      printf("  /%-2d    ",print_table) ;
      i = (1 << (32 - print_table)) ;
      n = (i * ipv4_alloc) ;
      n += n * nat_factor ;
      j = n ;
      printf("  %8d  ",j) ;
      i = 0 ;
      while ((i < 24) && (j > hdsizes[i])) ++i ;
      j = min_size - i ;
      printf("  /%d\n",j) ;
      }
    }
  read_delegated() ;
  init_addresses() ;
  if (verbosity > 0)
    printf("Registry Transaction Count:   %d\n\n",maxseq) ;
  run_delegations() ;  
  pool_status(ddate) ;

  if (verbosity > 0) print_dist() ;

}


/*=======================================================================*/

int
read_delegated()
{
  FILE *f ;
  char *c ;
  char inb[512] ;
  char rir[128],cc[128],typ[128],addr[128],sts[128] ;
  int i;
  unsigned int v4size ;
  unsigned int date ;
  unsigned int size ;
  unsigned int v4m ;
  unsigned int v6m ;
  double psize ;

  struct d_rec {
    int date ;
    float fdate ;
    char *d_rir ;
    unsigned int size ;
    unsigned int alloc ;
    struct d_rec *nxt ;
  } *d_list = 0, *tmp, *tmp1, *tmp2 ;

  if (!(f = fopen(delegated_file,"r"))) return(0) ;

  while (fgets(inb,511,f)) {
    while (c = strchr(inb,'|')) *c = ' ' ;
    sscanf(inb,"%s %s %s %s %d %d %s",rir,cc,typ,addr,&v4size,&date,sts) ;

    //    printf("cc=%s rir=%s date=%d %d -> %s",cc,rir,date, start_date,inb) ;
    if (!strcmp(cc,"ZZ")) continue ;
    if (date < start_date) continue ;
    if ((!strcmp(selectrir,"all")) || (!strcmp(rir,selectrir))) {

      //printf("GOT %s\n",rir) ;

      ++maxseq ;
      tmp = (struct d_rec *) malloc(sizeof *tmp) ;
      tmp->date = date ;
      tmp->d_rir = strdup(rir) ;
      tmp->size = size ;
      size = (date / 10000) ;
      tmp->fdate = size + (((date % 10000) / 100.00) / 12.0) ;

      size = ceil(log(v4size) / log(2.0)) ;
      v4m = 32 - size ;
      ++v4dist[v4m] ;
      psize = (double) (v4size * ipv4_alloc) ;
      psize += (psize * nat_factor) ;
      i = 0 ;
      while ((i < 24) && (psize > hdsizes[i])) ++i ;
      tmp->alloc = min_size - i;
      v6m = min_size - i ;
      ++v6dist[v6m] ;
      tmp->alloc = v6m ;
      tmp->nxt = 0 ;

      tmp->nxt = 0 ;
      if (!(tmp1 = d_list)) d_list = tmp ;
      else {
        tmp2 = 0 ;
        while ((tmp1) && (tmp1->date < tmp->date)) {
          tmp2 = tmp1 ;
          tmp1 = tmp1->nxt ;
	  }
        tmp->nxt = tmp1 ;
        if (tmp2) tmp2->nxt = tmp ;
        else d_list = tmp ;
        }
      }
    }
  fclose(f) ;
  ++maxseq ;
  dates = (float *) malloc(maxseq * (sizeof *dates)) ;
  allocs = (int *) malloc(maxseq * (sizeof *allocs)) ;
  lirps = (struct lir **) malloc(maxseq * (sizeof *lirps)) ;
  if (verbosity > 3) records = (char **)malloc(maxseq * (sizeof *records)) ;
  i = 0 ;
  tmp = d_list ;
  while (tmp) {
    if (i < maxseq) {
      dates[i] = tmp->fdate ;
      allocs[i] = tmp->alloc ;
      ++i ;
      }
    tmp1 = tmp ;
    tmp = tmp->nxt ;
    free(tmp1) ;
    }
  maxseq = i ;
}

void
init_addresses()
 {
  address = (struct address *) malloc(sizeof *address) ;
  address->start_addr = 0 ;
  address->mask_size = 0 ;
  address->block_size = (1ULL << ((min_size - ianablock) - address->mask_size)) ;
  address->allocated = 0;
  address->free = address->block_size ;
  address->block_owner = 0 ;
  address->nxt = 0 ;
 }

void
run_delegations()
 {
  int i = 0 ;
  int spin ;
  int lir_count = 0;
  int av ;
  int rcount = 0 ;
  int ecount = 0 ;
  int nspin = 1 ;
  unsigned long long minv = 0 ;
  unsigned long long minvc = 0 ;
  unsigned int size ;
  unsigned long long nsize ;
  struct lir *tmp ;

  srand(time(0)) ;
  spin = rand() % 1000 ;
  for (i = 0 ; i < spin ; ++i) random() ;

  for (i = 0 ; i < maxseq ; ++i) {

    ddate = dates[i] ;
    size = allocs[i] ;


    spin = random() % 100  ;

    nsize = (1 << (min_size - size)) ;


    if (verbosity > 3) printf("\n%s",records[i]) ;
    if (verbosity > 1) printf("%5.2f, /%d (%03uq):",ddate, size, nsize) ;

    if (spin > new_alloc) {
      rcount = ecount = 0 ;
      nspin = 1 ;
      minv = minvc = 0 ;
     
      if (verbosity > 1) printf("[add] ") ;
      tmp = lirs ;
      while (tmp) {
        if (nsize <= tmp->deficit) {
          tmp->deficit -= nsize ;
          if (verbosity > 1) printf("^LIR:%d Reduce overalloc by %qu from %qu to %qu",
				    tmp->lir_num, nsize, tmp->deficit + nsize, tmp->deficit) ;
          ecount = rcount = nspin = 0 ;
          tmp = 0 ;
	  }
        else {
          if ((ddate > tmp->next_date) && (nsize <= tmp->next_amount)) {
            tmp->ready = tmp->next_amount - nsize ;
            if (!minv) { minv = tmp->ready ; minvc = 1 ; }
            else if (tmp->ready == minv) ++minvc ;
            if (tmp->ready < minv) { minv = tmp->ready ; minvc = 1 ; }
            ++rcount ;
            if (nsize == tmp->next_amount) {
              ++ecount ;
              tmp->ready = -1 ;
	      }
	    }
          else tmp->ready = 0 ;
          tmp->rate = tmp->slash32 / ((tmp->next_date > ddate ? tmp->next_date : ddate) - tmp->first_allocation_date);
          tmp = tmp->nxt ;
          }
        }
      if (ecount) {
        if (ecount > 1) nspin = random() % ecount ;
        else nspin = 0 ;

        tmp = lirs ;
        while (tmp) {
          if (tmp->ready == -1) {
            if (!nspin) {
              if (verbosity > 1) printf("=LIR:%d (date=%5.2f) ",tmp->lir_num,tmp->next_date) ;
              tmp->slash32 += nsize ;
              tmp->next_amount = tmp->slash32 ;
              tmp->next_date = ddate + re_alloc + random_age() ;
              tmp->rate = tmp->slash32 / (ddate - tmp->first_allocation_date + 1.0);
	      lirps[i] = tmp ;

              if (!(av = allocate(1,tmp,tmp->slash32/2,ddate))) {
                if (verbosity > 1) printf(" FRAG") ;
                add_frags(1,tmp,tmp->slash32/2,ddate) ;
	        }
              else if (av > 1) {
                if (verbosity > 1) printf(" FRAG:1") ;
	        ++frags ;
	        }
	      else if (verbosity > 1) {
                printf(" ok: %u -> %u",tmp->slash32 - nsize, tmp->slash32) ;
	        }
              tmp = 0 ;
	      }
            --nspin ;
	    }
          if (tmp) tmp = tmp->nxt ;
	  }
	}
      else if (rcount) {
        if (minvc > 1) nspin = random() % minvc ;
        else nspin = 0 ;
        tmp = lirs ;
        while (tmp) {
          if (tmp->ready == minv) {
            if (!nspin) {
              int alloc ;

              if (verbosity > 1) printf("vLIR:%d (date=%5.2f) ",tmp->lir_num,tmp->next_date) ;
              if (sparse) {
                tmp->deficit = tmp->next_amount - nsize ;
                alloc = tmp->next_amount ;
                tmp->slash32 += tmp->next_amount ;
                tmp->next_amount = tmp->slash32 ;
                tmp->next_date = ddate + re_alloc + random_age() ;
	        }
              else {
                alloc = nsize ;
                tmp->slash32 += nsize ;
                tmp->next_amount -= nsize ;
	        }
              tmp->rate = tmp->slash32 / (ddate - tmp->first_allocation_date + 1.0);
	      lirps[i] = tmp ;

              if (!(av = allocate(1,tmp,alloc,ddate))) {
                if (verbosity > 1) printf(" FRAG") ;
                add_frags(1,tmp,alloc,ddate) ;
	        }
              else if (av > 1) {
                if (verbosity > 1) printf(" FRAG:1") ;
                //printf("Fragmentation for LIR - allocation of /%5.2f\n",prefix(tmp->slash32/2)) ;
	        ++frags ;
	        }
              else if (verbosity > 1) {
                printf(" ok: %u -> %u (%d)",tmp->slash32-alloc, tmp->slash32,nsize) ;
	        }
              tmp = 0;
	      }
            --nspin ;
	    }
          if (tmp) tmp = tmp->nxt ;
	  }
	}
      else if (nspin) {
        // printf(" xx\n") ;
          if (verbosity > 1) printf("-none ") ;
          spin = 0 ;
          } 
      }

    if (spin <= new_alloc) {
      if (verbosity > 1) printf("[new] ") ;
      /* this is a new allocation - set up a new LIR */
      tmp = (struct lir*) malloc(sizeof *tmp) ;
      tmp->lir_num = ++lir_count ;
      tmp->slash32 = nsize ;
      tmp->rate = tmp->slash32 ;
      tmp->deficit = 0 ;
      tmp->next_amount = tmp->slash32 ;
      tmp->next_date = ddate + re_alloc + random_age() ;
      tmp->last_allocation_size = tmp->slash32 ;
      tmp->first_allocation_date = ddate ;
      tmp->frag_count = 0 ;
      tmp->nxt = lirs ;
      lirs = tmp ;
      lirps[i] = lirs;
      // printf("%8.2f New Allocation: %d to %d\n",ddate,size,lir_count) ;
      if (verbosity > 1) printf("-LIR:%d (date=%5.2f) ",tmp->lir_num,ddate) ;
    
      if (!(av = allocate(0,lirs,lirs->slash32,ddate))) {
	if (verbosity > 1) printf(" FRAG") ;
         add_frags(0,lirs,lirs->slash32,ddate) ;
	 }
       else if (av > 1) {
         if (verbosity > 1) printf(" FRAG:1") ;
         //printf("Fragmentation for LIR - allocation of /%5.2f\n",prefix(lirs->slash32/2)) ;
	 ++frags ;
	 }
       else if (verbosity > 1) {
         printf(" ok: %u -> %u",0, tmp->slash32) ;
         }
      }
    if (verbosity > 1) printf("\n") ;
    if ((verbosity > 2) || (verbosity < 0)) pool_status(ddate) ;
    }
}

void
pool_status(double ddate)
{
  struct address *tmp = address ;
  unsigned long long pool_size = 0, alloctot = 0, totw = 0 ;
  int num_windows = 0 ;
  unsigned long long lgst_hole = 0 ;
  struct lir *lptr = lirs ;
  int numlirs = 0 ;
  char *format ;
  static int occupancy  = 0 ;
  static int lastmonth = -1 ;
  static int monthcount = -1 ;
  int month ;
  while (lptr) {
    ++numlirs ; 
    lptr = lptr->nxt ;
    }

  while (tmp) {
    pool_size += tmp->block_size ;
    alloctot += tmp->allocated ;
    if (tmp->block_owner) {
      totw += tmp->free ;     
      ++num_windows ;
      }
    if (tmp->free > lgst_hole) lgst_hole = tmp->free ;
    tmp = tmp->nxt ;
    }

  if (verbosity < 1) {
    format = "%5.2f %4.2f %4.2f %u %u %4.2f %5.2f %4.2f %5.2f %d %d %d\n";
    if (!verbosity)
        printf(format,
         (float) occupancy,
         prefix(pool_size),
         prefix(alloctot), num_windows,
	 numlirs ,
	 prefix(lgst_hole),
         (float) ((alloctot / (float) pool_size) * 100.0),
         prefix((unsigned long long) (alloctot / num_windows)), 
         prefix((unsigned long long) (totw / num_windows)),
         frags, failures, maxseq) ;
    if (verbosity == -1) {
      if (((alloctot * 100)  / pool_size) >= (occupancy)) {
        printf(format,
         (float) occupancy,
         prefix(pool_size),
         prefix(alloctot), num_windows,
	 numlirs ,
	 prefix(lgst_hole),
         (float) ((alloctot / (float) pool_size) * 100.0),
         prefix((unsigned long long) (alloctot / num_windows)), 
         prefix((unsigned long long) (totw / num_windows)),
         frags, failures, maxseq) ;
        occupancy += 10 ;
        if (occupancy > 100) exit(0) ;
        }
      }
    if (verbosity == -2) {
      month = (((int) (ddate * 100)) % 100) * 0.12 ;
      if (month != lastmonth) {
        format = "%d %5.2f %4.2f %4.2f %u %u %4.2f %5.2f %4.2f %5.2f %d %d %d\n";
        printf(format,
             ++monthcount,
	     ddate,
             prefix(pool_size),
             prefix(alloctot), num_windows,
	     numlirs ,
	     prefix(lgst_hole),
             (float) ((alloctot / (float) pool_size) * 100.0),
             prefix((unsigned long long) (alloctot / num_windows)), 
             prefix((unsigned long long) (totw / num_windows)),
             frags, failures, maxseq) ;
        lastmonth = month ;
        }
      }
    return ;
    }

      
  if (verbosity > 0) format = "\nPool Size:\t\t\t/%4.2f\nAllocation Total:\t\t/%4.2f\nNumber of Allocation Windows:\t%u\nNumber of LIRs:\t\t\t%u\nLargest Open Allocation Window:\t/%4.2f\nPercent Occupancy:\t\t%5.2f%%\nAverage Allocation Size:\t/%4.2f\nAverage Free Window Size:\t/%5.2f\nFragmented Allocations:\t\t%d\nNum of Failed Allocations:\t%d\nTransaction Count:\t\t%d\n";
  else format = "%4.2f %4.2f %u %u %4.2f %5.2f %4.2f %5.2f %d %d %d\n";
  printf("pool = %qu\n",pool_size) ;
  printf(format,
         prefix(pool_size),
         prefix(alloctot), num_windows,
	 numlirs ,
	 prefix(lgst_hole),
         (float) ((alloctot / (float) pool_size) * 100.0),
         prefix((unsigned long long) (alloctot / num_windows)), 
         prefix((unsigned long long) (totw / num_windows)),
         frags, failures, maxseq) ;
  

}


print_dist()
{
  int i ;
  int sz = 32;

  if (min_size > 32) sz = min_size ;
  printf("Mask ") ;
  for (i = 8 ; i <= sz ; ++i) printf(" %4d",i) ;
  printf("\n") ;
  printf("V4   ") ;
  for (i = 8 ; i <= 32 ; ++i) printf(" %4d",v4dist[i]) ;
  printf("\n") ;
  printf("V6   ") ;
  for (i = 8 ; i <= min_size ; ++i) printf(" %4d",v6dist[i]) ;
  printf("\n") ;
  
}

/*===================================================*/

void 
usage()
{
  printf(" \n Args: \n  -a sparse \n  -a ratesparse \n  -a sequential \n") ;
  printf("      Select the address managmenet algorithm to use	\n") ;
  printf("      default is -a ratesparse \n") ;
  printf(" \n") ;
  printf("  -d file\n") ;
  printf("      Select the delegation log file\n") ;
  printf("  \n") ;
  printf("  -h hd-ratio\n") ;
  printf("      Select the HD-Ratio value (default 0.8, min 0.1, max 0.99)\n") ;
  printf("  \n") ;
  printf("  -i n \n") ;
  printf("      Select the address block size as a /n \n") ;
  printf("      n should be between 2 and 32 \n") ;
  printf("      default is -i 12 \n") ;
  printf(" \n") ;
  printf("  -l subnet-id_length\n") ;
  printf("    Select the length of the subnet ID field (default 16, min 0, max 32)\n") ;
  printf("  \n") ;
  printf("  -m date \n") ;
  printf("      Selects the date to start the simulation. Format is yyyymmdd \n") ;
  printf("      Values between 1 and 120 for date are interested as months \n") ;
  printf("      -m 12 is equivalent to the last twleve months \n") ;
  printf("      default is -m 36 \n") ;
  printf(" \n") ;
  printf("  -n nat-factor\n") ;
  printf("    Selects the NAT factor where size = size + (size * nat_factor)\n") ;
  printf("    (default 0.0, max 10.0)\n") ;
  printf("  \n") ;
  printf("  -p probability_val \n") ;
  printf("      Selects the probability that an allocation refers to a new LIR \n") ;
  printf("      n is a value between 1 and 99 \n") ;
  printf("      default is -p 22 \n") ;
  printf(" \n") ;
  printf("  -r months \n") ;
  printf("      Number of months over which the initial allocation is notionally \n") ;
  printf("      evaluated. This is an average value, where the simulation randomizes \n") ;
  printf("      the value per LIR to a period between 6 months shorter and 18 months \n") ;
  printf("      longer than this average value. \n") ;
  printf("      default is -r 11 \n") ;
  printf(" \n") ;
  printf("  -s rir \n") ;
  printf("      Select a RIR for the simulation \n") ;
  printf("      possible values are: arin apnic ripcncc lacnic all\n") ;
  printf(" \n") ;
  printf("  -v n \n") ;
  printf("      Select verbosity level \n") ;
  printf("      default is 0 \n") ;
  printf("      Values:\n") ;
  printf("       -2 print out a summary of the allocation pool on a monthly basis\n") ;
  printf("       -1 print out a summary of the allocation pool for each 10%% shift in allocation density\n") ;
  printf("        0  print out a summary of the allocation pool at the end of the simulation\n") ;
  printf("        1 print out a more verbose summary\n") ;
  printf("        2 print out each allocation transaction\n") ;
  printf("        3 print out each allocation transaction and a summary of the pool after each transaction\n") ;
  printf("        4 print out the V4 transaction, the equiv v6 transaction and a summary of the pool\n") ;
  printf(" \n") ;
  printf("  -z min-alloc_size\n") ;
  printf("      Select minimum allocation size (default = 16, min = 2, max = 32\n") ;
  printf("  \n") ;
  exit(1) ;
}


int
check_date(int *date) 
{
  time_t t;
  struct tm* lt ;
  int year, month, day ;
 
  t = time(0) ;
  lt = localtime(&t) ;

  if (*date < 120) {
    year = *date / 12 ;
    month = *date % 12 ;
    lt->tm_mon -= month ;
    if (lt->tm_mon < 0) { lt->tm_mon += 12 ; ++year ; } ;
    lt->tm_year -= year ;
    t = mktime(lt) ;
    lt = localtime(&t) ;
    *date = ((lt->tm_year + 1900) * 10000) + ((lt->tm_mon + 1) * 100) + lt->tm_mday ;
    return(1) ;
    }  
  year = (*date / 10000) ;
  month = (*date / 100) % 100 ;
  day = *date % 100 ;
  if ((day < 1) || (day > 31)) return(0) ;
  if ((month < 0) || (month > 12)) return(0) ;
  if ((year < 1983) || (year > (lt->tm_year + 1900))) return(0) ;
  if ((((lt->tm_year + 1900) * 10000) + ((lt->tm_mon + 1) * 100) + lt->tm_mday) < *date) return(0) ;
  
  return(1) ;
}

int
add_frags(int expansion,struct lir *lirptr,unsigned long long slash32s,double ddate)
{
  unsigned long long unit = slash32s ;
  int av ;
  int fc = 0 ;

  ++frags ;
  while (slash32s) {
    if (av = allocate(expansion,lirptr,unit,ddate)) {
      if (av > 1) ++fc ;
      slash32s -= unit ;
      if (slash32s <= 0) { if (verbosity > 1) printf(":%d ",fc); return(1) ; }
      expansion = 1 ;
      while (unit > slash32s) unit = unit / 2 ;
      }
    else {
      if (unit <= 1) {
        ++failures ;
        return(0) ;
        }
      unit = unit / 2 ;
      }
    }

}


double
prefix(unsigned long long span)
{
  double tmp ;
  double size ;

  if (!span) return(0) ;
  tmp = span ;
  size = log(span) ;
  size = size / log(2.0) ;
  return(min_size - size) ;

  //  return(32 - log((double) span)/log(2.0));
}


double
random_age()
{
  int age ;
  double float_adj ;

  age = random() % 100 ;

  if (age < 40)
    return(float_adj = 0 - (0.5 * (age / 40.0))) ;
  if (age > 55) {
    age -= 55  ;
    return(float_adj = 2.0 * (age / 45.0)) ;
    }
  return(0.0);   
}


int
rate_sparse_allocate(int expansion, struct lir *lirptr,unsigned long long slash32s,double ddate)
{
  struct address *tmp, *tmp1 ;
  int i = 0 ;
  struct address *splitpoint = 0 ;
  float lir_lifetime, new_lifetime, lt, split_lt ;
  int rtnvalue = 1 ;
  int factor = 2 ;

  /* the first allocation just splits up the address block */

  if (!address->block_owner) {
    address->block_owner = lirptr ;
    address->allocated = slash32s ;
    address->free -= slash32s ;
    lirptr->ablock = address ;
    return(rtnvalue) ;
    }

  /* if this is an expansion of an existing window, and there is space, then 
     perform the expansion */

  if (expansion) {
    tmp = address ;
    while (tmp) {
      if (tmp->block_owner == lirptr) {
        if (tmp->free >= slash32s) {
          tmp->free -= slash32s ;
          tmp->allocated += slash32s ;
          return(rtnvalue) ;
	  }
        }
      tmp = tmp->nxt ;
      }
    ++rtnvalue ;
    factor = 1 ;
    }

  

    /* find all blocks where the open allocation window is at lease twice as large 
       as the local block */
    /* use the rate algorithm to find the 'best' block */
    /* split the block in half */
     
  split_lt = 0 ;
  splitpoint = 0 ;
  tmp = address ;
  while (tmp) {
    if ((tmp->free >= tmp->allocated) && (tmp->free >= (slash32s * factor))) {
      lir_lifetime = ((tmp->block_size / 2) - tmp->allocated) / tmp->block_owner->rate ;
      new_lifetime = (tmp->block_size / 2) / slash32s ;
      lt = lir_lifetime < new_lifetime ? lir_lifetime : new_lifetime ;
      if (lt > split_lt) {
        split_lt = lt ;
        splitpoint = tmp ;
        }
      }
    tmp = tmp->nxt ;
    }

  if (tmp = splitpoint) {

  //  printf(" SPLIT %d\n" ,slash32s) ;

  /* now split this in two */    
    tmp1 = (struct address *) malloc(sizeof *tmp1) ;
    tmp1->nxt = tmp->nxt ;
    tmp->nxt = tmp1 ;


    ++tmp->mask_size ;
    tmp->block_size = tmp->block_size / 2 ;
    tmp->free -= tmp->block_size ;
  
    tmp1->start_addr = tmp->start_addr + tmp->block_size ;
    tmp1->mask_size = tmp->mask_size;
    tmp1->block_size = tmp->block_size ;
    tmp1->allocated = slash32s ;
    tmp1->free = tmp1->block_size - tmp1->allocated ;
    tmp1->block_owner = lirptr ;
    lirptr->ablock = tmp1 ;
    ++lirptr->frag_count ;
    return(rtnvalue) ;
    }
  /* about to fail - lets see if there is ANY window at all left at this point  */
  tmp = address ;
  tmp1 = 0 ;
  factor = 0 ;
  while (tmp) {
    if (tmp->free >= slash32s) {
      if (tmp->free > factor) {
        tmp1 = tmp ;
        factor = tmp->free ;
        }
      }
    tmp = tmp->nxt ;
    }
  if (tmp = tmp1) {
    tmp1 = (struct address *) malloc(sizeof *tmp1) ;
    tmp1->nxt = tmp->nxt ;
    tmp->nxt = tmp1 ;
    tmp1->block_size = slash32s ;
    tmp1->start_addr = tmp->start_addr + tmp->block_size - slash32s ;
    tmp->block_size -= slash32s ;
    tmp->free -= slash32s ;
    tmp1->allocated = slash32s ;
    tmp1->free = tmp1->block_size - tmp1->allocated ;
    tmp1->block_owner = lirptr ;
    lirptr->ablock = tmp1 ;
    ++lirptr->frag_count ;
    return(rtnvalue) ;
    }
  return(0) ;

}


int
sparse_allocate(int expansion, struct lir *lirptr,unsigned long long slash32s,double ddate)
{
  struct address *tmp, *tmp1 ;
  int i = 0 ;
  struct address *splitpoint = 0 ;
  float lir_lifetime, new_lifetime, lt, split_lt ;
  int rtnvalue = 1 ;
  int factor = 2 ;
  static unsigned long c_alloc_size = 0 ;

  if (!address->block_owner) {
    address->block_owner = lirptr ;
    address->allocated = slash32s ;
    address->free -= slash32s ;
    lirptr->ablock = address ;
    return(rtnvalue) ;
    }

  if (expansion) {
    tmp = address ;
    while (tmp) {
      if (tmp->block_owner == lirptr) {
        if (tmp->free >= slash32s) {
          tmp->free -= slash32s ;
          tmp->allocated += slash32s ;
          return(rtnvalue) ;
	  }
        }
      tmp = tmp->nxt ;
      }
    ++rtnvalue ;
    factor = 1 ;
    }

  /* find the first open allocation window of c_alloc_size
     if none available halve c_alloc_size and try again */
  
  if (!c_alloc_size) c_alloc_size = (1 << 31) ;

  splitpoint = 0 ;
  split_lt = 0 ;

  while ((!splitpoint) && c_alloc_size) {
    tmp = address ;
    while (tmp) {
      if (tmp->free >= c_alloc_size) {
        if (slash32s > c_alloc_size) return(0) ;

        tmp1 = (struct address *) malloc(sizeof *tmp1) ;
        tmp1->nxt = tmp->nxt ;
        tmp->nxt = tmp1 ;
        tmp1->block_size = c_alloc_size ;
        tmp1->start_addr = tmp->start_addr + tmp->block_size - c_alloc_size ;
        tmp->block_size -= c_alloc_size ;
        tmp->free -= c_alloc_size ;
        tmp1->allocated = slash32s ;
        tmp1->free = tmp1->block_size - tmp1->allocated ;
        tmp1->block_owner = lirptr ;
        lirptr->ablock = tmp1 ;
        ++lirptr->frag_count ;
        return(rtnvalue) ;
        }
      tmp = tmp->nxt ;
      }
    c_alloc_size = c_alloc_size / 2 ;
    }
  return(0) ;
}


int
linear_allocate(int expansion, struct lir *lirptr,unsigned long long slash32s,double ddate)
{
  struct address *tmp, *tmp1 ;
  int i = 0 ;
  struct address *splitpoint = 0 ;
  float lir_lifetime, new_lifetime, lt, split_lt ;
  int rtnvalue = 1 ;
  int factor = 2 ;
  unsigned long long alloc ;
  static int not_initial = 0 ;
  float shut_date  = 0.0;

  if (expansion && not_initial) {
    tmp = address ;
    while (tmp) {
      if (tmp->block_owner == lirptr) {
        if (tmp->free >= slash32s) {
          tmp->free -= slash32s ;
          tmp->allocated += slash32s ;
          return(rtnvalue) ;
	  }
        }
      tmp = tmp->nxt ;
      }
    ++rtnvalue ;
    factor = 1 ;
    }

  /* need to split up the last block and create a new window */
  ++not_initial ;

  alloc = slash32s + max(slash32s,4) ;
  while (alloc) {
    tmp = address ;
    while (tmp) {
      if ((shut_date > 1.0) && (tmp->block_owner) && (tmp->block_owner->next_date < shut_date) && (tmp->free >= alloc)) {
        tmp1 = (struct address *) malloc(sizeof *tmp1) ;
        tmp1->start_addr = tmp->start_addr + tmp->block_size - alloc ;
        tmp->free -= alloc ;
        tmp->block_size -= alloc ;
        tmp1->block_size = alloc ;
        tmp1->allocated = slash32s ;
        tmp1->free = alloc - slash32s ;
        tmp1->block_owner = lirptr ;
        lirptr->ablock = tmp1 ;
        tmp1->nxt = tmp->nxt ;
        tmp->nxt = tmp1 ;
        return(rtnvalue) ;
        }
      if ((!tmp->block_owner) && (tmp->free >= alloc)) {
        tmp->block_owner = lirptr ;
        tmp->allocated = slash32s ;
        tmp->free = alloc - slash32s ;
        lirptr->ablock = tmp ;

        tmp1 = (struct address *) malloc(sizeof *tmp1) ;
        tmp1->nxt = tmp->nxt ;
        tmp->nxt = tmp1 ;
        tmp1->block_size = tmp1->free = tmp->block_size - alloc;
        tmp->block_size  = alloc ;
        tmp1->block_owner = 0 ;  
        tmp1->start_addr = tmp->start_addr + tmp->block_size ;
        tmp1->allocated = 0;
        return(rtnvalue) ;
        }
      tmp = tmp->nxt ;
      }
    if (shut_date < 1.0) 
      shut_date = ddate - 0.5 ; 
    else if (alloc > slash32s) {
      alloc = slash32s ;
      shut_date = 0.0 ;
      }
    else if (shut_date < 3000.0) {
      shut_date = 3001.0 ;
      }
    else 
      alloc = 0 ;
    }
  return(0) ;
}


int
allocate(int expansion, struct lir *lirptr,unsigned long long slash32s, double ddate)
{
  if (sparse == 2) return(rate_sparse_allocate(expansion,lirptr,slash32s,ddate));
  if (sparse == 1) return(sparse_allocate(expansion,lirptr,slash32s,ddate));
  return(linear_allocate(expansion,lirptr,slash32s,ddate)) ;
}
