00001
00002
00003 #ifndef _included_GridHierarchy_h
00004 #define _included_GridHierarchy_h
00005
00011 #include "DAGHParams.h"
00012 #include "DAGHDefaults.h"
00013
00014 #include "DAGHDistribution.h"
00015
00016 #include "GridUnitList.h"
00017 #include "GridBoxList.h"
00018 #include "BBoxList.h"
00019
00020 #include "DCoords.h"
00021
00022 #include "CommServer.h"
00023 #include "CommIOServer.h"
00024
00025 #include "GridFunctionVoid.h"
00026 #include "AllocError.h"
00027
00028 #include <iosfwd>
00029
00030
00031 #ifdef DAGH_IOV
00032 #include <RawTCP.H>
00033 #endif
00034
00035
00036
00037
00038 struct dimspec {
00039 double low;
00040 double high;
00041 double h;
00042 double hfine;
00043 unsigned n;
00044 unsigned nfine;
00045 inline dimspec():low(0),high(0),h(0),hfine(0),n(0),nfine(0) {}
00046 };
00047
00048
00049
00050
00051
00052 struct MergedGridUnit {
00053 BBox bb;
00054 GridUnitList gul;
00055 };
00056
00057
00058
00059
00060
00061 inline double toWorld(struct dimspec const &ds, const int lc)
00062 { return (ds.low + (lc*ds.hfine)); }
00063 inline double toWorldLower(struct dimspec const &ds, const int lc)
00064 { return (ds.high*((1.0*lc)/(1.0*ds.nfine)) +
00065 ds.low*(1.0 - (1.0*lc)/(1.0*ds.nfine))); }
00066 inline double toWorldUpper(struct dimspec const &ds, const int lc,
00067 const int step, const int olap)
00068 { return (ds.high*((1.0*(lc+(1-olap)*step))/(1.0*ds.nfine)) +
00069 ds.low*(1.0 - (1.0*(lc+(1-olap)*step))/(1.0*ds.nfine))); }
00070 inline int toLocal(struct dimspec const &ds, double const wc)
00071 { return ((int) ((wc - ds.low)/ds.hfine)); }
00072
00073
00074
00075
00076
00077
00078 void DAGHIO_EndIOPingFunction(GridHierarchy &GH);
00079
00080
00081 #ifdef DAGH_IOV
00082
00083
00084
00085 void WriteStream(GridHierarchy& GH, RawTCPport* Server, const GridBoxList& cgbl);
00086
00087 #endif
00088
00089 #ifndef DAGHDefaultDistribution
00090 #define DAGHDefaultDistribution (DAGHCompositeDistribution)
00091 #endif
00092
00093 #ifndef DAGHDefaultPartMinGUWidth
00094 #define DAGHDefaultPartMinGUWidth (1)
00095 #endif
00096 #ifndef DAGHDefaultRefineMinGUWidth
00097 #define DAGHDefaultRefineMinGUWidth (1)
00098 #endif
00099
00100 #ifndef DAGHDefaultBoundary
00101 #define DAGHDefaultBoundary (DAGHBoundaryRegular)
00102 #endif
00103
00104 #ifndef DAGHDefaultAdaptBoundary
00105 #define DAGHDefaultAdaptBoundary (DAGHAdaptBoundaryInterp)
00106 #endif
00107
00108 #ifndef DAGHDefaultRefineFactor
00109 #define DAGHDefaultRefineFactor (2)
00110 #endif
00111
00112 #ifndef DAGHDefaultRefineLevel
00113 #define DAGHDefaultRefineLevel (1)
00114 #endif
00115
00116 #ifndef DAGHMaxIOTypes
00117 #define DAGHMaxIOTypes (5)
00118 #endif
00119
00120 #ifndef DAGHChkPtTagNameSize
00121 #define DAGHChkPtTagNameSize (256)
00122 #endif
00123
00143 class GridHierarchy
00144 {
00145 friend std::ostream& operator<<(std::ostream& os, const GridHierarchy& );
00146 friend std::ofstream& operator<<(std::ofstream& ofs, const GridHierarchy&);
00147 friend std::ifstream& operator>>(std::ifstream& ifs, GridHierarchy&);
00148 friend std::stringstream& operator<<(std::stringstream& ofs,
00149 const GridHierarchy&);
00150 friend std::stringstream& operator>>(std::stringstream& ifs,
00151 GridHierarchy&);
00152
00153 public:
00155 short const rank;
00156
00157 private:
00159 struct dimspec dspecs[DAGHMaxRank];
00161 struct dimspec tspecs;
00163 BBox basebbox;
00164
00166 short daghtype;
00168 short overlap[DAGHMaxRank];
00169
00171 short bndrytype;
00173 short adaptbndrytype;
00175 short bndrywidth;
00177 short bndryperiodic[DAGHMaxRank];
00178 short periodicidx[DAGHMaxRank];
00179
00181 short extghostwidth;
00182
00183
00185 short levels;
00187 short maxlev;
00189 short crslev;
00191 short *refby;
00192
00194 short finelev;
00195
00197 BBox intbbox;
00199 BBoxList* intbaselist;
00201 double* bndrycoords;
00202 int nbndrycoords;
00203
00204
00205 BBox* cutbbox;
00206 int ncutbbox;
00207
00209 int *curtime;
00210
00212 int updatedstep;
00213
00214
00215 int nestbuffer;
00216
00217
00218 int cfactor;
00219 int guwidth;
00220
00221
00222 int maxgridsize;
00223
00224 public:
00226 short distribution_type;
00227 DAGHDistribution* distribution;
00228
00229 public:
00230
00232 GridUnitList *complist;
00234 GridBoxList **globalgbl, **oldglobalgbl;
00235
00236
00238 GridUnitList *locallist;
00240 GridBoxList **localgbl, **oldlocalgbl;
00242 BBoxList** localbboxarray;
00243
00244 public:
00246 short gfnum;
00247 GridFunctionVoid **gflist;
00248
00250 short io_type;
00251 DAGHIOServerRcv *io_write_server;
00252 DAGHIOServerSnd *io_read_server;
00253 DAGHIOServerPing *io_flush_server;
00254 DAGHIOServerPing *io_close_server;
00255 DAGHIOServerPing *io_end_server;
00256
00257 private:
00259 short chkptflag;
00260 int chkptpnum;
00261 char chkptname[DAGHChkPtTagNameSize];
00262
00263 #ifdef DAGH_IOV
00264 private:
00265 RawTCPserver *ServerPort;
00266 RawTCPport *Server;
00267 #endif
00268
00269 private:
00270
00271
00272
00273 GridHierarchy(GridHierarchy const &other);
00274 GridHierarchy const &operator= (GridHierarchy const &other);
00275
00276 public:
00277
00278
00279
00280 GridHierarchy(const int ndim, const int type, const int max_level);
00281
00282
00283
00284
00285 ~GridHierarchy(void);
00286
00287
00288
00289
00290 public:
00291 void DAGH_SetBaseGrid(double const *bbox, const int *shape,
00292 const int& ncuts, const int *cuts);
00293 void DAGH_SetTimeSpecs(double const starttime,
00294 double const stoptime,
00295 const int ntsteps);
00296 inline void DAGH_GetTimeSpecs(double& starttime, double& stoptime) {
00297 starttime = tspecs.low;
00298 stoptime = tspecs.high;
00299 }
00300
00301 inline void DAGH_SetBoundaryType(const int type)
00302 { bndrytype = type; }
00303 inline void DAGH_SetAdaptBoundaryType(const int type)
00304 { adaptbndrytype = type; }
00305 inline void DAGH_SetBoundaryWidth(const int width)
00306 { bndrywidth = width; }
00307 inline void DAGH_SetPeriodicBoundaries(const int dir, const int value)
00308 { if (dir>=0 && dir<rank) bndryperiodic[dir] = value; }
00309
00310 inline void DAGH_SetExternalGhostWidth(const int width)
00311 { extghostwidth = width; }
00312
00313 void DAGH_SetRefineFactor(const int rfactor);
00314 void DAGH_SetRefineFactor(const int lev, const int rfactor);
00315
00316 inline void DAGH_SetDistributionType(const int type)
00317 { distribution_type = type; if (distribution) distribution->set_dist(type); }
00318 inline void DAGH_SetDistributionType(const int type, BBoxList& bbl)
00319 { distribution_type = type; if (distribution) distribution->set_dist(type,bbl); }
00320
00321 inline void DAGH_SetWorkFunction(void *wf)
00322 { if (distribution) distribution->set_workfunc(wf); }
00323
00324 inline void DAGH_SetNestingBuffer(const int& nb)
00325 { nestbuffer = nb; }
00326
00327
00328
00329
00330 private:
00331 void DAGH_PeriodicAppendTogbl(GridBoxList *gbl, GridBox* gb, const BBox& bbox,
00332 const BBox& bbtest, const int dir, const int base,
00333 const int maxlength);
00334 public:
00335 void DAGH_PeriodicBoundaries(GridBoxList *gbl, GridBoxList *agbl);
00336
00337
00338
00339
00340 private:
00341 BBox shrinktoroot(BBox& bb);
00342 BBox growtoroot(BBox& bb);
00343 BBoxList* shrinktoroot(BBoxList& bbl);
00344 BBoxList* growtoroot(BBoxList& bbl);
00345
00346 int countcells(BBox& bb, BBoxList** bbla, int count);
00347 public:
00348 void DAGH_CreateGridUnitList(GridUnitList*& gul, BBoxList** bbla, int count=DAGHFalse);
00349 void DAGH_CreateGridUnitList(GridUnitList*& gul, const BBox& _bb);
00350 void DAGH_CreateBBoxList(BBoxList*& bbl, GridUnitList& gul);
00351
00352 public:
00354 void DAGH_ComposeHierarchy(void);
00355
00356
00357
00358
00359 public:
00360 int DAGH_AddGridFunction(GridFunctionVoid *gfv);
00361 inline void DAGH_DelGridFunction(const int gfid)
00362 {
00363 gflist[gfid] = (GridFunctionVoid *) 0;
00364 if (comm_service::dce()) comm_service::delete_comm(gfid);
00365 gfnum--;
00366 }
00367
00368 inline void DAGH_ComposeGridFunctions(void)
00369 {
00370 for (register int i=0; i<gfnum; i++)
00371 { if (gflist[i]) gflist[i]->GF_Compose(); }
00372 if (chkpt_restart()) {
00373 for (register int i=0; i<gfnum; i++)
00374 { if (gflist[i] && gflist[i]->checkpoint())
00375 gflist[i]->GF_CheckpointRestart(); }
00376 }
00377 }
00378
00379 inline int DAGH_GridFunctionTemplate(GridFunctionVoid& gfv)
00380 { return (DAGHTemplateComm + gfv.gfid); }
00381
00382
00383
00384
00385 public:
00397 void DAGH_RecomposeHierarchy(int Partition);
00398 inline void DAGH_RecomposeHierarchy() { DAGH_RecomposeHierarchy(DAGHTrue); }
00399
00400 public:
00402 void DAGH_Refine(const int lev);
00407 void DAGH_Refine(BBoxList &bblist, const int lev);
00408
00409
00410
00411
00412 public:
00413 inline void DAGH_RedistributeHierarchy(const int type)
00414 { if (distribution) distribution->set_dist(type); DAGH_RecomposeHierarchy(); }
00415 inline void DAGH_RedistributeHierarchy(const int type, BBoxList& bbl)
00416 { if (distribution) distribution->set_dist(type,bbl); DAGH_RecomposeHierarchy(); }
00417
00418
00419
00420
00421 public:
00423 void DAGH_Checkpoint(const char* name);
00425 void DAGH_Checkpoint(std::stringstream& ofs);
00426 int DAGH_Checkpoint_StrStream_Memory();
00427
00428 void DAGH_ComposeHierarchy(const char* name);
00430 bool DAGH_RecomposeHierarchy(const char* name);
00432 void DAGH_RecomposeHierarchy(std::stringstream& ifs);
00433
00434 void DAGH_InitChkpt(const char* name) {
00435 chkptflag = DAGHTrue;
00436 std::strncpy(chkptname,name,DAGHChkPtTagNameSize);
00437 chkptname[DAGHChkPtTagNameSize-1] = '\0';
00438 }
00439 void DAGH_InitChkpt()
00440 { chkptflag = DAGHTrue; }
00441 void DAGH_StopChkpt()
00442 { chkptflag = DAGHFalse; chkptname[0] = '\0'; chkptpnum = 0; }
00443
00444 int DAGH_OpenChkptIStream(const int p,
00445 std::ifstream& ifs);
00446 void DAGH_CloseChkptIStream(std::ifstream& ifs);
00447
00448 int DAGH_OpenChkptIStream(const int p,
00449 std::streampos*& chkptdir,
00450 char*& chkptnamedir,
00451 std::ifstream& ifs);
00452 int DAGH_OpenChkptStrStream(std::streampos*& chkptdir,
00453 char*& chkptnamedir,
00454 std::stringstream& ifs);
00455
00456 void DAGH_CloseChkptIStream(std::streampos*& chkptdir,
00457 char*& chkptnamedir,
00458 std::ifstream& ifs);
00459
00460 void DAGH_CloseChkptStrStream(std::streampos*& chkptdir,
00461 char*& chkptnamedir,
00462 std::stringstream& ifs)
00463 { if (chkptdir) delete [] chkptdir;
00464 if (chkptnamedir) delete [] chkptnamedir; }
00465
00466 int DAGH_GetGFChkptIStream(const int p, const char* gfname,
00467 const int gfid,
00468 std::ifstream& ifs);
00469 int DAGH_GetGFChkptStrStream(const char* gfname,
00470 const int gfid,
00471 std::stringstream& ifs);
00472
00473 int DAGH_OpenChkptOStream(const int p,
00474 std::ofstream& ofs);
00475 void DAGH_CloseChkptOStream(std::ofstream& ofs);
00476
00477
00478
00479
00480 private:
00481 void DAGH_GlbConcat(void *snddata, int sndsize, void *&rcvdata,
00482 int &rcvsize, MPI_Comm Cin=0);
00483
00484
00485
00486 void DAGH_PingIONode(const int tag, int flg);
00487 public:
00488 int DAGH_CommInit(MPI_Comm c = 0);
00489 void DAGH_CommKill(void);
00490
00491
00492
00493
00494 public:
00495 static void DAGH_InitOverlaps(const int Type, short* olap)
00496 { if (Type == DAGHCellCentered)
00497 {olap[0] = 0; olap[1] = 0; olap[2] = 0; }
00498 else if (Type == DAGHVertexCentered)
00499 {olap[0] = 1; olap[1] = 1; olap[2] = 1; }
00500 else if (Type == DAGHFaceCentered_X)
00501 {olap[0] = 1; olap[1] = 0; olap[2] = 0; }
00502 else if (Type == DAGHFaceCentered_Y)
00503 {olap[0] = 0; olap[1] = 1; olap[2] = 0; }
00504 else if (Type == DAGHFaceCentered_Z)
00505 {olap[0] = 0; olap[1] = 0; olap[2] = 1; } }
00506
00507
00508
00509
00510
00511 private:
00512 inline int daghoverlapCC_or_NCC() const
00513 { return ((daghtype==DAGHCellCentered) ? 0 : 1); }
00514
00515 inline const short* daghoverlap() const
00516 { return (overlap); }
00517
00518 inline int daghoverlap(const int dir) const
00519 { return (overlap[dir]); }
00520
00521
00522
00523 public:
00524 static int daghoverlapCC_or_NCC(const int type)
00525 { return((type==DAGHCellCentered) ? 0 : 1); }
00526
00527
00528
00529
00530 public:
00531 inline int default_alignment(const int r) const
00532 { return ((r == rank) ? DAGH_All :
00533 (r == 3) ? DAGH_All :
00534 (r == 2) ? DAGH_XY : DAGH_X); }
00535
00536
00537
00538 public:
00539 inline int dagh_type(void) const { return daghtype; }
00540
00541 inline int boundarywidth(void) const { return bndrywidth; }
00542 inline int boundarytype(void) const { return bndrytype; }
00543 inline int adaptboundarytype(void) const { return adaptbndrytype; }
00544 inline int periodicboundary(const int d) const { return bndryperiodic[d]; }
00545 inline int periodicindex(const int d) const { return periodicidx[d]; }
00546
00547 inline int externalghostwidth(void) const { return extghostwidth; }
00548
00549 inline int totallevels(void) const { return levels; }
00550
00551 inline int coarselevel(void) const { return crslev; }
00552 inline int maxlevel(void) const { return maxlev; }
00553 inline int finelevel(void) const { return finelev; }
00554
00557 inline int stepsize(const int lev) const {
00558 int ret=1; for (register int l=Max(0,lev); l<levels-1; l++) ret *= refby[l]; return (ret);
00559 }
00560 inline int timestep(const int lev) const {
00561 int ret=1; for (register int l=Max(0,lev); l<levels-1; l++) ret *= refby[l]; return (ret);
00562 }
00564 inline int stepstaken(const int t, const int l) const
00565 { return (t/timestep(l)); }
00567 inline double delta_t(const int l) const
00568 { return (tspecs.h/refinedby(l)); }
00570 inline double delta_x(const int dim, const int l) const
00571 { return (dspecs[dim].h/refinedby(l)); }
00572
00574 inline int refinefactor(const int lev) const
00575 { return (lev>=0 && lev<levels ? refby[lev] : 1); }
00577 inline int refinedby(const int lev) const {
00578 int ret=1; for (register int l=0; l<Min(lev,levels); l++) ret *= refby[l]; return (ret);
00579 }
00581 int refinedby(const int l1, const int l2) const {
00582 int ret=1;
00583 if (l1>=0 && l2>=0 && l1<levels && l2<levels)
00584 if (l2<l1)
00585 for (register int l=l2; l<l1; l++)
00586 ret *= refby[l];
00587 else if (l2>l1) {
00588 ret = -1;
00589 for (register int l=l1; l<l2; l++)
00590 ret *= refby[l];
00591 }
00592 return (ret);
00593 }
00594
00596 inline int buffer() const { return nestbuffer; }
00597
00605 inline const int& gucfactor() const { return cfactor; }
00607 inline const int& guminwidth() const { return guwidth; }
00609 inline void setgucfactor(const int& cfac) {
00610 cfactor = cfac;
00611 guwidth = (int)(std::log((double)cfactor)/std::log((double)2.0));
00612 if (guwidth<std::log((double)cfactor)/std::log((double)2.0)) guwidth++;
00613 }
00614
00616 inline const int& maxGridBoxSize() const { return maxgridsize; }
00617 inline void setmaxGridBoxSize(const int& mgsize) { maxgridsize = mgsize; }
00618
00619
00620
00621
00622 inline int updatedvaluestep() const { return updatedstep; }
00623 inline void setupdatedvaluestep(const int ustep) { updatedstep = ustep; }
00624
00625
00626
00627
00628 void chkpt_filename(std::ostringstream& obuf,
00629 const char* name, const int p);
00630 inline int chkpt_restart(void) const
00631 { return (chkptflag == DAGHTrue); }
00632 inline const char* chkpt_name(void) const
00633 { return chkptname; }
00634 inline int chkpt_pnum(void) const
00635 { return chkptpnum; }
00636 inline void chkpt_reset(void)
00637 { chkptflag = DAGHFalse; chkptname[0] = '\0'; chkptpnum = 0; }
00638
00639
00640
00641
00642 public:
00647 inline int getCurrentTime(const int lev) const
00648 { return curtime[lev] ; }
00649 inline void setCurrentTime(const int ctime, const int lev)
00650 { curtime[lev] = ctime; }
00652 inline void incrCurrentTime(const int lev)
00653 { curtime[lev] += timestep(lev); }
00654
00656 inline int getPreviousTime(const int lev) const
00657 { return curtime[lev] - timestep(lev); }
00659 inline int getNextTime(const int lev) const
00660 { return curtime[lev] + timestep(lev); }
00661
00662
00663
00664
00665 public:
00666 inline int GlobalLength(int lev) { return (globalgbl[lev] ? globalgbl[lev]->number() : 0); }
00667 inline int GlobalMaxIndex(int lev) { return (globalgbl[lev] ? globalgbl[lev]->maxindex() : 0); }
00668 inline int LocalLength(int lev) { return (localgbl[lev] ? localgbl[lev]->number() : 0); }
00669
00670 inline GridUnitList* clist() { return (complist); }
00671 inline GridUnitList* llist() { return (locallist); }
00672
00674 inline GridBoxList** lgbl() { return (localgbl); }
00676 inline GridBoxList** ggbl() { return (globalgbl); }
00677 inline GridBoxList** oldlgbl() { return (oldlocalgbl); }
00678 inline GridBoxList** oldggbl() { return (oldglobalgbl); }
00679
00681 inline GridBoxList* lgbl(int lev) { return (localgbl[lev]); }
00683 inline GridBoxList* ggbl(int lev) { return (globalgbl[lev]); }
00684 inline GridBoxList* oldlgbl(int lev) { return (oldlocalgbl[lev]); }
00685 inline GridBoxList* oldggbl(int lev) { return (oldglobalgbl[lev]); }
00686
00687
00688 void llb(int *lc) const;
00689 void lub(int *lc) const;
00690
00692 inline Coords llb(void) const { return (basebbox.lower()); }
00693 inline Coords lub(void) const { return (basebbox.upper()); }
00694
00695
00696 void wlb(double *wc) const;
00697 void wub(double *wc) const;
00698
00700 DCoords worldCoords(const int *lc, const int *ls) const;
00701 DCoords worldCoordsUpper(const int *lc, const int *ls) const;
00702 DCoords worldCoordsLower(const int *lc, const int *ls) const;
00703 DCoords worldStep(const int *ls) const;
00704
00705 void worldCoords(const int *lc, const int *ls, double *wc) const;
00706 void worldStep(const int *ls, double *ws) const;
00707
00708
00709 Coords localCoords(double const *wc) const;
00710 Coords localStep(double const *ws) const;
00711
00712 void localCoords(double const *wc, int *lc) const;
00713 void localStep(double const *ws, int *ls) const;
00714
00715
00716
00717
00718 public:
00720 inline BBox bbbox(void) const { return basebbox; }
00721 inline BBox glbbbox(void) const { return intbbox; }
00723 inline BBox wholebbox(void) const
00724 { return shrinkupperbydim(intbbox,daghoverlap()); }
00726 inline BBoxList* wholebaselist(void) const { return intbaselist; };
00728 inline const double* wholebndry(void) const { return bndrycoords; }
00729 inline const int& nbndry(void) const { return nbndrycoords; }
00730
00731
00732 void glb_bboxlist(BBoxList& bbl,
00733 const int l,
00734 const int type=DAGHCellCentered) const;
00735
00736
00737
00738 public:
00739 void DAGH_IOServe(void);
00740 inline void DAGH_SetIOType(const short type) { io_type = type; }
00741 inline const short& DAGH_IOType(void) { return io_type; }
00742 inline DAGHIO_WriteFunc DAGH_IOWrite(void) { return DAGHIO_Write[io_type]; }
00743 inline DAGHIO_ReadFunc DAGH_IORead(void) { return DAGHIO_Read[io_type]; }
00744 void DAGH_IOFlush(void);
00745 void DAGH_IOClose(void);
00746 void DAGH_IOEnd(void);
00747 friend void DAGHIO_EndIOPingFunction(GridHierarchy &GH);
00748
00749
00750
00751
00752 public:
00753 inline void DAGH_SyncGhostRegions(const int t, const int l)
00754 {
00755 register int i;
00756 for (i=0; i<gfnum; i++)
00757 if (gflist[i] && gflist[i]->comm())
00758 gflist[i]->GF_WriteGhosts(t,l);
00759 for (i=0; i<gfnum; i++)
00760 if (gflist[i] && gflist[i]->comm())
00761 gflist[i]->GF_ReadGhosts(t,l);
00762 }
00763
00764 inline void DAGH_SyncGhostRegions(const int t, const int l,
00765 const int axis, const int dir)
00766 {
00767 register int i;
00768 for (i=0; i<gfnum; i++)
00769 if (gflist[i] && gflist[i]->comm())
00770 gflist[i]->GF_WriteGhosts(t,l,axis,dir);
00771 for (i=0; i<gfnum; i++)
00772 if (gflist[i] && gflist[i]->comm())
00773 gflist[i]->GF_ReadGhosts(t,l,axis,dir);
00774 }
00775 };
00776
00777 std::ostream& operator<<(std::ostream&, const GridHierarchy&);
00778 std::ostream& operator<<(std::ostream&, const struct dimspecs&);
00779 std::ostream& operator<<(std::ostream&, const struct MergedGridUnit&);
00780
00781 std::ofstream& operator<<(std::ofstream&, const GridHierarchy&);
00782 std::ifstream& operator>>(std::ifstream&, GridHierarchy&);
00783 std::stringstream& operator<<(std::stringstream&, const GridHierarchy&);
00784 std::stringstream& operator>>(std::stringstream&, GridHierarchy&);
00785
00786
00787 #endif