/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "base/polygraph.h" #include #include #include "xstd/String.h" #include "xstd/Rnd.h" #include "xstd/TblDistr.h" #include "xstd/Error.h" #include "xstd/SrcLoc.h" #include "xstd/gadgets.h" #include "base/loadTblDistr.h" static SrcLoc TheLoc; static void ltrim(char *&p) { while (isspace(*p)) ++p; } static void rtrim(char *p) { int len = strlen(p); while (len && isspace(p[len-1])) --len; p[len] = 0; } inline void trim(char *&p) { ltrim(p); rtrim(p); } static int getLine(istream &is, Array &line) { line.reset(); static char buf[8*1024]; while (!line.count() && is && is.getline(buf, sizeof(buf))) { char *p = buf; // delete comments if (char *c = strchr(p, '#')) *c = 0; // split into "words" ltrim(p); while (strlen(p)) { //cerr << here << "{ p = '" << p << "'" << endl; char *eow = p; while (*eow && !isspace(*eow)) ++eow; if (p < eow) { line.append(p); p = *eow ? eow + 1 : eow; *eow = '\0'; } ltrim(p); } ++TheLoc; } /*cerr << here << line.count() << ": "; for (int i = 0; i < line.count(); ++i) cerr << " '" << line[i] << "'"; cerr << endl;*/ return line.count(); } static RndDistr *loadFailure(TblDistr *&tbl) { delete tbl; return tbl = 0; } static bool matchWord(const String &expected, const String &got) { if (expected != got) { cerr << TheLoc << "expected `" << expected << "', got `" << got << "'" << endl; return false; } return true; } static bool getNumber(double &num, const String &str) { if (!isNum(str.cstr(), num)) { cerr << TheLoc << "expected a number, got `" << str << "'" << endl; return false; } return true; } RndDistr *LoadTblDistr(const String &fname, const String &argType) { Assert(fname); clog << "loading distribution pdf from '" << fname << "'..." << endl; TheLoc = SrcLoc(fname, 0); TblDistr *tbl = 0; ifstream is(fname.cstr()); Array line; if (!is) { cerr << "error: cannot open '" << fname << "': " << Error::Last() << endl; return loadFailure(tbl); } // interpret header if (getLine(is, line) == 4) { const String type = argType + "_distr"; if (!matchWord(type, line.shift())) return loadFailure(tbl); tbl = new TblDistr(line.shift()); // name if (!matchWord("=", line.shift())) return loadFailure(tbl); if (!matchWord("{", line.shift())) return loadFailure(tbl); } else { cerr << TheLoc << "malformed or missing header line for a table distribution" << endl; return loadFailure(tbl); } Assert(tbl); TblDistrBin *wild_card = 0; double setContrib = -1; double accContrib = 0; double next_sup = 0; // load bins until end-of-bins symbol is found const String eob = "}"; while (getLine(is, line)) { if (line.count() == 1 && eob == line[0]) break; if (line.count() != 2) { cerr << TheLoc << "syntax error, expected ` '" << endl; return loadFailure(tbl); } TblDistrBin *bin = 0; String h = line.shift(); const char *column = h.chr(':'); if (column) { double min, max; if (h[0] == ':') { min = next_sup; if (!getNumber(max, h+1)) return loadFailure(tbl); } else { if (!getNumber(min, h)) return loadFailure(tbl); if (!getNumber(max, column+1)) return loadFailure(tbl); } if (min >= max) { cerr << TheLoc << "min must be smaller than max" << endl; return loadFailure(tbl); } bin = new TblDistrIntvl(min, max); next_sup = max; } else { double point; if (!getNumber(point, h)) return loadFailure(tbl); bin = new TblDistrPoint(point); next_sup = point + 1; } Assert(bin); if (tbl->overlapping(bin)) { cerr << TheLoc << "bin overlaps with earlier bin(s)" << endl; tbl->printBins(cerr); delete bin; return loadFailure(tbl); } h = line.shift(); if (h[0] == '*') { if (wild_card) { cerr << TheLoc << "only one wild card per pdf is allowed" << endl; return loadFailure(tbl); } wild_card = bin; const char *slash = h.chr('/'); if (slash) { if (!getNumber(setContrib, slash+1)) return loadFailure(tbl); } else { cerr << TheLoc << "wild card entry requires" << " total contribution field (e.g., '*/100')" << endl; return loadFailure(tbl); } } else { double contrib; if (!getNumber(contrib, h)) return loadFailure(tbl); if (contrib > 0) { // ignore non-contributing bins bin->absContrib(contrib); tbl->addBin(bin); accContrib += contrib; } else delete bin; } } is.close(); if (wild_card) { if (accContrib > setContrib) { cerr << TheLoc << "specified total contribution of " << setContrib << " is less than actual contribution of " << accContrib << endl; delete wild_card; return loadFailure(tbl); } const double contrib = setContrib - accContrib; if (contrib > 0) { wild_card->absContrib(contrib); tbl->addBin(wild_card); } else delete wild_card; } if (!tbl->binCount()) { cerr << TheLoc << ": error: empty pdf declaration" << endl; return loadFailure(tbl); } tbl->make(); clog << TheLoc << "loaded `" << *tbl << "'" << " idx entries: " << tbl->idxCount() << " idx depth: " << tbl->idxDepth() << endl; return tbl; }