00001 #include "function.h"
00002
00003 #include <cassert>
00004 #include <QFile>
00005 #include <QTextStream>
00006 #include <stdio.h>
00007 #include <QString>
00008 #include <QStringList>
00009
00010 #if defined(__APPLE__) || defined(linux)
00011 # include <sys/file.h>
00012 #endif
00013
00014 #define REPORT_ERROR(filename, line_nb, error) \
00015 error_occured = true; \
00016 err << "Error in file " << QString::fromStdString(filename) << " on line " << line_nb << error << endl
00017
00018 static QTextStream err(stderr);
00019
00020 namespace util
00021 {
00022
00025 void Function::init()
00026 {
00027 samples = 100000;
00028 cache_valid = false;
00029 error_occured = false;
00030 }
00031
00033 Function::Function() :
00034 pts(),
00035 max(),
00036 min(),
00037 scaling_x(1),
00038 scaling_y(1),
00039 shift_x(0),
00040 shift_y(0)
00041 {
00042 init();
00043 }
00044
00052 Function::Function(std::string filename)
00053 : FileObject(filename)
00054 , pts()
00055 , max()
00056 , min()
00057 , scaling_x(1)
00058 , scaling_y(1)
00059 , shift_x(0)
00060 , shift_y(0)
00061 {
00062 init();
00063 reread();
00064 }
00065
00066 Function::Function(const Function& copy)
00067 : FileObject(copy)
00068 , pts(copy.pts)
00069 , max(copy.max)
00070 , min(copy.min)
00071 , samples(copy.samples)
00072 , scaling_x(copy.scaling_x)
00073 , scaling_y(copy.scaling_y)
00074 , shift_x(copy.shift_x)
00075 , shift_y(copy.shift_y)
00076 , cache_valid(copy.cache_valid)
00077 , cache(copy.cache)
00078 , error_occured(copy.error_occured)
00079 {
00080 }
00081
00083 Function& Function::operator=(const Function& f) {
00084 FileObject::operator=(f);
00085 pts = f.pts;
00086 max = f.max;
00087 min = f.min;
00088 samples = f.samples;
00089 cache_valid = f.cache_valid;
00090 cache = f.cache;
00091 error_occured = f.error_occured;
00092 scaling_x = f.scaling_x;
00093 scaling_y = f.scaling_y;
00094 shift_x = f.shift_x;
00095 shift_y = f.shift_y;
00096 return *this;
00097 }
00098
00099 bool Function::error() {
00100 return error_occured;
00101 }
00102
00103 void Function::reread() {
00104 QFile f(QString::fromStdString(filename));
00105 error_occured = false;
00106 if(!f.open(QIODevice::ReadOnly))
00107 {
00108 error_occured = true;
00109 err << "Error opening file " << QString::fromStdString(filename) << ": " << f.errorString() << endl;
00110 return;
00111 }
00112 #if defined(__APPLE__) || defined(linux)
00113 flock(f.handle(), LOCK_SH);
00114 #endif
00115 QTextStream ts(&f);
00116 int ptid = 0, line_nb = 0;
00117
00118 max = Vector<2,double>(-HUGE_VAL,-HUGE_VAL);
00119 min = Vector<2,double>(HUGE_VAL,HUGE_VAL);
00120
00121 while(!ts.atEnd())
00122 {
00123 QString line = ts.readLine().trimmed();
00124 line_nb++;
00125 if(line.startsWith("fver"))
00126 {
00127 QStringList fields = line.split(" ");
00128 if(fields.size() != 3)
00129 {
00130 REPORT_ERROR(filename, line_nb, "cannot find major and minor in function version (fver)");
00131 return;
00132 }
00133 bool ok;
00134 int major = fields[1].toInt(&ok);
00135 if(!ok)
00136 {
00137 REPORT_ERROR(filename, line_nb, "major version number (" << fields[1] << ") is not a valid integer");
00138 return;
00139 }
00140 int minor = fields[2].toInt(&ok);
00141 if(!ok)
00142 {
00143 REPORT_ERROR(filename, line_nb, "minor version number (" << fields[2] << ") is not a valid integer");
00144 return;
00145 }
00146 if(major != 1 or minor != 1)
00147 {
00148 err << "Warning, this version usually deals with fver 1 1. Processing will continue but may fail" << endl;
00149 }
00150 }
00151 else if(line.startsWith("points:"))
00152 {
00153 QStringList fields = line.split(":");
00154 if(fields.size() != 2)
00155 {
00156 REPORT_ERROR(filename, line_nb, "Invalid point number specification, there is more than one ':'");
00157 return;
00158 }
00159 bool ok;
00160 unsigned int nbpts = fields[1].toUInt(&ok);
00161 if(!ok)
00162 {
00163 REPORT_ERROR(filename, line_nb, "Number of points (" << fields[1] << ") is not a valid integer");
00164 return;
00165 }
00166 pts.resize(nbpts);
00167 }
00168 else if(!line.contains(":"))
00169 {
00170 if(ptid >= (int)pts.size())
00171 {
00172 REPORT_ERROR(filename, line_nb, "There are more points than specified");
00173 return;
00174 }
00175 QStringList coords = line.split(" ");
00176 if(coords.size() != 2)
00177 {
00178 REPORT_ERROR(filename, line_nb, "There are " << coords.size() << " coordinates instead of 2");
00179 return;
00180 }
00181 double x, y;
00182 bool ok;
00183 x = coords[0].toDouble(&ok);
00184 if(!ok)
00185 {
00186 REPORT_ERROR(filename, line_nb, coords[0] << "' is not a valid double");
00187 return;
00188 }
00189 y = coords[1].toDouble(&ok);
00190 if(!ok)
00191 {
00192 REPORT_ERROR(filename, line_nb, coords[1] << "' is not a valid double");
00193 return;
00194 }
00195 if(x < min.x()) min.x() = x;
00196 if(y < min.y()) min.y() = y;
00197 if(x > max.x()) max.x() = x;
00198 if(y > max.y()) max.y() = y;
00199 Vector<2,double> p(x, y);
00200 pts[ptid++] = p;
00201 }
00202
00203 }
00204 if(ptid != (int)pts.size())
00205 {
00206 error_occured = true;
00207 err << "Error in file " << QString::fromStdString(filename)
00208 << ", there was " << pts.size() << " points announced, but only " << ptid << " points declared" << endl;
00209 return;
00210 }
00211
00212 cache_valid = false;
00213 #if defined(__APPLE__) || defined(linux)
00214 flock(f.handle(), LOCK_UN);
00215 #endif
00216 f.close();
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273 }
00274
00280 void Function::setSamples (size_t n)
00281 {
00282 samples = (unsigned int)n;
00283 cache_valid = false;
00284 }
00285
00295 double Function::operator()(double x) {
00296 x += shift_x;
00297 x *= scaling_x;
00298 if (x <= min.x()) return scaling_y*pts[0].y() + shift_y;
00299 if (x >= max.x()) return scaling_y*pts[pts.size() - 1].y() + shift_y;
00300
00301
00302 if (! cache_valid) {
00303 cache_valid = true;
00304 cache.resize (samples+1);
00305 for (size_t i = 0 ; i < samples ; i ++)
00306 cache[i].valid = false;
00307 }
00308
00309 double dx = (max.x() - min.x()) / samples;
00310 size_t lo = (size_t) ((x - min.x()) / dx);
00311 size_t hi = lo + 1;
00312
00313 double lox = min.x() + dx * lo;
00314 double hix = lox + dx;
00315 if (! cache [lo].valid) {
00316 cache [lo].valid = true;
00317 cache [lo].val = getVal (lox);
00318 }
00319 if (! cache [hi].valid) {
00320 cache [hi].valid = true;
00321 cache [hi].val = getVal (hix);
00322 }
00323
00324 double r = (x - lox) / dx;
00325 double ret = (1-r) * cache[lo].val + r * cache[hi].val;
00326
00327 return scaling_y*ret + shift_y;
00328 }
00329
00334 void Function::normalizeX(bool shift)
00335 {
00336 if(shift)
00337 {
00338 shift_x = min.x();
00339 scaling_x = max.x() - min.x();
00340 }
00341 else
00342 {
00343 scaling_x = max.x();
00344 }
00345 }
00346
00351 void Function::normalizeY(bool shift)
00352 {
00353 double ymin = HUGE_VAL, ymax = -HUGE_VAL;
00354 double dx = (max.x() - min.x()) / samples;
00355 double x0 = min.x();
00356 if (! cache_valid) {
00357 cache_valid = true;
00358 cache.resize (samples+1);
00359 for (size_t i = 0 ; i < samples ; i ++)
00360 cache[i].valid = false;
00361 }
00362 for(size_t i = 0 ; i < samples+1 ; ++i)
00363 {
00364 double y;
00365 if(cache[i].valid)
00366 {
00367 y = cache[i].val;
00368 }
00369 else
00370 {
00371 y = getVal(x0+dx*i);
00372 cache[i].valid = true;
00373 cache[i].val = y;
00374 }
00375 if(y < ymin) ymin = y;
00376 if(y > ymax) ymax = y;
00377 }
00378 if(shift)
00379 {
00380 shift_y = -ymin;
00381 if(ymax > ymin)
00382 scaling_y = 1/(ymax-ymin);
00383 else
00384 scaling_y = 1;
00385 }
00386 else
00387 {
00388 if(ymax != 0)
00389 {
00390 scaling_y = 1/ymax;
00391 }
00392 else
00393 {
00394 scaling_y = 1;
00395 }
00396 }
00397 }
00398
00405 double Function::getVal (double x) const {
00406 const double tofind = x;
00407 const double MaxError = 0.00001;
00408 double low = 0.0;
00409 double high = 1.0;
00410 double check = 0.5;
00411 Vector<2,double> tst;
00412 int counter = 0;
00413 do {
00414 check = (low + high) / 2.0;
00415 tst = P(check);
00416 if (tst.x() > tofind)
00417 high = check;
00418 else
00419 low = check;
00420 counter++;
00421 } while (fabs(tst.x() - tofind) > MaxError);
00422 return tst.y();
00423 }
00424
00426 Vector<2,double> Function::P(double x) const {
00427 const int n = (unsigned int)pts.size() - 1;
00428 const int t = 4;
00429 double u = x * (double(n - t) + 2.0);
00430
00431 Vector<2,double> sum;
00432
00433 for (int k = 0; k <= n; k++) {
00434 double coeff = N(k, t, u);
00435 sum += pts[k] * coeff;
00436 }
00437
00438 return sum;
00439 }
00440
00442 double Function::N(int k, int t, double u) const {
00443 double res = 0.0;
00444 if (1==t)
00445 res = Nk1(k, u);
00446 else
00447 res = Nkt(k, t, u);
00448 return res;
00449 }
00450
00451
00453 double Function::Nk1(int k, double u) const {
00454 if (Uk(k) <= u)
00455 {
00456 if (u < Uk(k + 1))
00457 return 1.0;
00458 }
00459 return 0.0;
00460 }
00461
00463 double Function::Nkt(int k, int t, double u) const {
00464 double sum = 0.0;
00465 int div = Uk(k + t - 1) - Uk(k);
00466 if (0 != div)
00467 sum = (u - Uk(k)) / div * N(k, t - 1, u);
00468
00469 div = Uk(k + t) - Uk(k + 1);
00470 if (0 != div)
00471 sum += (Uk(k + t) - u) / div * N(k + 1, t - 1, u);
00472
00473 return sum;
00474 }
00475
00477 int Function::Uk(int j) const {
00478 const int n = (unsigned int)pts.size() - 1;
00479 const int t = 4;
00480 if (j < t)
00481 return 0;
00482 if (j > n)
00483 return n - t + 2;
00484 return j - t + 1;
00485 }
00486
00488 const Vector<2,double>& Function::getMax() const {
00489 return max;
00490 }
00491
00493 const Vector<2,double>& Function::getMin() const {
00494 return min;
00495 }
00496
00497 }