00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include "Beobot/TopologicalMap.H"
00041 #include "Raster/Raster.H"
00042 #include "Image/DrawOps.H"
00043 #include "Image/MathOps.H"
00044
00045 #include <cstdio>
00046
00047
00048 TopologicalMap::TopologicalMap()
00049 : Map(),
00050 itsGraph(new Graph())
00051 {
00052 }
00053
00054
00055 TopologicalMap::TopologicalMap(std::string fileName)
00056 : Map(),
00057 itsGraph(new Graph())
00058 {
00059 read(fileName);
00060 }
00061
00062
00063 TopologicalMap::~TopologicalMap()
00064 {
00065 }
00066
00067
00068 bool TopologicalMap::read(std::string fileName)
00069 {
00070 FILE *fp; char inLine[200]; char comment[200];
00071
00072 LINFO("file name: %s", fileName.c_str());
00073 if((fp = fopen(fileName.c_str(),"rb")) == NULL)
00074 {
00075 LINFO("not found");
00076 itsMapWidth = 0; itsMapHeight = 0;
00077 LINFO("map size: 0x0");
00078
00079 itsMapScale = 1.0;
00080 LINFO("map scale: 1.0");
00081
00082 itsSegmentList.resize(0);
00083 itsSegmentEdgeIndexList.resize(0);
00084 itsSegmentLength.resize(0);
00085 LINFO("segment size: %d", 0);
00086
00087 return false;
00088 }
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 if (fgets(inLine, 200, fp) == NULL) LFATAL("fgets failed");
00104 int w, h; sscanf(inLine, "%s %d %d", comment, &w, &h);
00105 itsMapWidth = w; itsMapHeight = h;
00106 LINFO("map size: %d %d", itsMapWidth, itsMapHeight);
00107
00108
00109 if (fgets(inLine, 200, fp) == NULL) LFATAL("fgets failed");
00110 float scale; sscanf(inLine, "%s %f", comment, &scale);
00111 itsMapScale = scale;
00112 LINFO("map scale: %f", itsMapScale);
00113
00114
00115 if (fgets(inLine, 200, fp) == NULL) LFATAL("fgets failed");
00116 int ssize; sscanf(inLine, "%s %d", comment, &ssize);
00117 itsSegmentList.resize(ssize);
00118 itsSegmentEdgeIndexList.resize(ssize);
00119 itsSegmentLength.resize(ssize);
00120 LINFO("segment Num: %d", ssize);
00121
00122
00123 if (fgets(inLine, 200, fp) == NULL) LFATAL("fgets failed");
00124
00125
00126 uint ncount = 0;
00127 while(fgets(inLine, 200, fp) != NULL && inLine[0] != '=')
00128 {
00129 int x, y; sscanf(inLine, "%d %d", &x, &y);
00130 LINFO("node[%d]: (%d,%d)", itsGraph->getNumNode(), x, y);
00131
00132 rutz::shared_ptr<Node> node(new Node(ncount, Point2D<int>(x,y)));
00133 itsGraph->addNode(node);
00134 ncount++;
00135 }
00136
00137
00138 while((fgets(inLine, 200, fp) != NULL) && (inLine[0] != '='))
00139 {
00140 int s, e; sscanf(inLine, "%d %d", &s, &e);
00141 Point2D<int> scoor = itsGraph->getNode(s)->getCoordinate();
00142 Point2D<int> ecoor = itsGraph->getNode(e)->getCoordinate();
00143 float dist = scoor.distance(ecoor);
00144 LINFO("edge(%d,%d): %f", s, e, dist);
00145
00146 rutz::shared_ptr<Edge>
00147 edge(new Edge(itsGraph->getNode(s),
00148 itsGraph->getNode(e), dist));
00149 itsGraph->addEdge(edge);
00150 }
00151
00152
00153 uint scount = 0;
00154 while(fgets(inLine, 200, fp) != NULL)
00155 {
00156 std::string line(inLine);
00157 while(line.length() > 0)
00158 {
00159 uint cedge;
00160 std::string::size_type spos = line.find_first_of(' ');
00161 if(spos != std::string::npos)
00162 {
00163 cedge = uint(atoi(line.substr(0, spos).c_str()));
00164 line = line.substr(spos+1);
00165 }
00166 else
00167 {
00168 cedge = uint(atoi(line.c_str()));
00169 line = std::string("");
00170 }
00171 LINFO("seg[%d][%"ZU"]:%d",
00172 scount, itsSegmentList[scount].size(), cedge);
00173 itsSegmentList[scount].push_back(itsGraph->getEdge(cedge));
00174 itsSegmentEdgeIndexList[scount].push_back(cedge);
00175 }
00176 scount++;
00177 }
00178
00179 fclose(fp);
00180
00181
00182
00183 itsGraph->computeAdjecencyList();
00184 itsGraph->computeDistances();
00185
00186
00187 computeDistances();
00188
00189
00190 setSegmentLength();
00191
00192 return true;
00193 }
00194
00195
00196 void TopologicalMap::computeDistances()
00197 {
00198
00199 uint nnode = itsGraph->getNumNode();
00200 uint nsegment = getSegmentNum();
00201 itsNodeSegmentDistanceMatrix.resize(nnode, nsegment);
00202
00203
00204 for(uint i = 0; i < nnode; i++)
00205 {
00206
00207 for(uint j = 0; j < nsegment; j++)
00208 {
00209
00210 float mindist;
00211 uint nsedge = itsSegmentList[j].size();
00212 if(nsedge > 0)
00213 {
00214 uint snode = itsSegmentList[j][0]->getSourceNode()->getIndex();
00215 mindist = itsGraph->getUndirectedDistance(i,snode);
00216 }
00217 else mindist = -1.0F;
00218
00219 for(uint k = 0; k < nsedge; k++)
00220 {
00221 uint dnode = itsSegmentList[j][k]->getDestNode()->getIndex();
00222 float cdist = itsGraph->getUndirectedDistance(i, dnode);
00223 if(mindist > cdist) mindist = cdist;
00224 }
00225
00226 LDEBUG("itsSegmentList[%d][%d]: %f",i,j, mindist);
00227 itsNodeSegmentDistanceMatrix.setVal(i, j, mindist);
00228 }
00229 }
00230 }
00231
00232
00233 bool TopologicalMap::write(std::string fileName)
00234 {
00235 LFATAL("NOT YET implemented");
00236 return true;
00237 }
00238
00239
00240 Image<PixRGB<byte> > TopologicalMap::getMapImage(uint w, uint h)
00241 {
00242 float scale = std::min(w/itsMapWidth, h/itsMapHeight);
00243
00244 Image<PixRGB<byte> >
00245 res(int(itsMapWidth * scale), int(itsMapHeight * scale), ZEROS);
00246
00247
00248 for(uint i = 0; i < itsGraph->getNumEdge(); i++)
00249 {
00250 Point2D<int> sp = itsGraph->getEdge(i)->getSourceNode()->getCoordinate();
00251 Point2D<int> ep = itsGraph->getEdge(i)->getDestNode()->getCoordinate();
00252 drawArrow(res,
00253 Point2D<int>(int(sp.i * scale), int(sp.j * scale)),
00254 Point2D<int>(int(ep.i * scale), int(ep.j * scale)),
00255 PixRGB<byte>(127,127,127));
00256 }
00257
00258
00259 for(uint i = 0; i < itsGraph->getNumNode(); i++)
00260 {
00261 Point2D<int> p = itsGraph->getNode(i)->getCoordinate();
00262 Point2D<int> pd(int(p.i * scale), int(p.j * scale));
00263 drawDisk(res, pd, 2, PixRGB<byte>(255,255,255));
00264 }
00265
00266 drawRect(res,res.getBounds(),PixRGB<byte>(255,255,255));
00267
00268 return res;
00269 }
00270
00271
00272 float TopologicalMap::getDistance
00273 (uint asnum, float altrav, uint bsnum, float bltrav)
00274 {
00275 ASSERT(asnum < itsSegmentList.size() && altrav <= 1.0F &&
00276 bsnum < itsSegmentList.size() && bltrav <= 1.0F );
00277
00278
00279
00280 float dsa, dea, dsb, deb;
00281 rutz::shared_ptr<Edge> a = getEdge(asnum, altrav, dsa, dea);
00282 rutz::shared_ptr<Edge> b = getEdge(bsnum, bltrav, dsb, deb);
00283
00284
00285 uint sa = a->getSourceNode()->getIndex();
00286 uint ea = a->getDestNode()->getIndex();
00287 uint sb = b->getSourceNode()->getIndex();
00288 uint eb = b->getDestNode()->getIndex();
00289 LDEBUG("%d %d -- %d %d", sa, ea, sb, eb);
00290
00291
00292
00293 float dist = 0.0F;
00294 if(sa == sb && ea == eb) dist = fabs(dsa - dsb);
00295 else if(sa == eb && sb == ea) dist = fabs(dsa + dsb - a->getCost());
00296 else
00297 {
00298
00299 float dist1 = dsa + itsGraph->getUndirectedDistance(sa, sb) + dsb;
00300 float dist2 = dsa + itsGraph->getUndirectedDistance(sa, eb) + deb;
00301 float dist3 = dea + itsGraph->getUndirectedDistance(ea, sb) + dsb;
00302 float dist4 = dea + itsGraph->getUndirectedDistance(ea, eb) + deb;
00303 LDEBUG("%f %f %f %f", dist1,dist2,dist3,dist4);
00304 dist = std::min(std::min(dist1, dist2), std::min(dist3, dist4));
00305 }
00306 LDEBUG("-> %f",dist);
00307 return dist;
00308 }
00309
00310
00311 float TopologicalMap::getPath
00312 (uint asnum, float altrav, uint bsnum, float bltrav,
00313 std::vector<int> &moves)
00314 {
00315
00316
00317
00318
00319
00320 ASSERT(asnum < itsSegmentList.size() && altrav <= 1.0F &&
00321 bsnum < itsSegmentList.size() && bltrav <= 1.0F );
00322 moves.clear();
00323
00324
00325
00326 float dsa, dea, dsb, deb; uint eindexa, eindexb;
00327 rutz::shared_ptr<Edge> a = getEdge(asnum, altrav, dsa, dea, eindexa);
00328 rutz::shared_ptr<Edge> b = getEdge(bsnum, bltrav, dsb, deb, eindexb);
00329
00330
00331 uint sa = a->getSourceNode()->getIndex();
00332 uint ea = a->getDestNode()->getIndex();
00333 uint sb = b->getSourceNode()->getIndex();
00334 uint eb = b->getDestNode()->getIndex();
00335 LDEBUG("%d %d -- %d %d", sa, ea, sb, eb);
00336
00337
00338 float dist = 0.0F;
00339 if(sa == sb && ea == eb)
00340 {
00341 dist = fabs(dsa - dsb);
00342 if(dsb > dsa) moves.push_back(TOPOMAP_MOVE_FORWARD);
00343 else
00344 {
00345 moves.push_back(TOPOMAP_TURN_AROUND);
00346 moves.push_back(TOPOMAP_MOVE_FORWARD);
00347 }
00348 }
00349
00350 else if(sa == eb && sb == ea)
00351 {
00352 dist = fabs(dsa + dsb - a->getCost());
00353 if(deb < dsa) moves.push_back(TOPOMAP_MOVE_FORWARD);
00354 else
00355 {
00356 moves.push_back(TOPOMAP_TURN_AROUND);
00357 moves.push_back(TOPOMAP_MOVE_FORWARD);
00358 }
00359 }
00360
00361 else
00362 {
00363
00364 std::vector<uint> edges1;
00365 float dist1 = dsa +
00366 itsGraph->getUndirectedDistance(sa, sb, edges1) + dsb;
00367 std::vector<uint> edges2;
00368 float dist2 = dsa +
00369 itsGraph->getUndirectedDistance(sa, eb, edges2) + deb;
00370 std::vector<uint> edges3;
00371 float dist3 = dea +
00372 itsGraph->getUndirectedDistance(ea, sb, edges3) + dsb;
00373 std::vector<uint> edges4;
00374 float dist4 = dea +
00375 itsGraph->getUndirectedDistance(ea, eb, edges4) + deb;
00376 dist = std::min(std::min(dist1, dist2), std::min(dist3, dist4));
00377 LDEBUG("%f %f %f %f: min: %f", dist1, dist2, dist3, dist4, dist);
00378
00379
00380 if(dist1 == dist)
00381 {
00382 LDEBUG("E1: %"ZU, edges1.size());
00383 moves.push_back(TOPOMAP_TURN_AROUND);
00384 moves.push_back(TOPOMAP_MOVE_FORWARD);
00385 for(uint i = 0; i < edges1.size(); i++)
00386 {
00387 moves.push_back(edges1[i]);
00388 LDEBUG("E1: %d", edges1[i]);
00389 }
00390 moves.push_back(eindexb);
00391 }
00392 else if(dist2 == dist)
00393 {
00394 LDEBUG("E2: %"ZU, edges2.size());
00395 moves.push_back(TOPOMAP_TURN_AROUND);
00396 moves.push_back(TOPOMAP_MOVE_FORWARD);
00397 for(uint i = 0; i < edges2.size(); i++)
00398 {
00399 moves.push_back(edges2[i]);
00400 LDEBUG("E2: %d", edges2[i]);
00401 }
00402 moves.push_back(eindexb);
00403
00404
00405 }
00406 else if(dist3 == dist)
00407 {
00408 LDEBUG("E3: %"ZU,edges3.size());
00409 moves.push_back(TOPOMAP_MOVE_FORWARD);
00410 for(uint i = 0; i < edges3.size(); i++)
00411 {
00412 moves.push_back(edges3[i]);
00413 LDEBUG("E3: %d", edges3[i]);
00414 }
00415 moves.push_back(eindexb);
00416 }
00417 else if(dist4 == dist)
00418 {
00419 LDEBUG("E4: %"ZU, edges4.size());
00420 moves.push_back(TOPOMAP_MOVE_FORWARD);
00421 for(uint i = 0; i < edges4.size(); i++)
00422 {
00423 moves.push_back(edges4[i]);
00424 LDEBUG("E4: %d", edges4[i]);
00425 }
00426 moves.push_back(eindexb);
00427
00428
00429 }
00430
00431 for(uint i = 0; i < moves.size(); i++)
00432 LDEBUG("[%d]: %d", i, moves[i]);
00433 }
00434 return dist;
00435 }
00436
00437
00438
00439 rutz::shared_ptr<Edge> TopologicalMap::getEdge
00440 (uint cseg, float ltrav, float &sdist, float &edist)
00441 {
00442 uint i;
00443 return getEdge(cseg, ltrav, sdist, edist, i);
00444 }
00445
00446
00447 rutz::shared_ptr<Edge> TopologicalMap::getEdge
00448 (uint cseg, float ltrav, float &sdist, float &edist, uint &eindex)
00449 {
00450 ASSERT((cseg < itsSegmentList.size()) && (ltrav <= 1.0F));
00451
00452
00453 float slen = getSegmentLength(cseg);
00454 float altrav = ltrav * slen;
00455 LDEBUG("%d: %f * %f = altrav: %f", cseg, ltrav, slen, altrav);
00456
00457
00458 float cltrav = 0.0F;
00459 uint nsedge = itsSegmentList[cseg].size();
00460 if(nsedge == 0) return rutz::shared_ptr<Edge>();
00461
00462 uint j = 0;
00463 while((j < nsedge) && (altrav >= cltrav))
00464 { cltrav += itsSegmentList[cseg][j]->getCost(); j++; }
00465 LDEBUG("nsedge: %d -> j: %d", nsedge, j);
00466
00467
00468 edist = cltrav - altrav;
00469 sdist = itsSegmentList[cseg][j-1]->getCost() - edist;
00470 LDEBUG("sdist: %f, edist: %f", sdist, edist);
00471 eindex = itsSegmentEdgeIndexList[cseg][j-1];
00472 return itsSegmentList[cseg][j-1];
00473 }
00474
00475
00476 rutz::shared_ptr<Edge> TopologicalMap::getEdge
00477 (Point2D<int> loc, float &sdist, float &edist)
00478 {
00479 LFATAL("NOT YET implemented");
00480
00481
00482 return rutz::shared_ptr<Edge>();
00483 }
00484
00485
00486 float TopologicalMap::getSegmentDistance
00487 (uint sseg, float ltrav, uint dseg, uint &intIndex)
00488 {
00489 ASSERT(sseg < itsSegmentList.size() &&
00490 dseg < itsSegmentList.size() && ltrav <= 1.0F);
00491
00492
00493
00494
00495 if(sseg == dseg) { intIndex = itsGraph->getNumNode(); return 0.0F; }
00496
00497
00498 float slen = getSegmentLength(sseg);
00499 float altrav = ltrav * slen;
00500 float clen = altrav; uint i = 0;
00501
00502
00503 while(clen > 0.0F)
00504 {
00505 float len = itsSegmentList[sseg][i]->getCost();
00506 if(len > clen)
00507 {
00508 uint sind = itsSegmentList[sseg][i]->getSourceNode()->getIndex();
00509 uint eind = itsSegmentList[sseg][i]->getDestNode()->getIndex();
00510
00511 float sdist =
00512 itsNodeSegmentDistanceMatrix[Point2D<int>(sind,dseg)] + clen;
00513 float edist =
00514 itsNodeSegmentDistanceMatrix[Point2D<int>(eind,dseg)] + len - clen;
00515
00516
00517
00518 if(sdist > edist)
00519 { intIndex = eind; return edist; }
00520 else
00521 { intIndex = sind; return sdist; }
00522 }
00523 else if(len == clen)
00524 {
00525 uint eind = itsSegmentList[sseg][i]->getDestNode()->getIndex();
00526 float edist = itsNodeSegmentDistanceMatrix[Point2D<int>(eind,dseg)];
00527
00528
00529 intIndex = eind;
00530 return edist;
00531 }
00532 else{ clen -= len; i++; }
00533
00534 }
00535
00536
00537 uint sind = itsSegmentList[sseg][0]->getSourceNode()->getIndex();
00538 float sdist = itsNodeSegmentDistanceMatrix[Point2D<int>(sind,dseg)];
00539 intIndex = sind;
00540
00541
00542 return sdist;
00543 }
00544
00545
00546 void TopologicalMap::setSegmentLength()
00547 {
00548
00549 for(uint i = 0; i < itsSegmentList.size(); i++)
00550 {
00551
00552 itsSegmentLength[i] = 0.0F;
00553 uint nsedge = itsSegmentList[i].size();
00554 for(uint j = 0; j < nsedge; j++)
00555 itsSegmentLength[i] += itsSegmentList[i][j]->getCost();
00556 }
00557 }
00558
00559
00560 float TopologicalMap::getSegmentLength(uint index)
00561 {
00562 ASSERT(index < itsSegmentLength.size());
00563 return itsSegmentLength[index];
00564 }
00565
00566
00567 float TopologicalMap::getNodeSegmentMaxDistance()
00568 {
00569 float min, max;
00570 getMinMax(itsNodeSegmentDistanceMatrix, min, max);
00571 if(min == -1.0F) return -1.0F;
00572 else return max;
00573 }
00574
00575
00576 void TopologicalMap::getLocationFloat(uint cseg, float ltrav,
00577 float &x, float &y)
00578 {
00579 ASSERT((cseg < itsSegmentList.size()) &&
00580 (ltrav <= 1.0F) && (ltrav >= 0.0F));
00581
00582
00583 float slen = getSegmentLength(cseg);
00584 float altrav = ltrav * slen;
00585 LDEBUG("%d: %f * %f = altrav: %f", cseg, ltrav, slen, altrav);
00586
00587
00588 float cltrav = 0.0F;
00589 uint nsedge = itsSegmentList[cseg].size();
00590 if(nsedge == 0) { x = -1.0F; y = -1.0F; return; }
00591 uint j = 0;
00592 while((j < nsedge) && (altrav >= cltrav))
00593 { cltrav += itsSegmentList[cseg][j]->getCost(); j++; }
00594 LDEBUG("nsedge: %d -> j: %d", nsedge, j);
00595
00596
00597 float rdist = cltrav - altrav;
00598 float dist = itsSegmentList[cseg][j-1]->getCost();
00599 Point2D<int> sloc =
00600 itsSegmentList[cseg][j-1]->getSourceNode()->getCoordinate();
00601 Point2D<int> eloc =
00602 itsSegmentList[cseg][j-1]->getDestNode()->getCoordinate();
00603 LDEBUG("%f/%f (%d,%d) -> (%d,%d)",
00604 rdist, dist, sloc.i,sloc.j, eloc.i, eloc.j);
00605
00606 x = eloc.i - rdist/dist*(eloc.i - sloc.i);
00607 y = eloc.j - rdist/dist*(eloc.j - sloc.j);
00608 }
00609
00610
00611 Point2D<int> TopologicalMap::getLocation(uint cseg, float ltrav)
00612 {
00613 float x, y;
00614 getLocationFloat(cseg, ltrav, x, y);
00615 uint iloc = uint(x + 0.5F);
00616 uint jloc = uint(y + 0.5F);
00617 LDEBUG("(%d, %f) -> %d, %d", cseg, ltrav, iloc, jloc);
00618 return Point2D<int>(iloc, jloc);
00619 }
00620
00621
00622 void TopologicalMap::getLocation(Point2D<int>, uint &cseg, float <rav)
00623 {
00624
00625
00626
00627
00628 LFATAL("NOT YET implemented");
00629 }
00630
00631
00632 uint TopologicalMap::getSegmentLocation(Point2D<int> loc)
00633 {
00634
00635
00636
00637 LFATAL("NOT YET implemented");
00638
00639 return 0;
00640 }
00641
00642
00643 float TopologicalMap::getSegmentLengthTraveled(Point2D<int> loc)
00644 {
00645
00646
00647
00648 LFATAL("NOT YET implemented");
00649 return 0.0;
00650 }
00651
00652
00653 std::vector<rutz::shared_ptr<Node> >
00654 TopologicalMap::getNodesInInterval(uint index, float fltrav, float lltrav)
00655 {
00656 ASSERT(index < itsSegmentLength.size());
00657 ASSERT(fltrav >= 0.0 && fltrav <= 1.0);
00658 ASSERT(lltrav >= 0.0 && lltrav <= 1.0);
00659
00660 float slen = getSegmentLength(index);
00661 std::vector<rutz::shared_ptr<Node> > res;
00662 float caltrav = 0.0;
00663
00664
00665 uint ssize = itsSegmentList[index].size();
00666 for(uint i = 0; i < ssize; i++)
00667 {
00668
00669 float ltrav = caltrav/slen;
00670 LDEBUG("%d: %f * %f = altrav: %f", index, ltrav, slen, caltrav);
00671
00672 if(ltrav >= fltrav && ltrav <= lltrav)
00673 res.push_back(itsSegmentList[index][i]->getSourceNode());
00674 else if(ltrav > lltrav) return res;
00675
00676 caltrav += itsSegmentList[index][i]->getCost();
00677 }
00678
00679 if (lltrav == 1.0)
00680 res.push_back(itsSegmentList[index][ssize-1]->getDestNode());
00681
00682 return res;
00683 }
00684
00685
00686 std::vector<std::pair<uint,float> >
00687 TopologicalMap::getNodeLocationsInInterval
00688 (uint index, float fltrav, float lltrav)
00689 {
00690 ASSERT(index < itsSegmentLength.size());
00691 ASSERT(fltrav >= 0.0 && fltrav <= 1.0);
00692 ASSERT(lltrav >= 0.0 && lltrav <= 1.0);
00693
00694 float slen = getSegmentLength(index);
00695 std::vector<std::pair<uint,float> > res;
00696 float caltrav = 0.0;
00697
00698
00699 uint ssize = itsSegmentList[index].size();
00700 for(uint i = 0; i < ssize; i++)
00701 {
00702
00703 float ltrav = caltrav/slen;
00704 LDEBUG("%d: %f * %f = caltrav: %f", index, ltrav, slen, caltrav);
00705
00706 if(ltrav >= fltrav && ltrav <= lltrav)
00707 res.push_back(std::pair<uint,float>(index, ltrav));
00708 else if(ltrav > lltrav) return res;
00709
00710 caltrav += itsSegmentList[index][i]->getCost();
00711 }
00712
00713 if (lltrav == 1.0F)
00714 res.push_back(std::pair<uint,float>(index, 1.0F));
00715
00716 return res;
00717 }
00718
00719
00720 float TopologicalMap::getAngle
00721 (rutz::shared_ptr<Edge> e1, rutz::shared_ptr<Edge> e2)
00722 {
00723 return itsGraph->getAngle(e1, e2);
00724 }
00725
00726
00727
00728
00729
00730