00001
00007 #include "PruneAlphaPlane.h"
00008 #include "AlphaPlanePool.h"
00009 #include "GlobalResource.h"
00010 #include "SARSOP.h"
00011 #include <cfloat>
00012
00013
00014 using namespace std;
00015 using namespace momdp;
00016 namespace momdp
00017 {
00018
00019
00020
00021
00022 PruneAlphaPlane::PruneAlphaPlane()
00023 {
00024 lastPruneNumPlanes = 0;
00025 global_delta = 0.1;
00026 pruneTime = 0.0;
00027 numPrune = 0;
00028 }
00029
00030 void PruneAlphaPlane::updateCertsAndUses(int timeStamp){
00031
00032 DEBUG_TRACE(cout << "PruneAlpha::updateCertsAndUses timeStamp " << timeStamp << endl;);
00033
00034 prunePlanes();
00035
00036 updateCertsByDeltaDominance(global_delta, timeStamp);
00037 updateUsesByCorners();
00038 }
00039
00040 void PruneAlphaPlane::setDelta(double newDelta)
00041 {
00042
00043 DEBUG_TRACE(cout << "PruneAlphaPlane::setDelta newDelta " << newDelta << endl;);
00044 global_delta = newDelta;
00045 }
00046
00047 bool PruneAlphaPlane::isMax(SharedPointer<AlphaPlane> alphaPlane)
00048 {
00049 SARSOPAlphaPlaneTuple* dataTuple = (SARSOPAlphaPlaneTuple *) alphaPlane->solverData;
00050 return dataTuple->maxMeta.size() != 0;
00051 }
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062 void PruneAlphaPlane::pruneNotCertedAndNotUsed(void)
00063 {
00064 list<SharedPointer<AlphaPlane> > next_planes;
00065 next_planes.clear();
00066
00067
00068
00069 LISTFOREACH (SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00070 {
00071 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00072
00073 SARSOPAlphaPlaneTuple *tempTuple = (SARSOPAlphaPlaneTuple *)(try_alpha->solverData);
00074
00075
00076
00077 if(tempTuple->certifiedBeliefs.size()>0 || try_alpha->used>0 || isMax(try_alpha) )
00078 {
00079 next_planes.push_back(try_alpha);
00080 }
00081 else
00082 {
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094 if(tempTuple->certed==0)
00095 {
00096 DEBUG_TRACE( cout << "Alpha Pruned:" << endl; );
00097 DEBUG_TRACE( try_alpha->alpha->write(cout) << endl; );
00098
00099
00100
00101 }
00102 else if(tempTuple->certed<0)
00103 {
00104 printf("error: certed less than zero!!!!!!!!!!!\n");
00105 }
00106
00107
00108 }
00109 }
00110 alphaPlanePool->planes = next_planes;
00111 lastPruneNumPlanes = alphaPlanePool->planes.size();
00112 if (lastPruneNumPlanes == 0)
00113 {
00114 cout << "ERROR: ALPHA PLANE POOL IS EMPTY! for sval (not known, edit this code...):" << endl ;
00115 }
00116
00117 }
00118
00119
00120 void PruneAlphaPlane::pruneDynamicDeltaVersion(int timeStamp, int& overPrune, int& underPrune)
00121 {
00122 DEBUG_TRACE(cout << "PruneAlpha::pruneDynamicDeltaVersion global_delta " << global_delta << endl;);
00123 DEBUG_TRACE(cout<< "alphaPlanePool->planes.size() :"<< alphaPlanePool->planes.size() << endl;);
00124
00125 double curTime = GlobalResource::getInstance()->getRunTime();
00126
00127 overPrune = 0;
00128 underPrune = 0;
00129
00130
00131 resetAlphaPlaneStateMachine();
00132
00133 updateCertsAndUses(timeStamp);
00134
00135
00136 computePruneStats(&overPrune, &underPrune);
00137
00138 pruneNotCertedAndNotUsed();
00139
00140 double usedTime = GlobalResource::getInstance()->getRunTime() - curTime;
00141 pruneTime += usedTime;
00142 numPrune ++;
00143
00144
00145 }
00146
00147 void PruneAlphaPlane::Sanity()
00148 {
00149
00150
00151 int nAlpha = alphaPlanePool->planes.size();
00152 int nBelief = alphaPlanePool->beliefCache->size();
00153
00154
00155
00156
00157 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00158 {
00159 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00160 SARSOPAlphaPlaneTuple *tempTuple = (SARSOPAlphaPlaneTuple *)(try_alpha->solverData);
00161 tempTuple->sanityMax = false;
00162 }
00163
00164 for(int i = 0 ; i < alphaPlanePool->beliefCache->currentRowCount ; i ++)
00165 {
00166 double curMax = -DBL_MAX;
00167 SharedPointer<AlphaPlane> maxAlpha = NULL;
00168 const SharedPointer<belief_vector> b = alphaPlanePool->beliefCache ->getRow(i)->BELIEF;
00169
00170 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00171 {
00172 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00173 double dotProductValue = inner_prod( *(try_alpha -> alpha),*b);
00174 if(dotProductValue > curMax )
00175 {
00176 curMax = dotProductValue;
00177 maxAlpha = try_alpha;
00178 }
00179 }
00180
00181 SARSOPAlphaPlaneTuple *tempTuple = (SARSOPAlphaPlaneTuple *)(maxAlpha->solverData);
00182 tempTuple->sanityMax = true;
00183 }
00184
00185 int maxCount = 0;
00186 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00187 {
00188 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00189 SARSOPAlphaPlaneTuple *tempTuple = (SARSOPAlphaPlaneTuple *)(try_alpha->solverData);
00190 if(tempTuple->sanityMax == true)
00191 {
00192 maxCount ++;
00193 }
00194 }
00195
00196 }
00197
00198 void PruneAlphaPlane::updateCertsByDeltaDominance(double delta, int timeStamp)
00199 {
00200 DEBUG_TRACE(cout << "updateCertsByDeltaDominance" << endl;);
00201
00202 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00203 {
00204 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00205 updateCerts(try_alpha, delta, timeStamp);
00206
00207
00208 if(isMax(try_alpha))
00209 {
00210 DEBUG_TRACE( cout << "isMax" << endl );
00211 updateMax(try_alpha, delta, timeStamp);
00212 }
00213 }
00214 }
00215
00216 void PruneAlphaPlane::resetAlphaPlaneStateMachine()
00217 {
00218
00219
00220
00221
00222
00223
00224
00225 }
00226
00227
00228 void PruneAlphaPlane::computePruneStats(int *oP, int *uP)
00229 {
00230
00231 int overPrune = 0;
00232 int underPrune = 0;
00233
00234 int maxCount = 0;
00235
00236 LISTFOREACH (SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00237 {
00238
00239 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00240 SARSOPAlphaPlaneTuple *tempTuple = (SARSOPAlphaPlaneTuple *)(try_alpha->solverData);
00241 int beliefSize = problem->getBeliefSize();
00242
00243 if(isMax(try_alpha))
00244 {
00245 maxCount ++;
00246 }
00247
00248 if( isMax(try_alpha) && tempTuple ->certed == 0)
00249 {
00250 overPrune ++;
00251 }
00252 if( !( isMax(try_alpha)) && tempTuple -> certed > 0)
00253 {
00254 underPrune ++;
00255 }
00256 }
00257
00258
00259
00260 *oP = overPrune;
00261 *uP = underPrune;
00262 }
00263
00264
00265 void PruneAlphaPlane::updateUsesByCorners(){
00266
00267 resetUseds();
00268
00269 #if DEBUGSYL_061008_1
00270 unsigned int planeCount = 0;
00271 #endif
00272
00273 if(alphaPlanePool->planes.size()>0)
00274 {
00275 int beliefSize = problem->getBeliefSize();
00276
00277 std::vector <double> cornerValues;
00278
00279 vector <SharedPointer<AlphaPlane> > cornerPlanes;
00280
00281
00282
00283 for (int i = 0; i<beliefSize; i++)
00284 {
00285 cornerValues.push_back(-99e+20);
00286 cornerPlanes.push_back(NULL);
00287 }
00288
00289 #if DEBUGSYL_061008
00290 for (int i = 0; i<beliefSize; i++)
00291 {
00292 cout << "i : " << i << " : " << cornerValues.at(i);
00293 cout << " : " << cornerPlanes.at(i) << endl;
00294 }
00295 #endif
00296
00297 LISTFOREACH(SharedPointer<AlphaPlane>, plane, alphaPlanePool->planes) {
00298 updateCorners(*plane, &cornerValues, &cornerPlanes);
00299 }
00300
00301 #if DEBUGSYL_061008
00302 for (int i = 0; i<beliefSize; i++)
00303 {
00304 cout << "i : " << i << " : " << cornerValues.at(i);
00305 cout << " : " << cornerPlanes.at(i) << endl;
00306 }
00307 #endif
00308
00309
00310 for (int i = 0; i<(int)cornerPlanes.size(); i++)
00311 {
00312 SharedPointer<AlphaPlane> plane = cornerPlanes.at(i);
00313
00314 if(plane != NULL){
00315 plane->used++;
00316
00317 #if DEBUGSYL_061008_1
00318 planeCount++;
00319 #endif
00320
00321 }
00322 }
00323 }
00324 #if DEBUGSYL_061008_1
00325 else
00326 cout << "alphaPlanePool->planes.size() !> 0 !!!" <<endl;
00327 #endif
00328
00329
00330 #if DEBUGSYL_061008_1
00331 cout << " planeCount : " << planeCount << endl;
00332 #endif
00333
00334 }
00335
00336 void PruneAlphaPlane::updateCorners(SharedPointer<AlphaPlane> plane, std::vector<double, std::allocator<double> >* values, std::vector<SharedPointer<AlphaPlane>, std::allocator<SharedPointer<AlphaPlane> > >* indices)
00337 {
00338 SharedPointer<alpha_vector> alpha = plane->alpha;
00339 for (int i = 0; i<(int)alpha->data.size(); i++)
00340 {
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351 int index = i;
00352 double value = alpha->data[i];
00353 if(value > values->at(index))
00354 {
00355 values->at(index)=value;
00356 indices->at(index)=plane;
00357 }
00358 }
00359 }
00360
00361 void PruneAlphaPlane::updateMax(SharedPointer<AlphaPlane> plane, double delta, int timeStamp)
00362 {
00363 vector<AlphaPlaneMaxMeta*> nextMaxMeta;
00364
00365 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(plane->solverData);
00366
00367 FOREACH(AlphaPlaneMaxMeta* , entry, planeTuple->maxMeta)
00368 {
00369 int i = (*entry)->cacheIndex;
00370 bool removeThis = false;
00371 int maxTimeStamp;
00372
00373 if(alphaPlanePool->beliefCache ->getRow(i)->REACHABLE->isFringe())
00374 {
00375 const SharedPointer<belief_vector> b = alphaPlanePool->beliefCache ->getRow(i)->BELIEF;
00376
00377
00378 int lastTimeStamp = (*entry)->timestamp;
00379 maxTimeStamp = lastTimeStamp;
00380 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00381 {
00382 SharedPointer<AlphaPlane> try_alpha = * try_pair;
00383
00384 if(try_alpha->timeStamp > lastTimeStamp)
00385 {
00386 double dotProductValue = inner_prod( *(try_alpha -> alpha),*b);
00387 if(dotProductValue > (*entry)->lastLB )
00388 {
00389 removeThis = true;
00390 break;
00391 }
00392
00393 if( try_alpha->timeStamp > maxTimeStamp)
00394 {
00395 maxTimeStamp = try_alpha->timeStamp;
00396 }
00397 }
00398 }
00399 }
00400 else
00401 {
00402 removeThis = true;
00403 }
00404
00405 if(!removeThis )
00406 {
00407 (*entry)->timestamp = maxTimeStamp;
00408 }
00409 if(!removeThis)
00410 {
00411 nextMaxMeta.push_back((*entry));
00412 }
00413 else
00414 {
00415 delete (*entry);
00416 }
00417 }
00418
00419 planeTuple->maxMeta = nextMaxMeta;
00420
00421 }
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497 void PruneAlphaPlane::updateCerts(SharedPointer<AlphaPlane> plane, double delta, int timeStamp)
00498 {
00499 DEBUG_TRACE(cout << "updateCerts" << endl;);
00500
00501
00502 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(plane->solverData);
00503
00504 int oldCertState = planeTuple -> certed;
00505
00506 for(int i=0; i<(int)planeTuple->certifiedBeliefs.size(); i++)
00507 {
00508
00509 DEBUG_TRACE(cout << "i " << i << endl;);
00510
00511 BeliefTreeNode* n = planeTuple->certifiedBeliefs.at(i);
00512 SharedPointer<belief_vector> b = n->s->bvec;
00513
00514 int lastTimeStamp, maxTimeStamp;
00515 double value, maxValue;
00516 bool dominated;
00517 if(n->count > 0)
00518 {
00519
00520 DEBUG_TRACE(cout << "n->count " << n->count << endl;);
00521
00522
00523 lastTimeStamp = planeTuple->certifiedBeliefTimeStamps.at(i);
00524 maxTimeStamp = lastTimeStamp;
00525 value = inner_prod( *(plane->alpha), *b);
00526 maxValue = value;
00527 dominated = false;
00528 SharedPointer<AlphaPlane> maxPlane = NULL;
00529 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00530 {
00531 SharedPointer<AlphaPlane> try_alpha = * try_pair;
00532
00533
00534 #if USE_MASKED_ALPHA
00535 if (!mask_subset( *b, try_alpha->mask )) continue;
00536 #endif
00537
00538
00539 DEBUG_TRACE(cout << "try_alpha->timeStamp " << try_alpha->timeStamp << endl;);
00540 DEBUG_TRACE(cout << "lastTimeStamp " << lastTimeStamp << endl;);
00541
00542 if(try_alpha->timeStamp > lastTimeStamp)
00543 {
00544 alpha_vector alpha_distance;
00545
00546 copy(alpha_distance, *(plane->alpha));
00547 double newValue = inner_prod( *(try_alpha->alpha),*b);
00548
00549 DEBUG_TRACE(cout << "maxValue " << maxValue << endl;);
00550 DEBUG_TRACE(cout << "newValue " << newValue << endl;);
00551
00552 if(maxValue < newValue-0.00000000000001)
00553 {
00554 DEBUG_TRACE(cout << "smaller " << endl;);
00555
00556 if (maxPlane != NULL)
00557 {
00558 DEBUG_TRACE(cout << "maxPlane != NULL " << endl;);
00559 copy(alpha_distance, *(maxPlane->alpha));
00560 }
00561
00562 alpha_distance -=( *(try_alpha->alpha));
00563 double squareDelta = (newValue - maxValue)* (newValue-maxValue)/(alpha_distance.norm_2());
00564 double deltaValue = sqrt(squareDelta);
00565
00566 if (deltaValue > delta){
00567 maxPlane = try_alpha;
00568 dominated = true;
00569 maxValue = newValue;
00570 }
00571 }
00572
00573 if (try_alpha->timeStamp > maxTimeStamp)
00574 {
00575 maxTimeStamp = try_alpha->timeStamp;
00576 }
00577 }
00578 }
00579
00580
00581 if(dominated)
00582 {
00583 maxPlane->addDominatedBelief(maxTimeStamp, n);
00584 planeTuple->certifiedBeliefs.at(i)= NULL;
00585 }
00586 }
00587 else
00588 {
00589 planeTuple->certifiedBeliefs.at(i)= NULL;
00590 }
00591 }
00592
00593 int size = planeTuple->certifiedBeliefs.size();
00594 vector<BeliefTreeNode*> remainingBeliefs;
00595 vector<int> remainingTimeStamps;
00596 for(int i = 0; i<size; i++){
00597 BeliefTreeNode* n = planeTuple->certifiedBeliefs.at(i);
00598 int t = planeTuple->certifiedBeliefTimeStamps.at(i);
00599 if(n != NULL){
00600 remainingBeliefs.push_back(n);
00601 remainingTimeStamps.push_back(t);
00602 }
00603 }
00604 if( planeTuple->certifiedBeliefs.size()>0 && remainingBeliefs.size()==0)
00605 {
00606 }
00607 planeTuple->certifiedBeliefs = remainingBeliefs;
00608 planeTuple->certifiedBeliefTimeStamps = remainingTimeStamps;
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632 }
00633
00634
00635 int PruneAlphaPlane::countCerts(void)
00636 {
00637
00638 int total = 0;
00639 LISTFOREACH(SharedPointer<AlphaPlane>, plane, alphaPlanePool->planes)
00640 {
00641 SharedPointer<AlphaPlane> alpha = *plane;
00642 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(alpha->solverData);
00643 total += planeTuple->certifiedBeliefs.size();
00644 }
00645 return total;
00646 }
00647
00648
00649
00650 int PruneAlphaPlane::countCertedPlanes(void){
00651 int total = 0;
00652 LISTFOREACH(SharedPointer<AlphaPlane>, plane, alphaPlanePool->planes) {
00653 SharedPointer<AlphaPlane> alpha = *plane;
00654 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(alpha->solverData);
00655 if (planeTuple->certifiedBeliefs.size()>0)
00656 {
00657 total++;
00658 }
00659 }
00660 return total;
00661 }
00662
00663 int PruneAlphaPlane::countUses(void){
00664 int total = 0;
00665 LISTFOREACH(SharedPointer<AlphaPlane>, plane, alphaPlanePool->planes)
00666 {
00667 SharedPointer<AlphaPlane> alpha = *plane;
00668 total += alpha->used;
00669 }
00670 return total;
00671 }
00672
00673 int PruneAlphaPlane::countUsedPlanes(void){
00674 int total = 0;
00675 LISTFOREACH(SharedPointer<AlphaPlane>, plane, alphaPlanePool->planes)
00676 {
00677 SharedPointer<AlphaPlane> alpha = *plane;
00678 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(alpha->solverData);
00679 if(alpha->used >0 && planeTuple->certifiedBeliefs.size()==0)
00680 {
00681 total++;
00682 }
00683 }
00684 return total;
00685 }
00686
00687 void PruneAlphaPlane::resetUseds(void)
00688 {
00689 LISTFOREACH(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00690 {
00691 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00692 try_alpha->used = 0;
00693 }
00694 }
00695
00696
00697 void PruneAlphaPlane::prunePlanes(void)
00698 {
00699 list<SharedPointer<AlphaPlane> > next_planes;
00700 list<list<SharedPointer<AlphaPlane> >::iterator> erase_ptrs;
00701 bool try_dominates_in, in_dominates_try;
00702
00703 LISTFOREACH_NOCONST(SharedPointer<AlphaPlane>, try_pair, alphaPlanePool->planes)
00704 {
00705 SharedPointer<AlphaPlane> try_alpha = *try_pair;
00706 erase_ptrs.clear();
00707 LISTFOREACH_NOCONST(SharedPointer<AlphaPlane>, in_pair, next_planes)
00708 {
00709 SharedPointer<AlphaPlane> in_alpha = *in_pair;
00710
00711 #if USE_MASKED_ALPHA
00712 try_dominates_in = mask_dominates(try_alpha->alpha, in_alpha->alpha, ZMDP_BOUNDS_PRUNE_EPS,
00713 try_alpha->mask, in_alpha->mask);
00714 #else
00715 try_dominates_in = dominates(*try_alpha->alpha, *in_alpha->alpha, ZMDP_BOUNDS_PRUNE_EPS);
00716 #endif
00717 if (try_dominates_in)
00718 {
00719
00720 erase_ptrs.push_back(in_pair);
00721 }
00722 else
00723 {
00724 #if USE_MASKED_ALPHA
00725 in_dominates_try = mask_dominates(in_alpha->alpha, try_alpha->alpha, ZMDP_BOUNDS_PRUNE_EPS,
00726 in_alpha->mask, try_alpha->mask);
00727 #else
00728 in_dominates_try = dominates(*in_alpha->alpha, *try_alpha->alpha, ZMDP_BOUNDS_PRUNE_EPS);
00729 #endif
00730 if (in_dominates_try) goto next_try_pair;
00731 }
00732 }
00733
00734
00735 LISTFOREACH_NOCONST(list<SharedPointer<AlphaPlane> >::iterator, erase_ptr, erase_ptrs)
00736 {
00737 list<SharedPointer<AlphaPlane> >::iterator x, xp1;
00738 SharedPointer<AlphaPlane> testDeleteAlphaPlane = (**erase_ptr);
00739 SARSOPAlphaPlaneTuple *planeTuple = (SARSOPAlphaPlaneTuple *)(testDeleteAlphaPlane->solverData);
00740 if(planeTuple->certed==0)
00741 {
00742
00743
00744 }
00745 else if(planeTuple->certed<0)
00746 {
00747 printf("error2: certed num less than 0!!!!!!!!!!\n");
00748 }
00749 x = xp1 = (*erase_ptr);
00750 xp1++;
00751 next_planes.erase(x,xp1);
00752 }
00753 next_planes.push_back(try_alpha );
00754 next_try_pair: ;
00755 }
00756 #if USE_DEBUG_PRINT
00757 cout << "... pruned # planes from " << planes.size()
00758 << " down to " << next_planes.size() << endl;
00759 #endif
00760 alphaPlanePool->planes = next_planes;
00761 lastPruneNumPlanes = alphaPlanePool->planes.size();
00762 }
00763
00764
00765
00766 void PruneAlphaPlane::prune(void)
00767 {
00768 unsigned int nextPruneNumPlanes = max(lastPruneNumPlanes + 10,
00769 (int) (lastPruneNumPlanes * 1.1));
00770 if (alphaPlanePool->planes.size() > nextPruneNumPlanes)
00771 {
00772 #if DEBUGSYL_100908
00773 cout <<"Prune() for sval : " << alphaPlanePool->sval << " alphaPlanePool->planes.size() :"<< alphaPlanePool->planes.size() << endl;
00774 #endif
00775 prunePlanes();
00776 #if DEBUGSYL_100908
00777 cout <<"After calling prunePlanes(), alphaPlanePool->planes.size() :"<< alphaPlanePool->planes.size() << endl;
00778 #endif
00779 if (alphaPlanePool->planes.size() == 0)
00780 cout << "ERROR: ALPHA PLANE POOL IS EMPTY! for sval (edit code):" << endl;
00781
00782 }
00783 }
00784 };
00785