Irrlicht 3D Engine
irrMath.h
Go to the documentation of this file.
00001 // Copyright (C) 2002-2012 Nikolaus Gebhardt
00002 // This file is part of the "Irrlicht Engine".
00003 // For conditions of distribution and use, see copyright notice in irrlicht.h
00004 
00005 #ifndef __IRR_MATH_H_INCLUDED__
00006 #define __IRR_MATH_H_INCLUDED__
00007 
00008 #include "IrrCompileConfig.h"
00009 #include "irrTypes.h"
00010 #include <math.h>
00011 #include <float.h>
00012 #include <stdlib.h> // for abs() etc.
00013 #include <limits.h> // For INT_MAX / UINT_MAX
00014 
00015 #if defined(_IRR_SOLARIS_PLATFORM_) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) || defined (_WIN32_WCE)
00016     #define sqrtf(X) (irr::f32)sqrt((irr::f64)(X))
00017     #define sinf(X) (irr::f32)sin((irr::f64)(X))
00018     #define cosf(X) (irr::f32)cos((irr::f64)(X))
00019     #define asinf(X) (irr::f32)asin((irr::f64)(X))
00020     #define acosf(X) (irr::f32)acos((irr::f64)(X))
00021     #define atan2f(X,Y) (irr::f32)atan2((irr::f64)(X),(irr::f64)(Y))
00022     #define ceilf(X) (irr::f32)ceil((irr::f64)(X))
00023     #define floorf(X) (irr::f32)floor((irr::f64)(X))
00024     #define powf(X,Y) (irr::f32)pow((irr::f64)(X),(irr::f64)(Y))
00025     #define fmodf(X,Y) (irr::f32)fmod((irr::f64)(X),(irr::f64)(Y))
00026     #define fabsf(X) (irr::f32)fabs((irr::f64)(X))
00027     #define logf(X) (irr::f32)log((irr::f64)(X))
00028 #endif
00029 
00030 #ifndef FLT_MAX
00031 #define FLT_MAX 3.402823466E+38F
00032 #endif
00033 
00034 #ifndef FLT_MIN
00035 #define FLT_MIN 1.17549435e-38F
00036 #endif
00037 
00038 namespace irr
00039 {
00040 namespace core
00041 {
00042 
00044 
00045     const s32 ROUNDING_ERROR_S32 = 0;
00046 #ifdef __IRR_HAS_S64
00047     const s64 ROUNDING_ERROR_S64 = 0;
00048 #endif
00049     const f32 ROUNDING_ERROR_f32 = 0.000001f;
00050     const f64 ROUNDING_ERROR_f64 = 0.00000001;
00051 
00052 #ifdef PI // make sure we don't collide with a define
00053 #undef PI
00054 #endif
00055 
00056     const f32 PI        = 3.14159265359f;
00057 
00059     const f32 RECIPROCAL_PI = 1.0f/PI;
00060 
00062     const f32 HALF_PI   = PI/2.0f;
00063 
00064 #ifdef PI64 // make sure we don't collide with a define
00065 #undef PI64
00066 #endif
00067 
00068     const f64 PI64      = 3.1415926535897932384626433832795028841971693993751;
00069 
00071     const f64 RECIPROCAL_PI64 = 1.0/PI64;
00072 
00074     const f32 DEGTORAD = PI / 180.0f;
00075 
00077     const f32 RADTODEG   = 180.0f / PI;
00078 
00080     const f64 DEGTORAD64 = PI64 / 180.0;
00081 
00083     const f64 RADTODEG64 = 180.0 / PI64;
00084 
00086 
00089     inline f32 radToDeg(f32 radians)
00090     {
00091         return RADTODEG * radians;
00092     }
00093 
00095 
00098     inline f64 radToDeg(f64 radians)
00099     {
00100         return RADTODEG64 * radians;
00101     }
00102 
00104 
00107     inline f32 degToRad(f32 degrees)
00108     {
00109         return DEGTORAD * degrees;
00110     }
00111 
00113 
00116     inline f64 degToRad(f64 degrees)
00117     {
00118         return DEGTORAD64 * degrees;
00119     }
00120 
00122     template<class T>
00123     inline const T& min_(const T& a, const T& b)
00124     {
00125         return a < b ? a : b;
00126     }
00127 
00129     template<class T>
00130     inline const T& min_(const T& a, const T& b, const T& c)
00131     {
00132         return a < b ? min_(a, c) : min_(b, c);
00133     }
00134 
00136     template<class T>
00137     inline const T& max_(const T& a, const T& b)
00138     {
00139         return a < b ? b : a;
00140     }
00141 
00143     template<class T>
00144     inline const T& max_(const T& a, const T& b, const T& c)
00145     {
00146         return a < b ? max_(b, c) : max_(a, c);
00147     }
00148 
00150     template<class T>
00151     inline T abs_(const T& a)
00152     {
00153         return a < (T)0 ? -a : a;
00154     }
00155 
00158     template<class T>
00159     inline T lerp(const T& a, const T& b, const f32 t)
00160     {
00161         return (T)(a*(1.f-t)) + (b*t);
00162     }
00163 
00165     template <class T>
00166     inline const T clamp (const T& value, const T& low, const T& high)
00167     {
00168         return min_ (max_(value,low), high);
00169     }
00170 
00172     // Note: We use the same trick as boost and use two template arguments to
00173     // avoid ambiguity when swapping objects of an Irrlicht type that has not
00174     // it's own swap overload. Otherwise we get conflicts with some compilers
00175     // in combination with stl.
00176     template <class T1, class T2>
00177     inline void swap(T1& a, T2& b)
00178     {
00179         T1 c(a);
00180         a = b;
00181         b = c;
00182     }
00183 
00185     inline bool equals(const f64 a, const f64 b, const f64 tolerance = ROUNDING_ERROR_f64)
00186     {
00187         return (a + tolerance >= b) && (a - tolerance <= b);
00188     }
00189 
00191     inline bool equals(const f32 a, const f32 b, const f32 tolerance = ROUNDING_ERROR_f32)
00192     {
00193         return (a + tolerance >= b) && (a - tolerance <= b);
00194     }
00195 
00197     //\result true when numbers have a ULP <= maxUlpDiff AND have the same sign.
00198     inline bool equalsByUlp(f32 a, f32 b, int maxUlpDiff)
00199     {
00200         // Based on the ideas and code from Bruce Dawson on
00201         // http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/
00202         // When floats are interpreted as integers the two nearest possible float numbers differ just
00203         // by one integer number. Also works the other way round, an integer of 1 interpreted as float
00204         // is for example the smallest possible float number.
00205         union Float_t
00206         {
00207             Float_t(float f1 = 0.0f) : f(f1) {}
00208             // Portable sign-extraction
00209             bool sign() const { return (i >> 31) != 0; }
00210 
00211             int i;
00212             float f;
00213         };
00214 
00215         Float_t fa(a);
00216         Float_t fb(b);
00217 
00218         // Different signs, we could maybe get difference to 0, but so close to 0 using epsilons is better.
00219         if ( fa.sign() != fb.sign() )
00220         {
00221             // Check for equality to make sure +0==-0
00222             if (fa.i == fb.i)
00223                 return true;
00224             return false;
00225         }
00226 
00227         // Find the difference in ULPs.
00228         int ulpsDiff = abs_(fa.i- fb.i);
00229         if (ulpsDiff <= maxUlpDiff)
00230             return true;
00231 
00232         return false;
00233     }
00234 
00235 #if 0
00236 
00237     inline bool equals(const s32 a, const s32 b)
00238     {
00239         return (a == b);
00240     }
00241 
00243     inline bool equals(const u32 a, const u32 b)
00244     {
00245         return (a == b);
00246     }
00247 #endif
00248 
00249     inline bool equals(const s32 a, const s32 b, const s32 tolerance = ROUNDING_ERROR_S32)
00250     {
00251         return (a + tolerance >= b) && (a - tolerance <= b);
00252     }
00253 
00255     inline bool equals(const u32 a, const u32 b, const s32 tolerance = ROUNDING_ERROR_S32)
00256     {
00257         return (a + tolerance >= b) && (a - tolerance <= b);
00258     }
00259 
00260 #ifdef __IRR_HAS_S64
00261 
00262     inline bool equals(const s64 a, const s64 b, const s64 tolerance = ROUNDING_ERROR_S64)
00263     {
00264         return (a + tolerance >= b) && (a - tolerance <= b);
00265     }
00266 #endif
00267 
00269     inline bool iszero(const f64 a, const f64 tolerance = ROUNDING_ERROR_f64)
00270     {
00271         return fabs(a) <= tolerance;
00272     }
00273 
00275     inline bool iszero(const f32 a, const f32 tolerance = ROUNDING_ERROR_f32)
00276     {
00277         return fabsf(a) <= tolerance;
00278     }
00279 
00281     inline bool isnotzero(const f32 a, const f32 tolerance = ROUNDING_ERROR_f32)
00282     {
00283         return fabsf(a) > tolerance;
00284     }
00285 
00287     inline bool iszero(const s32 a, const s32 tolerance = 0)
00288     {
00289         return ( a & 0x7ffffff ) <= tolerance;
00290     }
00291 
00293     inline bool iszero(const u32 a, const u32 tolerance = 0)
00294     {
00295         return a <= tolerance;
00296     }
00297 
00298 #ifdef __IRR_HAS_S64
00299 
00300     inline bool iszero(const s64 a, const s64 tolerance = 0)
00301     {
00302         return abs_(a) > tolerance;
00303     }
00304 #endif
00305 
00306     inline s32 s32_min(s32 a, s32 b)
00307     {
00308         const s32 mask = (a - b) >> 31;
00309         return (a & mask) | (b & ~mask);
00310     }
00311 
00312     inline s32 s32_max(s32 a, s32 b)
00313     {
00314         const s32 mask = (a - b) >> 31;
00315         return (b & mask) | (a & ~mask);
00316     }
00317 
00318     inline s32 s32_clamp (s32 value, s32 low, s32 high)
00319     {
00320         return s32_min(s32_max(value,low), high);
00321     }
00322 
00323     /*
00324         float IEEE-754 bit represenation
00325 
00326         0      0x00000000
00327         1.0    0x3f800000
00328         0.5    0x3f000000
00329         3      0x40400000
00330         +inf   0x7f800000
00331         -inf   0xff800000
00332         +NaN   0x7fc00000 or 0x7ff00000
00333         in general: number = (sign ? -1:1) * 2^(exponent) * 1.(mantissa bits)
00334     */
00335 
00336     typedef union { u32 u; s32 s; f32 f; } inttofloat;
00337 
00338     #define F32_AS_S32(f)       (*((s32 *) &(f)))
00339     #define F32_AS_U32(f)       (*((u32 *) &(f)))
00340     #define F32_AS_U32_POINTER(f)   ( ((u32 *) &(f)))
00341 
00342     #define F32_VALUE_0     0x00000000
00343     #define F32_VALUE_1     0x3f800000
00344     #define F32_SIGN_BIT        0x80000000U
00345     #define F32_EXPON_MANTISSA  0x7FFFFFFFU
00346 
00349 #ifdef IRRLICHT_FAST_MATH
00350     #define IR(x)                           ((u32&)(x))
00351 #else
00352     inline u32 IR(f32 x) {inttofloat tmp; tmp.f=x; return tmp.u;}
00353 #endif
00354 
00356     #define AIR(x)              (IR(x)&0x7fffffff)
00357 
00359 #ifdef IRRLICHT_FAST_MATH
00360     #define FR(x)                           ((f32&)(x))
00361 #else
00362     inline f32 FR(u32 x) {inttofloat tmp; tmp.u=x; return tmp.f;}
00363     inline f32 FR(s32 x) {inttofloat tmp; tmp.s=x; return tmp.f;}
00364 #endif
00365 
00367     #define IEEE_1_0            0x3f800000
00368 
00369     #define IEEE_255_0          0x437f0000
00370 
00371 #ifdef IRRLICHT_FAST_MATH
00372     #define F32_LOWER_0(f)      (F32_AS_U32(f) >  F32_SIGN_BIT)
00373     #define F32_LOWER_EQUAL_0(f)    (F32_AS_S32(f) <= F32_VALUE_0)
00374     #define F32_GREATER_0(f)    (F32_AS_S32(f) >  F32_VALUE_0)
00375     #define F32_GREATER_EQUAL_0(f)  (F32_AS_U32(f) <= F32_SIGN_BIT)
00376     #define F32_EQUAL_1(f)      (F32_AS_U32(f) == F32_VALUE_1)
00377     #define F32_EQUAL_0(f)      ( (F32_AS_U32(f) & F32_EXPON_MANTISSA ) == F32_VALUE_0)
00378 
00379     // only same sign
00380     #define F32_A_GREATER_B(a,b)    (F32_AS_S32((a)) > F32_AS_S32((b)))
00381 
00382 #else
00383 
00384     #define F32_LOWER_0(n)      ((n) <  0.0f)
00385     #define F32_LOWER_EQUAL_0(n)    ((n) <= 0.0f)
00386     #define F32_GREATER_0(n)    ((n) >  0.0f)
00387     #define F32_GREATER_EQUAL_0(n)  ((n) >= 0.0f)
00388     #define F32_EQUAL_1(n)      ((n) == 1.0f)
00389     #define F32_EQUAL_0(n)      ((n) == 0.0f)
00390     #define F32_A_GREATER_B(a,b)    ((a) > (b))
00391 #endif
00392 
00393 
00394 #ifndef REALINLINE
00395     #ifdef _MSC_VER
00396         #define REALINLINE __forceinline
00397     #else
00398         #define REALINLINE inline
00399     #endif
00400 #endif
00401 
00402 #if defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
00403 
00404     // 8-bit bools in borland builder
00405 
00407     REALINLINE u32 if_c_a_else_b ( const c8 condition, const u32 a, const u32 b )
00408     {
00409         return ( ( -condition >> 7 ) & ( a ^ b ) ) ^ b;
00410     }
00411 
00413     REALINLINE u32 if_c_a_else_0 ( const c8 condition, const u32 a )
00414     {
00415         return ( -condition >> 31 ) & a;
00416     }
00417 #else
00418 
00420     REALINLINE u32 if_c_a_else_b ( const s32 condition, const u32 a, const u32 b )
00421     {
00422         return ( ( -condition >> 31 ) & ( a ^ b ) ) ^ b;
00423     }
00424 
00426     REALINLINE u16 if_c_a_else_b ( const s16 condition, const u16 a, const u16 b )
00427     {
00428         return ( ( -condition >> 15 ) & ( a ^ b ) ) ^ b;
00429     }
00430 
00432     REALINLINE u32 if_c_a_else_0 ( const s32 condition, const u32 a )
00433     {
00434         return ( -condition >> 31 ) & a;
00435     }
00436 #endif
00437 
00438     /*
00439         if (condition) state |= m; else state &= ~m;
00440     */
00441     REALINLINE void setbit_cond ( u32 &state, s32 condition, u32 mask )
00442     {
00443         // 0, or any postive to mask
00444         //s32 conmask = -condition >> 31;
00445         state ^= ( ( -condition >> 31 ) ^ state ) & mask;
00446     }
00447 
00448     inline f32 round_( f32 x )
00449     {
00450         return floorf( x + 0.5f );
00451     }
00452 
00453     REALINLINE void clearFPUException ()
00454     {
00455 #ifdef IRRLICHT_FAST_MATH
00456         return;
00457 #ifdef feclearexcept
00458         feclearexcept(FE_ALL_EXCEPT);
00459 #elif defined(_MSC_VER)
00460         __asm fnclex;
00461 #elif defined(__GNUC__) && defined(__x86__)
00462         __asm__ __volatile__ ("fclex \n\t");
00463 #else
00464 #  warn clearFPUException not supported.
00465 #endif
00466 #endif
00467     }
00468 
00469     // calculate: sqrt ( x )
00470     REALINLINE f32 squareroot(const f32 f)
00471     {
00472         return sqrtf(f);
00473     }
00474 
00475     // calculate: sqrt ( x )
00476     REALINLINE f64 squareroot(const f64 f)
00477     {
00478         return sqrt(f);
00479     }
00480 
00481     // calculate: sqrt ( x )
00482     REALINLINE s32 squareroot(const s32 f)
00483     {
00484         return static_cast<s32>(squareroot(static_cast<f32>(f)));
00485     }
00486 
00487 #ifdef __IRR_HAS_S64
00488     // calculate: sqrt ( x )
00489     REALINLINE s64 squareroot(const s64 f)
00490     {
00491         return static_cast<s64>(squareroot(static_cast<f64>(f)));
00492     }
00493 #endif
00494 
00495     // calculate: 1 / sqrt ( x )
00496     REALINLINE f64 reciprocal_squareroot(const f64 x)
00497     {
00498         return 1.0 / sqrt(x);
00499     }
00500 
00501     // calculate: 1 / sqrtf ( x )
00502     REALINLINE f32 reciprocal_squareroot(const f32 f)
00503     {
00504 #if defined ( IRRLICHT_FAST_MATH )
00505     #if defined(_MSC_VER)
00506         // SSE reciprocal square root estimate, accurate to 12 significant
00507         // bits of the mantissa
00508         f32 recsqrt;
00509         __asm rsqrtss xmm0, f           // xmm0 = rsqrtss(f)
00510         __asm movss recsqrt, xmm0       // return xmm0
00511         return recsqrt;
00512 
00513 /*
00514         // comes from Nvidia
00515         u32 tmp = (u32(IEEE_1_0 << 1) + IEEE_1_0 - *(u32*)&x) >> 1;
00516         f32 y = *(f32*)&tmp;
00517         return y * (1.47f - 0.47f * x * y * y);
00518 */
00519     #else
00520         return 1.f / sqrtf(f);
00521     #endif
00522 #else // no fast math
00523         return 1.f / sqrtf(f);
00524 #endif
00525     }
00526 
00527     // calculate: 1 / sqrtf( x )
00528     REALINLINE s32 reciprocal_squareroot(const s32 x)
00529     {
00530         return static_cast<s32>(reciprocal_squareroot(static_cast<f32>(x)));
00531     }
00532 
00533     // calculate: 1 / x
00534     REALINLINE f32 reciprocal( const f32 f )
00535     {
00536 #if defined (IRRLICHT_FAST_MATH)
00537 
00538         // SSE Newton-Raphson reciprocal estimate, accurate to 23 significant
00539         // bi ts of the mantissa
00540         // One Newtown-Raphson Iteration:
00541         // f(i+1) = 2 * rcpss(f) - f * rcpss(f) * rcpss(f)
00542         f32 rec;
00543         __asm rcpss xmm0, f               // xmm0 = rcpss(f)
00544         __asm movss xmm1, f               // xmm1 = f
00545         __asm mulss xmm1, xmm0            // xmm1 = f * rcpss(f)
00546         __asm mulss xmm1, xmm0            // xmm2 = f * rcpss(f) * rcpss(f)
00547         __asm addss xmm0, xmm0            // xmm0 = 2 * rcpss(f)
00548         __asm subss xmm0, xmm1            // xmm0 = 2 * rcpss(f)
00549                                           //        - f * rcpss(f) * rcpss(f)
00550         __asm movss rec, xmm0             // return xmm0
00551         return rec;
00552 
00553 
00555         // instead set f to a high value to get a return value near zero..
00556         // -1000000000000.f.. is use minus to stay negative..
00557         // must test's here (plane.normal dot anything ) checks on <= 0.f
00558         //u32 x = (-(AIR(f) != 0 ) >> 31 ) & ( IR(f) ^ 0xd368d4a5 ) ^ 0xd368d4a5;
00559         //return 1.f / FR ( x );
00560 
00561 #else // no fast math
00562         return 1.f / f;
00563 #endif
00564     }
00565 
00566     // calculate: 1 / x
00567     REALINLINE f64 reciprocal ( const f64 f )
00568     {
00569         return 1.0 / f;
00570     }
00571 
00572 
00573     // calculate: 1 / x, low precision allowed
00574     REALINLINE f32 reciprocal_approxim ( const f32 f )
00575     {
00576 #if defined( IRRLICHT_FAST_MATH)
00577 
00578         // SSE Newton-Raphson reciprocal estimate, accurate to 23 significant
00579         // bi ts of the mantissa
00580         // One Newtown-Raphson Iteration:
00581         // f(i+1) = 2 * rcpss(f) - f * rcpss(f) * rcpss(f)
00582         f32 rec;
00583         __asm rcpss xmm0, f               // xmm0 = rcpss(f)
00584         __asm movss xmm1, f               // xmm1 = f
00585         __asm mulss xmm1, xmm0            // xmm1 = f * rcpss(f)
00586         __asm mulss xmm1, xmm0            // xmm2 = f * rcpss(f) * rcpss(f)
00587         __asm addss xmm0, xmm0            // xmm0 = 2 * rcpss(f)
00588         __asm subss xmm0, xmm1            // xmm0 = 2 * rcpss(f)
00589                                           //        - f * rcpss(f) * rcpss(f)
00590         __asm movss rec, xmm0             // return xmm0
00591         return rec;
00592 
00593 
00594 /*
00595         // SSE reciprocal estimate, accurate to 12 significant bits of
00596         f32 rec;
00597         __asm rcpss xmm0, f             // xmm0 = rcpss(f)
00598         __asm movss rec , xmm0          // return xmm0
00599         return rec;
00600 */
00601 /*
00602         register u32 x = 0x7F000000 - IR ( p );
00603         const f32 r = FR ( x );
00604         return r * (2.0f - p * r);
00605 */
00606 #else // no fast math
00607         return 1.f / f;
00608 #endif
00609     }
00610 
00611 
00612     REALINLINE s32 floor32(f32 x)
00613     {
00614 #ifdef IRRLICHT_FAST_MATH
00615         const f32 h = 0.5f;
00616 
00617         s32 t;
00618 
00619 #if defined(_MSC_VER)
00620         __asm
00621         {
00622             fld x
00623             fsub    h
00624             fistp   t
00625         }
00626 #elif defined(__GNUC__)
00627         __asm__ __volatile__ (
00628             "fsub %2 \n\t"
00629             "fistpl %0"
00630             : "=m" (t)
00631             : "t" (x), "f" (h)
00632             : "st"
00633             );
00634 #else
00635 #  warn IRRLICHT_FAST_MATH not supported.
00636         return (s32) floorf ( x );
00637 #endif
00638         return t;
00639 #else // no fast math
00640         return (s32) floorf ( x );
00641 #endif
00642     }
00643 
00644 
00645     REALINLINE s32 ceil32 ( f32 x )
00646     {
00647 #ifdef IRRLICHT_FAST_MATH
00648         const f32 h = 0.5f;
00649 
00650         s32 t;
00651 
00652 #if defined(_MSC_VER)
00653         __asm
00654         {
00655             fld x
00656             fadd    h
00657             fistp   t
00658         }
00659 #elif defined(__GNUC__)
00660         __asm__ __volatile__ (
00661             "fadd %2 \n\t"
00662             "fistpl %0 \n\t"
00663             : "=m"(t)
00664             : "t"(x), "f"(h)
00665             : "st"
00666             );
00667 #else
00668 #  warn IRRLICHT_FAST_MATH not supported.
00669         return (s32) ceilf ( x );
00670 #endif
00671         return t;
00672 #else // not fast math
00673         return (s32) ceilf ( x );
00674 #endif
00675     }
00676 
00677 
00678 
00679     REALINLINE s32 round32(f32 x)
00680     {
00681 #if defined(IRRLICHT_FAST_MATH)
00682         s32 t;
00683 
00684 #if defined(_MSC_VER)
00685         __asm
00686         {
00687             fld   x
00688             fistp t
00689         }
00690 #elif defined(__GNUC__)
00691         __asm__ __volatile__ (
00692             "fistpl %0 \n\t"
00693             : "=m"(t)
00694             : "t"(x)
00695             : "st"
00696             );
00697 #else
00698 #  warn IRRLICHT_FAST_MATH not supported.
00699         return (s32) round_(x);
00700 #endif
00701         return t;
00702 #else // no fast math
00703         return (s32) round_(x);
00704 #endif
00705     }
00706 
00707     inline f32 f32_max3(const f32 a, const f32 b, const f32 c)
00708     {
00709         return a > b ? (a > c ? a : c) : (b > c ? b : c);
00710     }
00711 
00712     inline f32 f32_min3(const f32 a, const f32 b, const f32 c)
00713     {
00714         return a < b ? (a < c ? a : c) : (b < c ? b : c);
00715     }
00716 
00717     inline f32 fract ( f32 x )
00718     {
00719         return x - floorf ( x );
00720     }
00721 
00722 } // end namespace core
00723 } // end namespace irr
00724 
00725 #ifndef IRRLICHT_FAST_MATH
00726     using irr::core::IR;
00727     using irr::core::FR;
00728 #endif
00729 
00730 #endif
00731