iproute2/netem/maketable.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: NIST-PD */
   2/*
   3 * Experimental data  distribution table generator
   4 * Taken from the uncopyrighted NISTnet code (public domain).
   5 *
   6 * Read in a series of "random" data values, either
   7 * experimentally or generated from some probability distribution.
   8 * From this, create the inverse distribution table used to approximate
   9 * the distribution.
  10 */
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <math.h>
  14#include <malloc.h>
  15#include <string.h>
  16#include <sys/types.h>
  17#include <sys/stat.h>
  18
  19
  20double *
  21readdoubles(FILE *fp, int *number)
  22{
  23        struct stat info;
  24        double *x;
  25        int limit;
  26        int n=0, i;
  27
  28        if (!fstat(fileno(fp), &info) &&
  29            info.st_size > 0) {
  30                limit = 2*info.st_size/sizeof(double);  /* @@ approximate */
  31        } else {
  32                limit = 10000;
  33        }
  34
  35        x = calloc(limit, sizeof(double));
  36        if (!x) {
  37                perror("double alloc");
  38                exit(3);
  39        }
  40
  41        for (i=0; i<limit; ++i){
  42                if (fscanf(fp, "%lf", &x[i]) != 1 ||
  43                    feof(fp))
  44                        break;
  45                ++n;
  46        }
  47        *number = n;
  48        return x;
  49}
  50
  51void
  52arraystats(double *x, int limit, double *mu, double *sigma, double *rho)
  53{
  54        int n=0, i;
  55        double sumsquare=0.0, sum=0.0, top=0.0;
  56        double sigma2=0.0;
  57
  58        for (i=0; i<limit; ++i){
  59                sumsquare += x[i]*x[i];
  60                sum += x[i];
  61                ++n;
  62        }
  63        *mu = sum/(double)n;
  64        *sigma = sqrt((sumsquare - (double)n*(*mu)*(*mu))/(double)(n-1));
  65
  66        for (i=1; i < n; ++i){
  67                top += ((double)x[i]- *mu)*((double)x[i-1]- *mu);
  68                sigma2 += ((double)x[i-1] - *mu)*((double)x[i-1] - *mu);
  69
  70        }
  71        *rho = top/sigma2;
  72}
  73
  74/* Create a (normalized) distribution table from a set of observed
  75 * values.  The table is fixed to run from (as it happens) -4 to +4,
  76 * with granularity .00002.
  77 */
  78
  79#define TABLESIZE       16384/4
  80#define TABLEFACTOR     8192
  81#ifndef MINSHORT
  82#define MINSHORT        -32768
  83#define MAXSHORT        32767
  84#endif
  85
  86/* Since entries in the inverse are scaled by TABLEFACTOR, and can't be bigger
  87 * than MAXSHORT, we don't bother looking at a larger domain than this:
  88 */
  89#define DISTTABLEDOMAIN ((MAXSHORT/TABLEFACTOR)+1)
  90#define DISTTABLEGRANULARITY 50000
  91#define DISTTABLESIZE (DISTTABLEDOMAIN*DISTTABLEGRANULARITY*2)
  92
  93static int *
  94makedist(double *x, int limit, double mu, double sigma)
  95{
  96        int *table;
  97        int i, index, first=DISTTABLESIZE, last=0;
  98        double input;
  99
 100        table = calloc(DISTTABLESIZE, sizeof(int));
 101        if (!table) {
 102                perror("table alloc");
 103                exit(3);
 104        }
 105
 106        for (i=0; i < limit; ++i) {
 107                /* Normalize value */
 108                input = (x[i]-mu)/sigma;
 109
 110                index = (int)rint((input+DISTTABLEDOMAIN)*DISTTABLEGRANULARITY);
 111                if (index < 0) index = 0;
 112                if (index >= DISTTABLESIZE) index = DISTTABLESIZE-1;
 113                ++table[index];
 114                if (index > last)
 115                        last = index +1;
 116                if (index < first)
 117                        first = index;
 118        }
 119        return table;
 120}
 121
 122/* replace an array by its cumulative distribution */
 123static void
 124cumulativedist(int *table, int limit, int *total)
 125{
 126        int accum=0;
 127
 128        while (--limit >= 0) {
 129                accum += *table;
 130                *table++ = accum;
 131        }
 132        *total = accum;
 133}
 134
 135static short *
 136inverttable(int *table, int inversesize, int tablesize, int cumulative)
 137{
 138        int i, inverseindex, inversevalue;
 139        short *inverse;
 140        double findex, fvalue;
 141
 142        inverse = (short *)malloc(inversesize*sizeof(short));
 143        for (i=0; i < inversesize; ++i) {
 144                inverse[i] = MINSHORT;
 145        }
 146        for (i=0; i < tablesize; ++i) {
 147                findex = ((double)i/(double)DISTTABLEGRANULARITY) - DISTTABLEDOMAIN;
 148                fvalue = (double)table[i]/(double)cumulative;
 149                inverseindex = (int)rint(fvalue*inversesize);
 150                inversevalue = (int)rint(findex*TABLEFACTOR);
 151                if (inversevalue <= MINSHORT) inversevalue = MINSHORT+1;
 152                if (inversevalue > MAXSHORT) inversevalue = MAXSHORT;
 153                if (inverseindex >= inversesize) inverseindex = inversesize- 1;
 154
 155                inverse[inverseindex] = inversevalue;
 156        }
 157        return inverse;
 158
 159}
 160
 161/* Run simple linear interpolation over the table to fill in missing entries */
 162static void
 163interpolatetable(short *table, int limit)
 164{
 165        int i, j, last, lasti = -1;
 166
 167        last = MINSHORT;
 168        for (i=0; i < limit; ++i) {
 169                if (table[i] == MINSHORT) {
 170                        for (j=i; j < limit; ++j)
 171                                if (table[j] != MINSHORT)
 172                                        break;
 173                        if (j < limit) {
 174                                table[i] = last + (i-lasti)*(table[j]-last)/(j-lasti);
 175                        } else {
 176                                table[i] = last + (i-lasti)*(MAXSHORT-last)/(limit-lasti);
 177                        }
 178                } else {
 179                        last = table[i];
 180                        lasti = i;
 181                }
 182        }
 183}
 184
 185static void
 186printtable(const short *table, int limit)
 187{
 188        int i;
 189
 190        printf("# This is the distribution table for the experimental distribution.\n");
 191
 192        for (i=0 ; i < limit; ++i) {
 193                printf("%d%c", table[i],
 194                       (i % 8) == 7 ? '\n' : ' ');
 195        }
 196}
 197
 198int
 199main(int argc, char **argv)
 200{
 201        FILE *fp;
 202        double *x;
 203        double mu, sigma, rho;
 204        int limit;
 205        int *table;
 206        short *inverse;
 207        int total;
 208
 209        if (argc > 1) {
 210                if (!(fp = fopen(argv[1], "r"))) {
 211                        perror(argv[1]);
 212                        exit(1);
 213                }
 214        } else {
 215                fp = stdin;
 216        }
 217        x = readdoubles(fp, &limit);
 218        if (limit <= 0) {
 219                fprintf(stderr, "Nothing much read!\n");
 220                exit(2);
 221        }
 222        arraystats(x, limit, &mu, &sigma, &rho);
 223#ifdef DEBUG
 224        fprintf(stderr, "%d values, mu %10.4f, sigma %10.4f, rho %10.4f\n",
 225                limit, mu, sigma, rho);
 226#endif
 227
 228        table = makedist(x, limit, mu, sigma);
 229        free((void *) x);
 230        cumulativedist(table, DISTTABLESIZE, &total);
 231        inverse = inverttable(table, TABLESIZE, DISTTABLESIZE, total);
 232        interpolatetable(inverse, TABLESIZE);
 233        printtable(inverse, TABLESIZE);
 234        return 0;
 235}
 236