00001 #pragma ident "$Id: SpecialFunctions.hpp 292 2009-09-02 20:29:30Z BrianTolman $"
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00045 #ifndef SPECIAL_FUNCTIONS_INCLUDE
00046 #define SPECIAL_FUNCTIONS_INCLUDE
00047
00048 #include <cmath>
00049 #include <limits>
00050 #include "Exception.hpp"
00051
00052 namespace gpstk
00053 {
00059 double lnGamma(const double& x) throw(Exception)
00060 {
00061 static const double con[8] = {
00062 76.18009172947146, -86.50532032941677, 24.01409824083091,
00063 -1.231739572450155, 1.208650973866179e-3, -5.395239384953e-6,
00064 1.000000000190015, 2.5066282746310005 };
00065 if(x <= 0) {
00066 Exception e("Non-positive argument in lnGamma()");
00067 GPSTK_THROW(e);
00068 }
00069 double y(x);
00070 double t(x+5.5);
00071 t -= (x+0.5) * ::log(t);
00072 double s(con[6]);
00073 for(int i=0; i<=5; i++) s += con[i] / ++y;
00074 return (-t + ::log(con[7]*s/x));
00075 }
00076
00081 double factorial(const int& n) throw(Exception)
00082 {
00083 if(n < 0) {
00084 Exception e("Negative argument in factorial()");
00085 GPSTK_THROW(e);
00086 }
00087
00088 if(n > 32) return ::exp(lnGamma(double(n+1)));
00089
00090 static double store[33] = { 1.0, 1.0, 2.0, 6.0, 24.0, 120.0 };
00091 static int nstore=5;
00092
00093 while(nstore < n) {
00094 int i = nstore++;
00095 store[nstore] = store[i] * nstore;
00096 }
00097
00098 return store[n];
00099 }
00100
00105 double lnFactorial(const int& n) throw(Exception)
00106 {
00107 if(n < 0) {
00108 Exception e("Negative argument in lnFactorial()");
00109 GPSTK_THROW(e);
00110 }
00111 if(n <= 1) return 0.0;
00112 return lnGamma(double(n+1));
00113 }
00114
00123 double binomialCoeff(const int& n, const int& k) throw(Exception)
00124 {
00125 if(n < 0 || k > n) {
00126 Exception e("Invalid arguments in binomialCoeff()");
00127 GPSTK_THROW(e);
00128 }
00129
00130 if(n <= 32)
00131 return (factorial(n) / (factorial(k)*factorial(n-k)));
00132
00133 return floor(0.5 + ::exp(lnFactorial(n)-lnFactorial(k)-lnFactorial(n-k)));
00134 }
00135
00142 double beta(const double& x, const double& y) throw(Exception)
00143 {
00144 try {
00145 return ::exp(lnGamma(x) + lnGamma(y) - lnGamma(x+y));
00146 }
00147 catch(Exception& e) {
00148 e.addText("Called by beta(x,y)");
00149 GPSTK_RETHROW(e);
00150 }
00151 }
00152
00159 double seriesIncompGamma(const double& a, const double& x) throw(Exception)
00160 {
00161 if(x < 0) {
00162 Exception e("Negative first argument in seriesIncompGamma()");
00163 GPSTK_THROW(e);
00164 }
00165 if(a <= 0) {
00166 Exception e("Non-positive second argument in seriesIncompGamma()");
00167 GPSTK_THROW(e);
00168 }
00169
00170 double lngamma(lnGamma(a));
00171
00172 static const int imax(100);
00173 static const double eps(10*std::numeric_limits<double>().epsilon());
00174
00175 double atmp(a),sum(1.0/a);
00176 double del(sum);
00177 for(int i=1; i<=imax; i++) {
00178 ++atmp;
00179 del *= x/atmp;
00180 sum += del;
00181 if(::fabs(del) < ::fabs(sum)*eps)
00182 return (sum * ::exp(-x + a * ::log(x) - lngamma));
00183 }
00184 Exception e("Overflow in seriesIncompGamma; first arg too big");
00185 GPSTK_THROW(e);
00186
00187 return 0.0;
00188 }
00189
00196 double contfracIncompGamma(const double& a, const double& x) throw(Exception)
00197 {
00198 if(x < 0) {
00199 Exception e("Negative first argument in contfracIncompGamma()");
00200 GPSTK_THROW(e);
00201 }
00202 if(a <= 0) {
00203 Exception e("Non-positive second argument in contfracIncompGamma()");
00204 GPSTK_THROW(e);
00205 }
00206
00207 double lngamma(lnGamma(a));
00208
00209 static const int imax(100);
00210 static const double eps(10*std::numeric_limits<double>().epsilon());
00211 static const double fpmin(10*std::numeric_limits<double>().min());
00212
00213 double b(x+1.0-a),c(1.0/fpmin);
00214 double d(1.0/b);
00215 double h(d);
00216 int i;
00217 for(i=1; i<=imax; i++) {
00218 double an(-i*(i-a));
00219 b += 2.0;
00220 d = an*d+b;
00221 if(::fabs(d) < fpmin) d=fpmin;
00222 c = b+an/c;
00223 if(::fabs(c) < fpmin) c=fpmin;
00224 d = 1.0/d;
00225 double del(d*c);
00226 h *= del;
00227 if(::fabs(del-1.0) < eps) break;
00228 }
00229 if(i > imax) {
00230 Exception e("Overflow in contfracIncompGamma; first arg too big");
00231 GPSTK_THROW(e);
00232 }
00233
00234 return (::exp(-x + a * ::log(x) - lngamma) * h);
00235 }
00236
00243 double incompGamma(const double& a, const double& x) throw(Exception)
00244 {
00245 if(x < 0) {
00246 Exception e("Negative first argument in incompGamma()");
00247 GPSTK_THROW(e);
00248 }
00249 if(a <= 0) {
00250 Exception e("Non-positive second argument in incompGamma()");
00251 GPSTK_THROW(e);
00252 }
00253
00254 if(x < a+1.0)
00255 return seriesIncompGamma(a,x);
00256 else
00257 return (1.0 - contfracIncompGamma(a,x));
00258 }
00259
00266 double compIncompGamma(const double& a, const double& x) throw(Exception)
00267 {
00268 if(x < 0) {
00269 Exception e("Negative first argument in compIncompGamma()");
00270 GPSTK_THROW(e);
00271 }
00272 if(a <= 0) {
00273 Exception e("Non-positive second argument in compIncompGamma()");
00274 GPSTK_THROW(e);
00275 }
00276
00277 if(x < a+1.0)
00278 return (1.0 - seriesIncompGamma(a,x));
00279 else
00280 return contfracIncompGamma(a,x);
00281 }
00282
00286 double errorFunc(const double& x) throw(Exception)
00287 {
00288 if(x < 0) {
00289 Exception e("Negative first argument in errorFunc()");
00290 GPSTK_THROW(e);
00291 }
00292 try {
00293 return (x < 0.0 ? -incompGamma(0.5,x*x) : incompGamma(0.5,x*x));
00294 }
00295 catch(Exception& e) {
00296 e.addText("Called by errorFunc()");
00297 GPSTK_RETHROW(e);
00298 }
00299 }
00300
00304 double compErrorFunc(const double& x) throw(Exception)
00305 {
00306 if(x < 0) {
00307 Exception e("Negative first argument in compErrorFunc()");
00308 GPSTK_THROW(e);
00309 }
00310 try {
00311 return (x < 0.0 ? 1.0+incompGamma(0.5,x*x) : compIncompGamma(0.5,x*x));
00312 }
00313 catch(Exception& e) {
00314 e.addText("Called by compErrorFunc()");
00315 GPSTK_RETHROW(e);
00316 }
00317 }
00318
00325 double ChisqProbability(const double& x, const int& n) throw(Exception)
00326 {
00327 if(x <= 0) {
00328 Exception e("Non-positive chi-sq argument in ChisqProbability()");
00329 GPSTK_THROW(e);
00330 }
00331 if(n < 0) {
00332 Exception e("Non-positive degrees of freedom in ChisqProbability()");
00333 GPSTK_THROW(e);
00334 }
00335
00336 return incompGamma(double(n)/2.0,x/2.0);
00337 }
00338
00345 double CompChisqProbability(const double& x, const int& n) throw(Exception)
00346 {
00347 if(x <= 0) {
00348 Exception e("Non-positive chi-sq argument in CompChisqProbability()");
00349 GPSTK_THROW(e);
00350 }
00351 if(n < 0) {
00352 Exception e("Non-positive degrees of freedom in CompChisqProbability()");
00353 GPSTK_THROW(e);
00354 }
00355
00356 return compIncompGamma(double(n)/2.0,x/2.0);
00357 }
00358
00359
00361 double cfIBeta(const double& x, const double& a, const double& b) throw(Exception)
00362 {
00363 static const int imax(100);
00364 static const double eps(10*std::numeric_limits<double>().epsilon());
00365 static const double fpmin(10*std::numeric_limits<double>().min());
00366 const double qab(a+b);
00367 const double qap(a+1.0);
00368 const double qam(a-1.0);
00369 double c(1),d(1-qab*x/qap),aa,del;
00370 if(::fabs(d) < fpmin) d=fpmin;
00371 d = 1.0/d;
00372 double h(d);
00373 int i,i2;
00374 for(i=1; i<=imax; i++) {
00375 i2 = 2*i;
00376 aa = i*(b-i)*x/((qam+i2)*(a+i2));
00377 d = 1.0 + aa*d;
00378 if(::fabs(d) < fpmin) d=fpmin;
00379 c = 1.0 + aa/c;
00380 if(::fabs(c) < fpmin) c=fpmin;
00381 d = 1.0/d;
00382 h *= d*c;
00383 aa = -(a+i)*(qab+i)*x/((a+i2)*(qap+i2));
00384 d = 1.0 + aa*d;
00385 if(::fabs(d) < fpmin) d=fpmin;
00386 c = 1.0 + aa/c;
00387 if(::fabs(c) < fpmin) c=fpmin;
00388 d = 1.0/d;
00389 del = d*c;
00390 h *= del;
00391 if(::fabs(del-1.0) < eps) break;
00392 }
00393 if(i > imax) {
00394 Exception e("Overflow in cfIBeta(); a or b too big");
00395 GPSTK_THROW(e);
00396 }
00397 return h;
00398 }
00399
00406 double incompleteBeta(const double& x, const double& a, const double& b)
00407 throw(Exception)
00408 {
00409 if(x < 0 || x > 1) {
00410 Exception e("Invalid x argument in incompleteBeta()");
00411 GPSTK_THROW(e);
00412 }
00413 if(a <= 0 || b <= 0) {
00414 Exception e("Non-positive argument in incompleteBeta()");
00415 GPSTK_THROW(e);
00416 }
00417
00418 if(x == 0) return 0.0;
00419 if(x == 1) return 1.0;
00420
00421 try {
00422 double factor = ::exp(lnGamma(a+b) - lnGamma(a) - lnGamma(b)
00423 + a * ::log(x) + b * ::log(1.0-x));
00424 if(x < (a+1.0)/(a+b+2.0))
00425 return factor*cfIBeta(x,a,b)/a;
00426 else
00427 return 1.0-factor*cfIBeta(1.0-x,b,a)/b;
00428 }
00429 catch(Exception& e) {
00430 e.addText("Called by incompleteBeta()");
00431 GPSTK_RETHROW(e);
00432 }
00433 }
00434
00446 double StudentsDistProbability(const double& t, const int& n) throw(Exception)
00447 {
00448 if(n <= 0) {
00449 Exception e("Non-positive degrees of freedom in StudentsDistribution()");
00450 GPSTK_THROW(e);
00451 }
00452
00453 return (1.0 - incompleteBeta(double(n)/(t*t+double(n)),double(n)/2,0.5));
00454 }
00455
00472 double FDistProbability(const double& f, const int& n1, const int& n2)
00473 throw(Exception)
00474 {
00475 if(f < 0) {
00476 Exception e("Negative statistic in FDistribution()");
00477 GPSTK_THROW(e);
00478 }
00479 if(n1 <= 0 || n2 <= 0) {
00480 Exception e("Non-positive degrees of freedom in FDistribution()");
00481 GPSTK_THROW(e);
00482 }
00483
00484 return incompleteBeta(double(n2)/(double(n2)+double(n1)*f),
00485 double(n2)/2.0,double(n1)/2.0);
00486 }
00487
00488 }
00489
00490 #endif // SPECIAL_FUNCTIONS_INCLUDE