Statistics
| Branch: | Revision:

root / src / sim / chistogram.cc @ 08285dff

History | View | Annotate | Download (11.9 KB)

1
//=========================================================================
2
//  CHISTOGRAM.CC - part of
3
//
4
//                  OMNeT++/OMNEST
5
//           Discrete System Simulation in C++
6
//
7
//   Member functions of
8
//    cHistogramBase    : common base class for histogram classes
9
//    cHistogram        : equi-distant histogram
10
//    cLongHistogram    : long integer histogram
11
//    cDoubleHistogram  : double histogram
12
//
13
//   Authors: Andras Varga, Gabor Lencse
14
//
15
//=========================================================================
16

    
17
/*--------------------------------------------------------------*
18
  Copyright (C) 1992-2008 Andras Varga
19
  Copyright (C) 2006-2008 OpenSim Ltd.
20

21
  This file is distributed WITHOUT ANY WARRANTY. See the file
22
  `license' for details on this and other legal matters.
23
*--------------------------------------------------------------*/
24

    
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <math.h>
29
#include "random.h"
30
#include "distrib.h"
31
#include "globals.h"
32
#include "cdetect.h"
33
#include "chistogram.h"
34
#include "cexception.h"
35
#include "cenvir.h"
36

    
37
#ifdef WITH_PARSIM
38
#include "ccommbuffer.h"
39
#endif
40

    
41
USING_NAMESPACE
42

    
43
#define MIN(a,b) ((a)<(b) ? (a) : (b))
44
#define MAX(a,b) ((a)>(b) ? (a) : (b))
45

    
46

    
47
Register_Class(cLongHistogram);
48
Register_Class(cDoubleHistogram);
49

    
50

    
51
cHistogramBase::cHistogramBase(const char *name, int numcells) :
52
cDensityEstBase(name)
53
{
54
    cellv = NULL;
55
    num_cells = numcells;
56
}
57

    
58
cHistogramBase::~cHistogramBase()
59
{
60
    delete [] cellv;
61
}
62

    
63
void cHistogramBase::parsimPack(cCommBuffer *buffer)
64
{
65
#ifndef WITH_PARSIM
66
    throw cRuntimeError(this, eNOPARSIM);
67
#else
68
    cDensityEstBase::parsimPack(buffer);
69
    buffer->pack(num_cells);
70

    
71
    if (buffer->packFlag(cellv!=NULL))
72
        buffer->pack(cellv, num_cells);
73
#endif
74
}
75

    
76
void cHistogramBase::parsimUnpack(cCommBuffer *buffer)
77
{
78
#ifndef WITH_PARSIM
79
    throw cRuntimeError(this, eNOPARSIM);
80
#else
81
    cDensityEstBase::parsimUnpack(buffer);
82
    buffer->pack(num_cells);
83

    
84
    if (buffer->checkFlag())
85
    {
86
        cellv = new unsigned int[num_cells];
87
        buffer->unpack(cellv, num_cells);
88
    }
89
#endif
90
}
91

    
92
cHistogramBase& cHistogramBase::operator=(const cHistogramBase& res)
93
{
94
    if (this==&res) return *this;
95

    
96
    cDensityEstBase::operator=(res);
97

    
98
    num_cells = res.num_cells;
99
    delete [] cellv;
100
    cellv = NULL;
101
    if (res.cellv)
102
    {
103
        cellv = new unsigned[num_cells];
104
        memcpy(cellv, res.cellv, num_cells*sizeof(unsigned));
105
    }
106
    return *this;
107
}
108

    
109
void cHistogramBase::doMergeCellValues(const cDensityEstBase *other)
110
{
111
    for (int i=0; i<num_cells; i++)
112
        cellv[i] += (unsigned int) other->getCellValue(i);  //TODO overflow check
113
}
114

    
115
void cHistogramBase::clearResult()
116
{
117
    cDensityEstBase::clearResult();
118

    
119
    delete [] cellv;
120
    cellv = NULL;
121
}
122

    
123
void cHistogramBase::transform()
124
{
125
    if (isTransformed())
126
        throw cRuntimeError(this, "transform(): histogram already transformed");
127

    
128
    setupRange(); // this will set num_cells if it was unspecified (-1)
129

    
130
    int i;
131
    cellv = new unsigned [num_cells];
132
    for (i=0; i<num_cells; i++)
133
        cellv[i] = 0;
134

    
135
    for (i=0; i<num_vals; i++)
136
        collectTransformed(firstvals[i]);
137

    
138
    delete [] firstvals;
139
    firstvals = NULL;
140

    
141
    transfd = true;
142
}
143

    
144
int cHistogramBase::getNumCells() const
145
{
146
    if (!isTransformed())
147
        return 0;
148
    return num_cells;
149
}
150

    
151
void cHistogramBase::saveToFile(FILE *f) const
152
{
153
    cDensityEstBase::saveToFile(f);
154
    fprintf(f, "%d\t #= num_cells\n", num_cells);
155
    fprintf(f, "%d\t #= cellv[] exists\n", cellv!=NULL);
156
    if (cellv) for (int i=0; i<num_cells; i++) fprintf(f, " %u\n", cellv[i]);
157
}
158

    
159
void cHistogramBase::loadFromFile(FILE *f)
160
{
161
    cDensityEstBase::loadFromFile(f);
162
    freadvarsf(f, "%d\t #= num_cells", &num_cells);
163

    
164
    int cellv_exists;
165
    freadvarsf(f, "%d\t #= cellv[] exists", &cellv_exists);
166
    delete [] cellv; cellv = NULL;
167
    if (cellv_exists)
168
    {
169
        cellv = new unsigned[num_cells];
170
        for (int i=0; i<num_cells; i++) freadvarsf(f, " %u", cellv+i);
171
    }
172
}
173

    
174
void cHistogramBase::setNumCells(int numcells)
175
{
176
    if (cellv)
177
        throw cRuntimeError(this, "setNumCells(): too late, cells already set up");
178
    num_cells = numcells;
179
}
180

    
181

    
182
//----
183
// cHistogram - member functions
184

    
185
cHistogram::cHistogram(const char *name, int numcells, Mode mode) :
186
cHistogramBase(name, numcells)
187
{
188
    cellsize = 0;
189
    this->mode = mode;
190
}
191

    
192
void cHistogram::parsimPack(cCommBuffer *buffer)
193
{
194
#ifndef WITH_PARSIM
195
    throw cRuntimeError(this, eNOPARSIM);
196
#else
197
    cHistogramBase::parsimPack(buffer);
198
    buffer->pack(cellsize);
199
#endif
200
}
201

    
202
void cHistogram::parsimUnpack(cCommBuffer *buffer)
203
{
204
#ifndef WITH_PARSIM
205
    throw cRuntimeError(this, eNOPARSIM);
206
#else
207
    cHistogramBase::parsimUnpack(buffer);
208
    buffer->unpack(cellsize);
209
#endif
210
}
211

    
212
cHistogram& cHistogram::operator = (const cHistogram& res)
213
{
214
    if (this==&res) return *this;
215

    
216
    cHistogramBase::operator=(res);
217
    cellsize = res.cellsize;
218
    mode = res.mode;
219
    return *this;
220
}
221

    
222
void cHistogram::setMode(Mode mode)
223
{
224
    if (isTransformed())
225
        throw cRuntimeError(this, "setMode() cannot be called when cells have been set up already");
226
    this->mode = mode;
227
}
228

    
229
void cHistogram::setCellSize(double d)
230
{
231
    if (isTransformed())
232
        throw cRuntimeError(this, "setCellSize() cannot be called when cells have been set up already");
233
    cellsize = d;
234
}
235

    
236
void cHistogram::getAttributesToRecord(opp_string_map& attributes)
237
{
238
    cHistogramBase::getAttributesToRecord(attributes);
239

    
240
    if (mode == MODE_INTEGERS)
241
        attributes["type"] = "int";
242
}
243

    
244
void cHistogram::setupRange()
245
{
246
    cHistogramBase::setupRange();
247

    
248
    // the following code sets num_cells, and cellsize as (rangemax - rangemin) / num_cells
249

    
250
    if (mode == MODE_AUTO)
251
    {
252
        // if all precollected numbers are integers (and they are not all zeroes), go for integer mode
253
        bool allzeroes = true;
254
        bool allints = true;
255
        for (int i=0; i < num_vals; i++)
256
        {
257
            if (firstvals[i] != 0)
258
                allzeroes = false;
259
            if (firstvals[i] != floor(firstvals[i]))
260
                allints = false;
261
        }
262

    
263
        mode = (num_vals > 0 && allints && !allzeroes) ? MODE_INTEGERS : MODE_DOUBLES;
264
    }
265

    
266
    if (mode == MODE_INTEGERS)
267
        setupRangeInteger();
268
    else
269
        setupRangeDouble();
270
}
271

    
272
void cHistogram::setupRangeInteger()
273
{
274
    // set up the missing ones of: rangemin, rangemax, num_cells, cellsize;
275
    // throw error if not everything can be set up consistently
276

    
277
    // cellsize is double but we want to calculate with integers here
278
    long cellsize = (long) this->cellsize;
279

    
280
    // convert range limits to integers
281
    rangemin = floor(rangemin);
282
    rangemax = ceil(rangemax);
283

    
284
    if (range_mode == RANGE_FIXED)
285
    {
286
#define COMPLAINT "Cannot set up cells to satisfy constraints"
287
        long range = (long)(rangemax-rangemin);
288

    
289
        if (num_cells>0 && cellsize>0) {
290
            if (num_cells*cellsize != range)
291
                throw cRuntimeError(this, COMPLAINT": numCells*cellSize != rangeMax-rangeMin");
292
        }
293
        else if (cellsize>0) {
294
            if (range % cellsize != 0)
295
                throw cRuntimeError(this, COMPLAINT": specified range is not a multiple of cellSize");
296
            num_cells = range / cellsize;
297
        }
298
        else if (num_cells>0) {
299
            if (range % num_cells != 0)
300
                throw cRuntimeError(this, COMPLAINT": specified range is not a multiple of numCells");
301
            cellsize = range / num_cells;
302
        }
303
        else {
304
            int mincellsize = (int) ceil(range/200.0);
305
            int maxcellsize = (int) ceil(range/10.0);
306
            for (cellsize = mincellsize; cellsize <= maxcellsize; cellsize++)
307
                if (range % cellsize == 0)
308
                    break;
309
            if (cellsize > maxcellsize)
310
                throw cRuntimeError(this, COMPLAINT": specified range is too large, and cannot divide it to 10..200 equal-sized cells");
311
            num_cells = range / cellsize;
312
        }
313
#undef COMPLAINT
314
    }
315
    else
316
    {
317
        // non-fixed range
318
        if (num_cells>0 && cellsize>0) {
319
            // both given; num_cells*cellsize will determine the range
320
        }
321
        else if (num_cells>0) {
322
            // num_cells given ==> choose cellsize
323
            cellsize = (long) ceil((rangemax-rangemin)/num_cells);
324
        }
325
        else if (cellsize>0) {
326
            // cellsize given ==> choose num_cells
327
            num_cells = (int) ceil((rangemax-rangemin)/cellsize);
328
        }
329
        else {
330
            // neither given, choose both
331
            double range = rangemax - rangemin;
332
            cellsize = (long) ceil(range / 200.0);  // for range<=200, cellsize==1
333
            num_cells = (int) ceil(range/cellsize);
334
        }
335

    
336
        // adjust range to be cellsize*num_cells
337
        double newrange = cellsize*num_cells;
338
        double rangediff = newrange - (rangemax-rangemin);
339

    
340
        switch (range_mode)
341
        {
342
           case RANGE_AUTO:
343
             rangemin -= floor(rangediff/2);
344
             rangemax = rangemin + newrange;
345
             break;
346
           case RANGE_AUTOLOWER:
347
             rangemin = rangemax - newrange;
348
             break;
349
           case RANGE_AUTOUPPER:
350
             rangemax = rangemin + newrange;
351
             break;
352
        }
353
    }
354

    
355
    // write back the integer cellsize into double
356
    this->cellsize = cellsize;
357
}
358

    
359
void cHistogram::setupRangeDouble()
360
{
361
    if (num_cells == -1)
362
        num_cells = 30; // to allow merging every 2, 3, 5, 6 adjacent cells in post-processing
363
    cellsize = (rangemax - rangemin) / num_cells;
364
}
365

    
366
double cHistogram::random() const
367
{
368
    if (num_vals == 0)
369
    {
370
        return 0L;
371
    }
372
    else if (num_vals < num_firstvals)
373
    {
374
        // randomly select a sample from the stored ones
375
        return firstvals[genk_intrand(genk, num_vals)];
376
    }
377
    else
378
    {
379
        long m = genk_intrand(genk, num_vals - cell_under - cell_over);
380

    
381
        // select a random cell (k-1) and return a random number from it
382
        int k;
383
        for (k = 0; m >= 0; k++)
384
           m -= cellv[k];
385

    
386
        if (mode == MODE_INTEGERS)
387
        {
388
            // min_vals, max_vals: integer-valued doubles (e.g.: -3.0, 5.0)
389
            // rangemin, rangemax: doubles like -1.5, 4.5 (integer+0.5)
390
            // cellsize: integer-valued double, >0
391
            return ceil(rangemin) + (k-1)*(long)cellsize + genk_intrand(genk, (long)cellsize);
392
        }
393
        else
394
        {
395
            // return an uniform double from the given cell
396
            return rangemin + (k-1)*cellsize + genk_dblrand(genk)*cellsize;
397
        }
398
    }
399
}
400

    
401
void cHistogram::collectTransformed (double val)
402
{
403
    int k = (int)floor((val-rangemin)/cellsize);
404
    if (k < 0 || val < rangemin)
405
        cell_under++;
406
    else if (k >= num_cells || val >= rangemax)
407
        cell_over++;
408
    else
409
        cellv[k]++;
410
}
411

    
412
double cHistogram::getPDF(double x) const
413
{
414
    if (!isTransformed())
415
        throw cRuntimeError(this, "getPDF(x) cannot be called before histogram is transformed");
416

    
417
    int k = (int)floor((x-rangemin)/cellsize);
418
    if (k < 0 || x < rangemin || k >= num_cells || x >= rangemax)
419
        return 0.0;
420

    
421
    return cellv[k] / cellsize / num_vals;
422
}
423

    
424
double cHistogram::getCDF(double) const
425
{
426
    throw cRuntimeError(this, "getCDF() not implemented");
427
}
428

    
429
// return kth basepoint
430
double cHistogram::getBasepoint(int k) const
431
{
432
    //   k=0           : rangemin
433
    //   k=1,2,...     : rangemin + k*cellsize
434
    //   k=num_cells   : rangemax
435

    
436
    if (k<0 || k>num_cells)
437
        throw cRuntimeError(this, "invalid basepoint index %u", k);
438

    
439
    if (k == num_cells)
440
        return rangemax;
441
    else
442
        return rangemin + k*cellsize;
443
}
444

    
445
double cHistogram::getCellValue(int k) const
446
{
447
    if (k<0 || k>num_cells)
448
        throw cRuntimeError(this, "invalid cell index %u", k);
449
    return cellv[k];
450
}
451

    
452
void cHistogram::saveToFile(FILE *f) const
453
{
454
    cHistogramBase::saveToFile(f);
455
    fprintf(f, "%g\t #= cellsize\n", cellsize);
456
}
457

    
458
void cHistogram::loadFromFile(FILE *f)
459
{
460
    cHistogramBase::loadFromFile(f);
461
    freadvarsf(f, "%g\t #= cellsize", &cellsize);
462
}
463

    
464