00001 #ifndef Impala_Core_Feature_WeibullNgbPnLoop_h
00002 #define Impala_Core_Feature_WeibullNgbPnLoop_h
00003
00004 #include "Core/Array/Pattern/Categories.h"
00005 #include "Core/Array/Pattern/Cnum.h"
00006 #include "Core/Array/Element/Vec3Real64.h"
00007 #include "Core/Array/Arrays.h"
00008 #include "Core/Array/Sort.h"
00009 #include "Core/Array/MulVal.h"
00010 #include "Core/Array/Set.h"
00011 #include "Core/Array/PrintData.h"
00012
00013 #include "limits.h"
00014 #include "float.h"
00015 #include <errno.h>
00016
00017 namespace Impala
00018 {
00019 namespace Core
00020 {
00021 namespace Feature
00022 {
00023
00024
00025 #ifndef M_PI
00026 #define M_PI 3.1415926535897932384626433832795029
00027 #endif
00028 #ifndef MMGAMMA
00029 #define MMGAMMA 0.5772156649015328606065120900824024
00030 #endif
00031 #ifndef M_LN2
00032 #define M_LN2 0.6931471805599453094172321214581766
00033 #endif
00034
00035
00038 template<class DstArrayT, class SrcArrayT>
00039 class WeibullNgbPnLoop
00040 {
00041 public:
00042
00044 typedef Array::Pattern::TagLoop IteratorCategory;
00045
00047 typedef Array::Pattern::TagNPhase PhaseCategory;
00048
00049 typedef typename DstArrayT::StorType DstStorType;
00050 typedef typename DstArrayT::ArithType DstArithType;
00051 typedef typename SrcArrayT::StorType SrcStorType;
00052 typedef typename SrcArrayT::ArithType SrcArithType;
00053
00054
00056 WeibullNgbPnLoop(bool verbose, int ngbWidth, int ngbHeight)
00057 {
00058 mNgbWidth = ngbWidth;
00059 mNgbHeight = ngbHeight;
00060 mVerbose = verbose;
00061 mFrac = mNgbWidth*mNgbHeight;
00062 mGamma = 0.1;
00063 mGammaNext = 0.0;
00064 mLast = false;
00065 mData = new SrcArrayT(mFrac, 1, 0, 0);
00066 mEcdf = new SrcArrayT(mFrac, 1, 0, 0);
00067 }
00068
00070 ~WeibullNgbPnLoop()
00071 {
00072 delete mData;
00073 delete mEcdf;
00074 }
00076 int
00077 Width()
00078 {
00079 return mNgbWidth;
00080 }
00081
00083 int
00084 Height()
00085 {
00086 return mNgbHeight;
00087 }
00088
00090 void
00091 Init(int phase, int x, int y, SrcArithType value)
00092 {
00093 if(mLast) return;
00094 int ph = phase%2;
00095
00096
00097
00098
00099 if (phase == 1)
00100 {
00101
00102
00103 mGamma = 0.1;
00104 mGammaNext = 0.0;
00105 mPrecision = 0.005;
00106 mMaxNumIters = 20;
00107 mIter = 1;
00108 mMaxGamma = 25;
00109 }
00110 mPhase = phase;
00111 if (ph)
00112 {
00113 mSumDataPowerGamma = 0.0;
00114 mSumDataLog = 0.0;
00115 mF = 0.0;
00116 mFprime = 0.0;
00117 }
00118
00119 }
00120
00122 void
00123 NextEl(int x, int y, SrcArithType value)
00124 {
00125 SrcArithType dataPowerGamma;
00126 SrcArithType sumItem;
00127 SrcArithType data = value + 0.000001;
00128 dataPowerGamma = pow(data, mGamma);
00129 int phase = mPhase%2;
00130
00131 if(mVerbose)
00132 if (mGamma == 0.1 && phase == 0)
00133 std::cout << value << std::endl;
00134
00135
00136
00137 if (mLast)
00138 {
00139 mData->SetValue(value, x*mNgbWidth + y, 0);
00140 return;
00141 }
00142 switch (phase)
00143 {
00144 case 1 :
00145 mSumDataPowerGamma += dataPowerGamma;
00146 mSumDataLog += dataPowerGamma*log(data);
00147 break;
00148
00149 case 0 :
00150 sumItem = dataPowerGamma/mSumDataPowerGamma;
00151 mF += log(mFrac*sumItem) - mFrac*(sumItem)*log(mFrac*sumItem);
00152 mFprime += (mSumDataPowerGamma/dataPowerGamma - mFrac*(log(mFrac*sumItem) + 1)) *
00153 (dataPowerGamma*(log(data)*mSumDataPowerGamma - mSumDataLog))/
00154 (mSumDataPowerGamma*mSumDataPowerGamma);
00155 break;
00156 }
00157
00158 }
00159
00161 void
00162 Done(int phase)
00163 {
00164
00165 int ph = phase%2;
00166 if (mVerbose)
00167 std::cout << "done: phase = " << phase << std::endl;
00168 if(!ph)
00169 {
00170 if (mVerbose)
00171 std::cout << "mGamma = " << mGamma << std::endl;
00172
00173 mF = mFrac + mF;
00174 mGammaNext = mGamma - mF/mFprime;
00175 if (mVerbose)
00176 std::cout << "mGammaNext = " << mGammaNext << std::endl;
00177 }
00178
00179 }
00180
00182 bool
00183 HasNextPhase(int lastPhase)
00184 {
00185 if(mLast)
00186 {
00187 mLast = false;
00188 return false;
00189 }
00190 int ph = lastPhase%2;
00191 if (mVerbose)
00192 std::cout << " WeibullNgbPnLoop::hasNext(" << lastPhase << ") "
00193 << std::endl;
00194
00195 if (ph)
00196 return true;
00197
00198 else
00199 {
00200 if (fabs(mGamma - mGammaNext) < mPrecision || mIter > mMaxNumIters ||
00201 mGammaNext > mMaxGamma || isnan(mGammaNext) || isinf(mGammaNext) || mGammaNext < 0)
00202 {
00203 if (mVerbose)
00204 {
00205 std::cout << "no next phase! " << "fabs(mGamma - mGammaNext) = " << fabs(mGamma - mGammaNext) << std::endl;
00206 std::cout << "mIter = " << mIter << " maxIter = " << mMaxNumIters << std::endl;
00207 std::cout << "maxGamma = " << mMaxGamma << std::endl;
00208 std::cout << "mGamma = " << mGamma << " mGammaNext = " << mGammaNext << std::endl;
00209 }
00210
00211 mIter = 1;
00212 mLast = true;
00213 return true;
00214 }
00215 else
00216 {
00217 if (mVerbose)
00218 std::cout << "there is next phase: iteration = " << mIter << std::endl;
00219 mGamma = mGammaNext;
00220 mIter++;
00221 return true;
00222 }
00223 }
00224 }
00225
00227 DstArithType
00228 Result()
00229 {
00230 double Beta = pow(mSumDataPowerGamma/mFrac, (1/mGamma));
00231 if(Beta == 0)
00232 Beta = 0.00000001;
00233 Core::Array::Sort(mEcdf, mData);
00234 if(mVerbose)
00235 {
00236 std::cout << "******** mData **********" << std::endl;
00237 Core::Array::PrintData(mData, false);
00238 std::cout << "******** mData sorted **********" << std::endl << std::endl;
00239 Core::Array::PrintData(mEcdf, false);
00240 }
00241 Core::Array::Set(mData, mEcdf);
00242 Core::Array::MulVal(mEcdf, mEcdf, 1/mData->Value(mFrac-1, 0));
00243 double F;
00244 double dF;
00245 double AD = 0.0;
00246 for (int i=0; i<mFrac; i++)
00247 {
00248 F = 1 - exp(-pow(mData->Value(i,0)/Beta, mGamma));
00249 dF = (mGamma/Beta)*pow((mData->Value(i,0)/Beta), (mGamma-1))*F;
00250 AD += pow((mEcdf->Value(i,0) - F), 2)*dF/(F*(1 - F));
00251 }
00252 AD = AD/mFrac;
00253 if (mVerbose)
00254 std::cout << "result: Gamma = " << mGamma << " Beta = " << Beta << " AD = " << AD << std::endl;
00255 return Core::Array::Element::Vec3Real64(Beta, mGamma, AD);
00256 }
00257
00258 double
00259 gammaFunc(double xx) const
00260 {
00261 double x,y,tmp,ser;
00262 static double cof[6] =
00263 {
00264 76.18009172947146, -86.50532032941677,
00265 24.01409824083091, -1.231739572450155,
00266 0.1208650973866179e-2, -0.5395239384953e-5
00267 };
00268
00269 int j;
00270 y=x=xx;
00271 tmp=x+5.5;
00272 tmp -= (x+0.5)*log(tmp);
00273 ser=1.000000000190015;
00274 for (j=0;j<=5;j++)
00275 ser += cof[j]/++y;
00276 xx = -tmp+log(2.5066282746310005*ser/x);
00277 return exp(xx);
00278 }
00279
00280 double
00281 DiGamma(double x) const
00282 {
00283
00284 if( x < 0.0 )
00285 return DiGamma(1.0-x)+M_PI/tan(M_PI*(1.0-x)) ;
00286 else if( x < 1.0 )
00287 return DiGamma(1.0+x)-1.0/x ;
00288 else if ( x == 1.0)
00289 return -MMGAMMA ;
00290 else if ( x == 2.0)
00291 return 1.0-MMGAMMA ;
00292 else if ( x == 3.0)
00293 return 1.5-MMGAMMA ;
00294 else if ( x > 3.0)
00295
00296 return 0.5*(DiGamma(x/2.0)+DiGamma((x+1.0)/2.0))+M_LN2 ;
00297 else
00298 {
00299 static double Kncoe[] =
00300 { .30459198558715155634315638246624251,
00301 .72037977439182833573548891941219706, -.12454959243861367729528855995001087,
00302 .27769457331927827002810119567456810e-1, -.67762371439822456447373550186163070e-2,
00303 .17238755142247705209823876688592170e-2, -.44817699064252933515310345718960928e-3,
00304 .11793660000155572716272710617753373e-3, -.31253894280980134452125172274246963e-4,
00305 .83173997012173283398932708991137488e-5, -.22191427643780045431149221890172210e-5,
00306 .59302266729329346291029599913617915e-6, -.15863051191470655433559920279603632e-6,
00307 .42459203983193603241777510648681429e-7, -.11369129616951114238848106591780146e-7,
00308 .304502217295931698401459168423403510e-8, -.81568455080753152802915013641723686e-9,
00309 .21852324749975455125936715817306383e-9, -.58546491441689515680751900276454407e-10,
00310 .15686348450871204869813586459513648e-10, -.42029496273143231373796179302482033e-11,
00311 .11261435719264907097227520956710754e-11, -.30174353636860279765375177200637590e-12,
00312 .80850955256389526647406571868193768e-13, -.21663779809421233144009565199997351e-13,
00313 .58047634271339391495076374966835526e-14, -.15553767189204733561108869588173845e-14,
00314 .41676108598040807753707828039353330e-15, -.11167065064221317094734023242188463e-15 } ;
00315
00316 double Tn_1 = 1.0 ;
00317 double Tn = x-2.0 ;
00318 double resul = Kncoe[0] + Kncoe[1]*Tn ;
00319
00320 x -= 2.0 ;
00321
00322 for(int n = 2 ; n < sizeof(Kncoe)/sizeof(double) ;n++)
00323 {
00324 const double Tn1 = 2.0 * x * Tn - Tn_1 ;
00325 resul += Kncoe[n]*Tn1 ;
00326 Tn_1 = Tn ;
00327 Tn = Tn1 ;
00328 }
00329 return resul ;
00330 }
00331 }
00332
00335 #define ISNAN(x) (isnan(x) !=0)
00336 #define ML_POSINF (1.0 / 0.0)
00337 #define ML_NEGINF ((-1.0) / 0.0)
00338 #define ML_NAN (0.0 / 0.0)
00339 #define n_max (100)
00340 #define M_LOG10_2 0.30102999566398119521
00341
00342 int Rf_i1mach(int i)
00343 {
00344 switch(i)
00345 {
00346
00347 case 1: return 5;
00348 case 2: return 6;
00349 case 3: return 0;
00350 case 4: return 0;
00351
00352 case 5: return CHAR_BIT * sizeof(int);
00353 case 6: return sizeof(int)/sizeof(char);
00354
00355 case 7: return 2;
00356 case 8: return CHAR_BIT * sizeof(int) - 1;
00357 case 9: return INT_MAX;
00358
00359 case 10: return FLT_RADIX;
00360
00361 case 11: return FLT_MANT_DIG;
00362 case 12: return FLT_MIN_EXP;
00363
00364 case 13: return FLT_MAX_EXP;
00365
00366 case 14: return DBL_MANT_DIG;
00367 case 15: return DBL_MIN_EXP;
00368 case 16: return DBL_MAX_EXP;
00369
00370 default: return 0;
00371 }
00372 }
00373
00374 double Rf_d1mach(int i)
00375 {
00376 switch(i)
00377 {
00378
00379 case 1: return DBL_MIN;
00380 case 2: return DBL_MAX;
00381
00382 case 3:
00383
00384 return 0.5*DBL_EPSILON;
00385
00386 case 4:
00387
00388 return DBL_EPSILON;
00389
00390 case 5: return M_LOG10_2;
00391
00392 default: return 0.0;
00393 }
00394 }
00395
00396 double fmax2(double x, double y)
00397 {
00398 #ifdef IEEE_754
00399 if (ISNAN(x) || ISNAN(y))
00400 return x + y;
00401 #endif
00402 return (x < y) ? y : x;
00403 }
00404
00405 double fmin2(double x, double y)
00406 {
00407 #ifdef IEEE_754
00408 if (ISNAN(x) || ISNAN(y))
00409 return x + y;
00410 #endif
00411 return (x < y) ? x : y;
00412 }
00413
00414 int imin2(int x, int y)
00415 {
00416 return (x < y) ? x : y;
00417 }
00418
00419 void dpsifn(double x, int n, int kode, int m, double *ans, int *nz, int *ierr)
00420 {
00421 const static double bvalues[] = {
00422 1.00000000000000000e+00,
00423 -5.00000000000000000e-01,
00424 1.66666666666666667e-01,
00425 -3.33333333333333333e-02,
00426 2.38095238095238095e-02,
00427 -3.33333333333333333e-02,
00428 7.57575757575757576e-02,
00429 -2.53113553113553114e-01,
00430 1.16666666666666667e+00,
00431 -7.09215686274509804e+00,
00432 5.49711779448621554e+01,
00433 -5.29124242424242424e+02,
00434 6.19212318840579710e+03,
00435 -8.65802531135531136e+04,
00436 1.42551716666666667e+06,
00437 -2.72982310678160920e+07,
00438 6.01580873900642368e+08,
00439 -1.51163157670921569e+10,
00440 4.29614643061166667e+11,
00441 -1.37116552050883328e+13,
00442 4.88332318973593167e+14,
00443 -1.92965793419400681e+16
00444 };
00445
00446 int i, j, k, mm, mx, nn, np, nx, fn;
00447 double arg, den, elim, eps, fln, fx, rln, rxsq,
00448 r1m4, r1m5, s, slope, t, ta, tk, tol, tols, tss, tst,
00449 tt, t1, t2, wdtol, xdmln, xdmy, xinc, xln = 0.0 ,
00450 xm, xmin, xq, yint;
00451 double trm[23], trmr[n_max + 1];
00452
00453 *ierr = 0;
00454 if (n < 0 || kode < 1 || kode > 2 || m < 1) {
00455 *ierr = 1;
00456 return;
00457 }
00458 if (x <= 0.) {
00459
00460
00461
00462 if (x == (long)x) {
00463
00464 for(j=0; j < m; j++)
00465 ans[j] = ((j+n) % 2) ? ML_POSINF : ML_NAN;
00466 return;
00467 }
00468 dpsifn(1. - x, n, 1, m, ans, nz, ierr);
00469
00470
00471
00472
00473
00474 if(m > 1 || n > 3) {
00475
00476 *ierr = 4; return;
00477 }
00478 x *= M_PI;
00479 if (n == 0)
00480 tt = cos(x)/sin(x);
00481 else if (n == 1)
00482 tt = -1/pow(sin(x),2);
00483 else if (n == 2)
00484 tt = 2*cos(x)/pow(sin(x),3);
00485 else if (n == 3)
00486 tt = -2*(2*pow(cos(x),2) + 1)/pow(sin(x),4);
00487 else
00488 tt = ML_NAN;
00489
00490
00491 s = (n % 2) ? -1. : 1.;
00492
00493
00494 t1 = t2 = s = 1.;
00495 for(k=0, j=k-n; j < m; k++, j++, s = -s) {
00496
00497 t1 *= M_PI;
00498 if(k >= 2)
00499 t2 *= k;
00500 if(j >= 0)
00501 ans[j] = s*(ans[j] + t1/t2 * tt);
00502 }
00503 if (n == 0 && kode == 2)
00504 ans[0] += xln;
00505 return;
00506 }
00507
00508 *nz = 0;
00509 mm = m;
00510 nx = imin2(-Rf_i1mach(15), Rf_i1mach(16));
00511 r1m5 = Rf_d1mach(5);
00512 r1m4 = Rf_d1mach(4) * 0.5;
00513 wdtol = fmax2(r1m4, 0.5e-18);
00514
00515
00516
00517 elim = 2.302 * (nx * r1m5 - 3.0);
00518 xln = log(x);
00519 for(;;) {
00520 nn = n + mm - 1;
00521 fn = nn;
00522 t = (fn + 1) * xln;
00523
00524
00525
00526 if (fabs(t) > elim) {
00527 if (t <= 0.0) {
00528 *nz = 0;
00529 *ierr = 2;
00530 return;
00531 }
00532 }
00533 else {
00534 if (x < wdtol) {
00535 ans[0] = pow(x, -n-1.0);
00536 if (mm != 1) {
00537 for(k = 1; k < mm ; k++)
00538 ans[k] = ans[k-1] / x;
00539 }
00540 if (n == 0 && kode == 2)
00541 ans[0] += xln;
00542 return;
00543 }
00544
00545
00546
00547 rln = r1m5 * Rf_i1mach(14);
00548 rln = fmin2(rln, 18.06);
00549 fln = fmax2(rln, 3.0) - 3.0;
00550 yint = 3.50 + 0.40 * fln;
00551 slope = 0.21 + fln * (0.0006038 * fln + 0.008677);
00552 xm = yint + slope * fn;
00553 mx = (int)xm + 1;
00554 xmin = mx;
00555 if (n != 0) {
00556 xm = -2.302 * rln - fmin2(0.0, xln);
00557 arg = xm / n;
00558 arg = fmin2(0.0, arg);
00559 eps = exp(arg);
00560 xm = 1.0 - eps;
00561 if (fabs(arg) < 1.0e-3)
00562 xm = -arg;
00563 fln = x * xm / eps;
00564 xm = xmin - x;
00565 if (xm > 7.0 && fln < 15.0)
00566 break;
00567 }
00568 xdmy = x;
00569 xdmln = xln;
00570 xinc = 0.0;
00571 if (x < xmin) {
00572 nx = (int)x;
00573 xinc = xmin - nx;
00574 xdmy = x + xinc;
00575 xdmln = log(xdmy);
00576 }
00577
00578
00579
00580 t = fn * xdmln;
00581 t1 = xdmln + xdmln;
00582 t2 = t + xdmln;
00583 tk = fmax2(fabs(t), fmax2(fabs(t1), fabs(t2)));
00584 if (tk <= elim)
00585 goto L10;
00586 }
00587 nz++;
00588 mm--;
00589 ans[mm] = 0.;
00590 if (mm == 0)
00591 return;
00592 }
00593 nn = (int)fln + 1;
00594 np = n + 1;
00595 t1 = (n + 1) * xln;
00596 t = exp(-t1);
00597 s = t;
00598 den = x;
00599 for(i=1; i <= nn; i++) {
00600 den += 1.;
00601 trm[i] = pow(den, (double)-np);
00602 s += trm[i];
00603 }
00604 ans[0] = s;
00605 if (n == 0 && kode == 2)
00606 ans[0] = s + xln;
00607
00608 if (mm != 1) {
00609
00610 tol = wdtol / 5.0;
00611 for(j = 1; j < mm; j++) {
00612 t /= x;
00613 s = t;
00614 tols = t * tol;
00615 den = x;
00616 for(i=1; i <= nn; i++) {
00617 den += 1.;
00618 trm[i] /= den;
00619 s += trm[i];
00620 if (trm[i] < tols)
00621 break;
00622 }
00623 ans[j] = s;
00624 }
00625 }
00626 return;
00627
00628 L10:
00629 tss = exp(-t);
00630 tt = 0.5 / xdmy;
00631 t1 = tt;
00632 tst = wdtol * tt;
00633 if (nn != 0)
00634 t1 = tt + 1.0 / fn;
00635 rxsq = 1.0 / (xdmy * xdmy);
00636 ta = 0.5 * rxsq;
00637 t = (fn + 1) * ta;
00638 s = t * bvalues[2];
00639 if (fabs(s) >= tst) {
00640 tk = 2.0;
00641 for(k = 4; k <= 22; k++) {
00642 t = t * ((tk + fn + 1)/(tk + 1.0))*((tk + fn)/(tk + 2.0)) * rxsq;
00643 trm[k] = t * bvalues[k-1];
00644 if (fabs(trm[k]) < tst)
00645 break;
00646 s += trm[k];
00647 tk += 2.;
00648 }
00649 }
00650 s = (s + t1) * tss;
00651 if (xinc != 0.0) {
00652
00653
00654
00655 nx = (int)xinc;
00656 np = nn + 1;
00657 if (nx > n_max) {
00658 *nz = 0;
00659 *ierr = 3;
00660 return;
00661 }
00662 else {
00663 if (nn==0)
00664 goto L20;
00665 xm = xinc - 1.0;
00666 fx = x + xm;
00667
00668
00669 for(i = 1; i <= nx; i++) {
00670 trmr[i] = pow(fx, (double)-np);
00671 s += trmr[i];
00672 xm -= 1.;
00673 fx = x + xm;
00674 }
00675 }
00676 }
00677 ans[mm-1] = s;
00678 if (fn == 0)
00679 goto L30;
00680
00681
00682
00683 for(j = 2; j <= mm; j++) {
00684 fn--;
00685 tss *= xdmy;
00686 t1 = tt;
00687 if (fn!=0)
00688 t1 = tt + 1.0 / fn;
00689 t = (fn + 1) * ta;
00690 s = t * bvalues[2];
00691 if (fabs(s) >= tst) {
00692 tk = 4 + fn;
00693 for(k=4; k <= 22; k++) {
00694 trm[k] = trm[k] * (fn + 1) / tk;
00695 if (fabs(trm[k]) < tst)
00696 break;
00697 s += trm[k];
00698 tk += 2.;
00699 }
00700 }
00701 s = (s + t1) * tss;
00702 if (xinc != 0.0) {
00703 if (fn == 0)
00704 goto L20;
00705 xm = xinc - 1.0;
00706 fx = x + xm;
00707 for(i=1 ; i<=nx ; i++) {
00708 trmr[i] = trmr[i] * fx;
00709 s += trmr[i];
00710 xm -= 1.;
00711 fx = x + xm;
00712 }
00713 }
00714 ans[mm - j] = s;
00715 if (fn == 0)
00716 goto L30;
00717 }
00718 return;
00719
00720 L20:
00721 for(i = 1; i <= nx; i++)
00722 s += 1. / (x + nx - i);
00723
00724 L30:
00725 if (kode!=2)
00726 ans[0] = s - xdmln;
00727 else if (xdmy != x) {
00728 xq = xdmy / x;
00729 ans[0] = s - log(xq);
00730 }
00731 return;
00732 }
00733
00734 double TriGamma(double x)
00735 {
00736 double ans;
00737 int nz, ierr;
00738 if(ISNAN(x)) return x;
00739 dpsifn(x, 1, 1, 1, &ans, &nz, &ierr);
00740 if(ierr != 0)
00741 {
00742 errno = EDOM;
00743 return ML_NAN;
00744 }
00745 return ans;
00746 }
00747
00748
00749
00750 private:
00751 int mNgbWidth;
00752 int mNgbHeight;
00753 int mFrac;
00754 SrcArithType mFactor;
00755 SrcArithType mGamma;
00756 SrcArithType mGammaNext;
00757 SrcArithType mMaxGamma;
00758 SrcArithType mSumDataPowerGamma;
00759 SrcArithType mSumDataLog;
00760 SrcArithType mF;
00761 SrcArithType mFprime;
00762 SrcArithType mPrecision;
00763 SrcArrayT* mEcdf;
00764 SrcArrayT* mData;
00765 int mIter;
00766 int mMaxNumIters;
00767 int mPhase;
00768 bool mVerbose;
00769 bool mLast;
00770 };
00771
00772 }
00773 }
00774 }
00775
00776 #endif