#pragma once #include #include namespace cgv { namespace math { //helper function to compute cheb approximation of erf functions template T erfccheb(const T z) { static const int ncof=28; static const double cof[28] = {-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2,-9.561514786808631e-3,-9.46595344482036e-4, 3.66839497852761e-4,4.2523324806907e-5,-2.0278578112534e-5, -1.624290004647e-6,1.303655835580e-6,1.5626441722e-8,-8.5238095915e-8, 6.529054439e-9,5.059343495e-9,-9.91364156e-10,-2.27365122e-10, 9.6467911e-11, 2.394038e-12,-6.886027e-12,8.94487e-13, 3.13092e-13, -1.12708e-13,3.81e-16,7.106e-15,-1.523e-15,-9.4e-17,1.21e-16,-2.8e-17}; int j; double t,ty,tmp,d=0.,dd=0.; assert(z >= 0.); t = 2./(2.+z); ty = 4.*t - 2.; for (j=ncof-1;j>0;j--) { tmp = d; d = ty*d - dd + cof[j]; dd = tmp; } return (T)(t*exp(-z*z + 0.5*(cof[0] + ty*d) - dd)); } ///error function template T erf(const T x) { if (x >=0.) return 1.0 - erfccheb(x); else return erfccheb(-x) - 1.0; } ///complementary error function template T erfc(const T x) { if (x >= 0.) return erfccheb(x); else return 2.0 - erfccheb(-x); } ///scaled complementary error function template T erfcx(const T x) { return exp(x*x)* erfccheb(x); } ///inverse complementary error function template T erfc_inv(const T p) { double x,err,t,pp; if (p >= 2.0) return -100.; if (p <= 0.0) return 100.; pp = (p < 1.0)? p : 2. - p; t = sqrt(-2.*log(pp/2.)); x = -0.70711*((2.30753+t*0.27061)/(1.+t*(0.99229+t*0.04481)) - t); for (int j=0;j<2;j++) { err = erfc(x) - pp; x += err/(1.12837916709551257*exp(-sqr(x))-x*err); } return (p < 1.0? x : -x); } ///inverse error function template T erf_inv(const T p) { return erfc_inv(1.-p); } ///normal distribution prob density function template T norm_pdf(const T x,const T mu=0,const T sig=1) { return (T) (0.398942280401432678/sig)*exp(-0.5*sqr((x-mu)/sig)); } ///normal cumulative distribution function template T norm_cdf(const T x,const T mu=0,const T sig=1) { return 0.5*erfc(-0.707106781186547524*(x-mu)/sig); } ///inverse normal cumulative distribution function template T norm_inv(const T& p,const T mu=0,const T sig=1) { assert(p >= 0 && p <= 1); return -1.41421356237309505*sig*erfc_inv(2.*p)+mu; } ///lognormal normal prob distribution function template T logn_pdf(const T x,const T mu=0,const T sig=1) { assert (x >= 0.); if (x == 0.) return 0.; return (0.398942280401432678/(sig*x))*exp(-0.5*sqr((log(x)-mu)/sig)); } ///log normal cumulative distribution function template T logn_cdf(const T x, const T mu=0,const T sig=1) { assert(x >= 0.); if (x == 0.) return 0.; return (T)(0.5*erfc(-0.707106781186547524*(log(x)-mu)/sig)); } ///inverse log normal cumulative distribution function template T logn_inv(const T p, const T mu=0,const T sig=1) { assert (p > 0. && p < 1.); return exp(-1.41421356237309505*sig*erfc_inv(2.*p)+mu); } ///uniform distribution prob density function template T uniform_pdf(const T x,const T a,const T b) { if(x < a) return 0; if(x > b) return 0; return 1.0/(b-a); } ///uniform cumulative distribution function template T uniform_cdf(const T x,const T a,const T b) { if(x < a) return 0; if(x >=b) return 1; return (x-a)/(b-a); } //cumulative kolmogorov-smirnov distribution function template T ks_cdf(const T z) { assert(z >= 0.); if (z == 0.) return 0.; if (z < 1.18) { double y = exp(-1.23370055013616983/sqr(z)); return (T)(2.25675833419102515*sqrt(-log(y)) *(y + pow(y,9) + pow(y,25) + pow(y,49))); } else { double x = exp(-2.*sqr(z)); return (T)(1. - 2.*(x - pow(x,4) + pow(x,9))); } } ///complementary cumulative kolmogorov-smirnov distribution function template T ksc_cdf(const T z) { assert (z >= 0.) ; if (z == 0.) return 1.; if (z < 1.18) return 1.-ks_cdf(z); double x = exp(-2.*sqr(z)); return (T)(2.*(x - pow(x,4) + pow(x,9))); } ///helper function to compute ksc_inv template T invxlogx(const T y) { const double ooe = 0.367879441171442322; double t,u,to=0.; assert(y < 0. && y > -ooe); if (y < -0.2) u = log(ooe-sqrt(2*ooe*(y+ooe))); else u = -10.; do { u += (t=(log(y/u)-u)*(u/(1.+u))); if (t < 1.e-8 && abs(t+to)<0.01*abs(t)) break; to = t; } while (abs(t/u) > 1.e-15); return(T) exp(u); } ///inverse complementary cumulative kolmogorv-smirnov distribution function template T ksc_inv(const T q) { double y,logy,yp,x,xp,f,ff,u,t; assert (q > 0. && q <= 1.) if (q == 1.) return 0.; if (q > 0.3) { f = -0.392699081698724155*SQR(1.-q); y = invxlogx(f); do { yp = y; logy = log(y); ff = f/sqr(1.+ pow(y,4)+ pow(y,12)); u = (y*logy-ff)/(1.+logy); y = y - (t=u/std::max(0.5,1.-0.5*u/(y*(1.+logy)))); } while (abs(t/y)>1.e-15); return (T)(1.57079632679489662/sqrt(-log(y))); } else { x = 0.03; do { xp = x; x = 0.5*q+pow(x,4)-pow(x,9); if (x > 0.06) x += pow(x,16)-pow(x,25); } while (abs((xp-x)/x)>1.e-15); return (T)sqrt(-0.5*log(x)); } } ///inverse of the cumulative kolmogorv-smirnov distribution function template T ks_inv(const T p) { return ksc_inv(1.-p); } ///kolmogorov-smirnov test for comparing samples with given normal distribution ///returns the p-value for the null hypothesis that the data is drawn from a normal distribution defined by mu and sigma template T norm_ks_test(const T mu,const T sig,vec data) { sort_values(data); unsigned n = data.size(); T fo=0; T dt,ff,fn,en,d=0.0; en=(T)n; for(unsigned i = 0; i < n; i++) { fn = (T)(i+1.0)/en; ff = norm_cdf(data(i),mu,sig); dt = std::max(std::abs(fo-ff),std::abs(fn-ff)); if(dt > d) d=dt; fo=fn; } en=sqrt(en); return ksc_cdf((en+0.12+0.11/en)*d); } ///kolmogorov-smirnov test for comparing two sampled distributions ///returns the p-value for the null hypothesis that the the samples data1 are drawn from the same distribution as the samples of data2 template T ks_test(vec data1, vec data2) { int j1=0,j2=0,n1=data1.size(),n2=data2.size(); T d1,d2,dt,en1,en2,en,fn1=0.0,fn2=0.0; KSdist ks; sort_values(data1); sort_values(data2); en1=n1; en2=n2; T d=0.0; while (j1 < n1 && j2 < n2) { if ((d1=data1(j1)) <= (d2=data2(j2))) do fn1=++j1/en1; while (j1 < n1 && d1 == data1(j1)); if (d2 <= d1) do fn2=++j2/en2; while (j2 < n2 && d2 == data2(j2)); if ((dt=abs(fn2-fn1)) > d) d=dt; } en=sqrt(en1*en2/(en1+en2)); return ksc_cdf((en+0.12+0.11/en)*d); } } }