46 //! In a sense, the path can be treated as a graph, for is has \c NodeIt |
46 //! In a sense, the path can be treated as a graph, for is has \c NodeIt |
47 //! and \c EdgeIt with the same usage. These types converts to the \c Node |
47 //! and \c EdgeIt with the same usage. These types converts to the \c Node |
48 //! and \c Edge of the original graph. |
48 //! and \c Edge of the original graph. |
49 //! |
49 //! |
50 //! \todo Thoroughfully check all the range and consistency tests. |
50 //! \todo Thoroughfully check all the range and consistency tests. |
51 template<typename Graph, typename DM = DefaultDebugMode> |
51 template<typename Graph> |
52 class DirPath { |
52 class DirPath { |
53 public: |
53 public: |
54 /// Edge type of the underlying graph. |
54 /// Edge type of the underlying graph. |
55 typedef typename Graph::Edge GraphEdge; |
55 typedef typename Graph::Edge GraphEdge; |
56 /// Node type of the underlying graph. |
56 /// Node type of the underlying graph. |
72 /// \brief Subpath constructor. |
72 /// \brief Subpath constructor. |
73 /// |
73 /// |
74 /// Subpath defined by two nodes. |
74 /// Subpath defined by two nodes. |
75 /// \warning It is an error if the two edges are not in order! |
75 /// \warning It is an error if the two edges are not in order! |
76 DirPath(const DirPath &P, const NodeIt &a, const NodeIt &b) { |
76 DirPath(const DirPath &P, const NodeIt &a, const NodeIt &b) { |
77 if( DM::range_check && (!a.valid() || !b.valid) ) { |
77 if(!a.valid() || !b.valid) { |
78 // FIXME: this check should be more elaborate... |
78 // FIXME: this check should be more elaborate... |
79 fault("DirPath, subpath ctor: invalid bounding nodes"); |
79 fault("DirPath, subpath ctor: invalid bounding nodes"); |
80 } |
80 } |
81 gr = P.gr; |
81 gr = P.gr; |
82 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
82 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
85 /// \brief Subpath constructor. |
85 /// \brief Subpath constructor. |
86 /// |
86 /// |
87 /// Subpath defined by two edges. Contains edges in [a,b) |
87 /// Subpath defined by two edges. Contains edges in [a,b) |
88 /// \warning It is an error if the two edges are not in order! |
88 /// \warning It is an error if the two edges are not in order! |
89 DirPath(const DirPath &P, const EdgeIt &a, const EdgeIt &b) { |
89 DirPath(const DirPath &P, const EdgeIt &a, const EdgeIt &b) { |
90 if( DM::range_check && (!a.valid() || !b.valid) ) { |
90 if (!a.valid() || !b.valid) { |
91 // FIXME: this check should be more elaborate... |
91 // FIXME: this check should be more elaborate... |
92 fault("DirPath, subpath ctor: invalid bounding nodes"); |
92 fault("DirPath, subpath ctor: invalid bounding nodes"); |
93 } |
93 } |
94 gr = P.gr; |
94 gr = P.gr; |
95 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
95 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
105 |
105 |
106 /// \brief Starting point of the path. |
106 /// \brief Starting point of the path. |
107 /// |
107 /// |
108 /// Starting point of the path. |
108 /// Starting point of the path. |
109 /// Returns INVALID if the path is empty. |
109 /// Returns INVALID if the path is empty. |
110 GraphNode from() const { |
110 GraphNode tail() const { |
111 return empty() ? INVALID : gr->tail(edges[0]); |
111 return empty() ? INVALID : gr->tail(edges[0]); |
112 } |
112 } |
113 /// \brief End point of the path. |
113 /// \brief End point of the path. |
114 /// |
114 /// |
115 /// End point of the path. |
115 /// End point of the path. |
116 /// Returns INVALID if the path is empty. |
116 /// Returns INVALID if the path is empty. |
117 GraphNode to() const { |
117 GraphNode head() const { |
118 return empty() ? INVALID : gr->head(edges[length()-1]); |
118 return empty() ? INVALID : gr->head(edges[length()-1]); |
119 } |
119 } |
120 |
120 |
121 /// \brief Initializes node or edge iterator to point to the first |
121 /// \brief Initializes node or edge iterator to point to the first |
122 /// node or edge. |
122 /// node or edge. |
125 template<typename It> |
125 template<typename It> |
126 It& first(It &i) const { return i=It(*this); } |
126 It& first(It &i) const { return i=It(*this); } |
127 |
127 |
128 /// \brief Initializes node iterator to point to the node of a given index. |
128 /// \brief Initializes node iterator to point to the node of a given index. |
129 NodeIt& nth(NodeIt &i, int n) const { |
129 NodeIt& nth(NodeIt &i, int n) const { |
130 if( DM::range_check && (n<0 || n>int(length())) ) |
130 if(n<0 || n>int(length())) |
131 fault("DirPath::nth: index out of range"); |
131 fault("DirPath::nth: index out of range"); |
132 return i=NodeIt(*this, n); |
132 return i=NodeIt(*this, n); |
133 } |
133 } |
134 |
134 |
135 /// \brief Initializes edge iterator to point to the edge of a given index. |
135 /// \brief Initializes edge iterator to point to the edge of a given index. |
136 EdgeIt& nth(EdgeIt &i, int n) const { |
136 EdgeIt& nth(EdgeIt &i, int n) const { |
137 if( DM::range_check && (n<0 || n>=int(length())) ) |
137 if(n<0 || n>=int(length())) |
138 fault("DirPath::nth: index out of range"); |
138 fault("DirPath::nth: index out of range"); |
139 return i=EdgeIt(*this, n); |
139 return i=EdgeIt(*this, n); |
140 } |
140 } |
141 |
141 |
142 /// Checks validity of a node or edge iterator. |
142 /// Checks validity of a node or edge iterator. |
146 |
146 |
147 /// Steps the given node or edge iterator. |
147 /// Steps the given node or edge iterator. |
148 template<typename It> |
148 template<typename It> |
149 static |
149 static |
150 It& next(It &e) { |
150 It& next(It &e) { |
151 if( DM::range_check && !e.valid() ) |
151 if( !e.valid() ) |
152 fault("DirPath::next() on invalid iterator"); |
152 fault("DirPath::next() on invalid iterator"); |
153 return ++e; |
153 return ++e; |
154 } |
154 } |
155 |
155 |
156 /// \brief Returns node iterator pointing to the head node of the |
156 /// \brief Returns node iterator pointing to the head node of the |
157 /// given edge iterator. |
157 /// given edge iterator. |
158 NodeIt head(const EdgeIt& e) const { |
158 NodeIt head(const EdgeIt& e) const { |
159 if( DM::range_check && !e.valid() ) |
159 if( !e.valid() ) |
160 fault("DirPath::head() on invalid iterator"); |
160 fault("DirPath::head() on invalid iterator"); |
161 return NodeIt(*this, e.idx+1); |
161 return NodeIt(*this, e.idx+1); |
162 } |
162 } |
163 |
163 |
164 /// \brief Returns node iterator pointing to the tail node of the |
164 /// \brief Returns node iterator pointing to the tail node of the |
165 /// given edge iterator. |
165 /// given edge iterator. |
166 NodeIt tail(const EdgeIt& e) const { |
166 NodeIt tail(const EdgeIt& e) const { |
167 if( DM::range_check && !e.valid() ) |
167 if( !e.valid() ) |
168 fault("DirPath::tail() on invalid iterator"); |
168 fault("DirPath::tail() on invalid iterator"); |
169 return NodeIt(*this, e.idx); |
169 return NodeIt(*this, e.idx); |
170 } |
170 } |
171 |
171 |
172 |
172 |
250 bool valid() const { return idx!=-1; } |
250 bool valid() const { return idx!=-1; } |
251 |
251 |
252 ///Conversion to Graph::Node |
252 ///Conversion to Graph::Node |
253 operator const GraphNode& () const { |
253 operator const GraphNode& () const { |
254 if(idx >= p->length()) |
254 if(idx >= p->length()) |
255 return p->to(); |
255 return p->head(); |
256 else if(idx >= 0) |
256 else if(idx >= 0) |
257 return p->gr->tail(p->edges[idx]); |
257 return p->gr->tail(p->edges[idx]); |
258 else |
258 else |
259 return INVALID; |
259 return INVALID; |
260 } |
260 } |
310 ///Push a new edge to the front of the path |
310 ///Push a new edge to the front of the path |
311 |
311 |
312 ///Push a new edge to the front of the path. |
312 ///Push a new edge to the front of the path. |
313 ///\sa setStartNode |
313 ///\sa setStartNode |
314 void pushFront(const GraphEdge& e) { |
314 void pushFront(const GraphEdge& e) { |
315 if( DM::consistensy_check && !empty() && P.gr->head(e)!=from() ) { |
315 if( !empty() && P.gr->head(e)!=tail() ) { |
316 fault("DirPath::Builder::pushFront: nonincident edge"); |
316 fault("DirPath::Builder::pushFront: nonincident edge"); |
317 } |
317 } |
318 front.push_back(e); |
318 front.push_back(e); |
319 } |
319 } |
320 |
320 |
321 ///Push a new edge to the back of the path |
321 ///Push a new edge to the back of the path |
322 |
322 |
323 ///Push a new edge to the back of the path. |
323 ///Push a new edge to the back of the path. |
324 ///\sa setStartNode |
324 ///\sa setStartNode |
325 void pushBack(const GraphEdge& e) { |
325 void pushBack(const GraphEdge& e) { |
326 if( DM::consistensy_check && !empty() && P.gr->tail(e)!=to() ) { |
326 if( !empty() && P.gr->tail(e)!=head() ) { |
327 fault("DirPath::Builder::pushBack: nonincident edge"); |
327 fault("DirPath::Builder::pushBack: nonincident edge"); |
328 } |
328 } |
329 back.push_back(e); |
329 back.push_back(e); |
330 } |
330 } |
331 |
331 |
353 void reserve(size_t r) { |
353 void reserve(size_t r) { |
354 front.reserve(r); |
354 front.reserve(r); |
355 back.reserve(r); |
355 back.reserve(r); |
356 } |
356 } |
357 |
357 |
|
358 void reserveFront(size_t r) {} |
|
359 void reserveBack(size_t r) {} |
|
360 |
358 private: |
361 private: |
359 bool empty() { |
362 bool empty() { |
360 return front.empty() && back.empty() && P.empty(); |
363 return front.empty() && back.empty() && P.empty(); |
361 } |
364 } |
362 |
365 |
363 GraphNode from() const { |
366 GraphNode tail() const { |
364 if( ! front.empty() ) |
367 if( ! front.empty() ) |
365 return P.gr->tail(front[front.size()-1]); |
368 return P.gr->tail(front[front.size()-1]); |
366 else if( ! P.empty() ) |
369 else if( ! P.empty() ) |
367 return P.gr->tail(P.edges[0]); |
370 return P.gr->tail(P.edges[0]); |
368 else if( ! back.empty() ) |
371 else if( ! back.empty() ) |
369 return P.gr->tail(back[0]); |
372 return P.gr->tail(back[0]); |
370 else |
373 else |
371 return INVALID; |
374 return INVALID; |
372 } |
375 } |
373 GraphNode to() const { |
376 GraphNode head() const { |
374 if( ! back.empty() ) |
377 if( ! back.empty() ) |
375 return P.gr->head(back[back.size()-1]); |
378 return P.gr->head(back[back.size()-1]); |
376 else if( ! P.empty() ) |
379 else if( ! P.empty() ) |
377 return P.gr->head(P.edges[P.length()-1]); |
380 return P.gr->head(P.edges[P.length()-1]); |
378 else if( ! front.empty() ) |
381 else if( ! front.empty() ) |
409 //! In a sense, the path can be treated as a graph, for is has \c NodeIt |
412 //! In a sense, the path can be treated as a graph, for is has \c NodeIt |
410 //! and \c EdgeIt with the same usage. These types converts to the \c Node |
413 //! and \c EdgeIt with the same usage. These types converts to the \c Node |
411 //! and \c Edge of the original graph. |
414 //! and \c Edge of the original graph. |
412 //! |
415 //! |
413 //! \todo Thoroughfully check all the range and consistency tests. |
416 //! \todo Thoroughfully check all the range and consistency tests. |
414 template<typename Graph, typename DM = DefaultDebugMode> |
417 template<typename Graph> |
415 class UndirPath { |
418 class UndirPath { |
416 public: |
419 public: |
417 /// Edge type of the underlying graph. |
420 /// Edge type of the underlying graph. |
418 typedef typename Graph::Edge GraphEdge; |
421 typedef typename Graph::Edge GraphEdge; |
419 /// Node type of the underlying graph. |
422 /// Node type of the underlying graph. |
435 /// \brief Subpath constructor. |
438 /// \brief Subpath constructor. |
436 /// |
439 /// |
437 /// Subpath defined by two nodes. |
440 /// Subpath defined by two nodes. |
438 /// \warning It is an error if the two edges are not in order! |
441 /// \warning It is an error if the two edges are not in order! |
439 UndirPath(const UndirPath &P, const NodeIt &a, const NodeIt &b) { |
442 UndirPath(const UndirPath &P, const NodeIt &a, const NodeIt &b) { |
440 if( DM::range_check && (!a.valid() || !b.valid) ) { |
443 if(!a.valid() || !b.valid) { |
441 // FIXME: this check should be more elaborate... |
444 // FIXME: this check should be more elaborate... |
442 fault("UndirPath, subpath ctor: invalid bounding nodes"); |
445 fault("UndirPath, subpath ctor: invalid bounding nodes"); |
443 } |
446 } |
444 gr = P.gr; |
447 gr = P.gr; |
445 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
448 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
448 /// \brief Subpath constructor. |
451 /// \brief Subpath constructor. |
449 /// |
452 /// |
450 /// Subpath defined by two edges. Contains edges in [a,b) |
453 /// Subpath defined by two edges. Contains edges in [a,b) |
451 /// \warning It is an error if the two edges are not in order! |
454 /// \warning It is an error if the two edges are not in order! |
452 UndirPath(const UndirPath &P, const EdgeIt &a, const EdgeIt &b) { |
455 UndirPath(const UndirPath &P, const EdgeIt &a, const EdgeIt &b) { |
453 if( DM::range_check && (!a.valid() || !b.valid) ) { |
456 if(!a.valid() || !b.valid) { |
454 // FIXME: this check should be more elaborate... |
457 // FIXME: this check should be more elaborate... |
455 fault("UndirPath, subpath ctor: invalid bounding nodes"); |
458 fault("UndirPath, subpath ctor: invalid bounding nodes"); |
456 } |
459 } |
457 gr = P.gr; |
460 gr = P.gr; |
458 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
461 edges.insert(edges.end(), P.edges.begin()+a.idx, P.edges.begin()+b.idx); |
468 |
471 |
469 /// \brief Starting point of the path. |
472 /// \brief Starting point of the path. |
470 /// |
473 /// |
471 /// Starting point of the path. |
474 /// Starting point of the path. |
472 /// Returns INVALID if the path is empty. |
475 /// Returns INVALID if the path is empty. |
473 GraphNode from() const { |
476 GraphNode tail() const { |
474 return empty() ? INVALID : gr->tail(edges[0]); |
477 return empty() ? INVALID : gr->tail(edges[0]); |
475 } |
478 } |
476 /// \brief End point of the path. |
479 /// \brief End point of the path. |
477 /// |
480 /// |
478 /// End point of the path. |
481 /// End point of the path. |
479 /// Returns INVALID if the path is empty. |
482 /// Returns INVALID if the path is empty. |
480 GraphNode to() const { |
483 GraphNode head() const { |
481 return empty() ? INVALID : gr->head(edges[length()-1]); |
484 return empty() ? INVALID : gr->head(edges[length()-1]); |
482 } |
485 } |
483 |
486 |
484 /// \brief Initializes node or edge iterator to point to the first |
487 /// \brief Initializes node or edge iterator to point to the first |
485 /// node or edge. |
488 /// node or edge. |
488 template<typename It> |
491 template<typename It> |
489 It& first(It &i) const { return i=It(*this); } |
492 It& first(It &i) const { return i=It(*this); } |
490 |
493 |
491 /// \brief Initializes node iterator to point to the node of a given index. |
494 /// \brief Initializes node iterator to point to the node of a given index. |
492 NodeIt& nth(NodeIt &i, int n) const { |
495 NodeIt& nth(NodeIt &i, int n) const { |
493 if( DM::range_check && (n<0 || n>int(length())) ) |
496 if(n<0 || n>int(length())) |
494 fault("UndirPath::nth: index out of range"); |
497 fault("UndirPath::nth: index out of range"); |
495 return i=NodeIt(*this, n); |
498 return i=NodeIt(*this, n); |
496 } |
499 } |
497 |
500 |
498 /// \brief Initializes edge iterator to point to the edge of a given index. |
501 /// \brief Initializes edge iterator to point to the edge of a given index. |
499 EdgeIt& nth(EdgeIt &i, int n) const { |
502 EdgeIt& nth(EdgeIt &i, int n) const { |
500 if( DM::range_check && (n<0 || n>=int(length())) ) |
503 if(n<0 || n>=int(length())) |
501 fault("UndirPath::nth: index out of range"); |
504 fault("UndirPath::nth: index out of range"); |
502 return i=EdgeIt(*this, n); |
505 return i=EdgeIt(*this, n); |
503 } |
506 } |
504 |
507 |
505 /// Checks validity of a node or edge iterator. |
508 /// Checks validity of a node or edge iterator. |
509 |
512 |
510 /// Steps the given node or edge iterator. |
513 /// Steps the given node or edge iterator. |
511 template<typename It> |
514 template<typename It> |
512 static |
515 static |
513 It& next(It &e) { |
516 It& next(It &e) { |
514 if( DM::range_check && !e.valid() ) |
517 if( !e.valid() ) |
515 fault("UndirPath::next() on invalid iterator"); |
518 fault("UndirPath::next() on invalid iterator"); |
516 return ++e; |
519 return ++e; |
517 } |
520 } |
518 |
521 |
519 /// \brief Returns node iterator pointing to the head node of the |
522 /// \brief Returns node iterator pointing to the head node of the |
520 /// given edge iterator. |
523 /// given edge iterator. |
521 NodeIt head(const EdgeIt& e) const { |
524 NodeIt head(const EdgeIt& e) const { |
522 if( DM::range_check && !e.valid() ) |
525 if( !e.valid() ) |
523 fault("UndirPath::head() on invalid iterator"); |
526 fault("UndirPath::head() on invalid iterator"); |
524 return NodeIt(*this, e.idx+1); |
527 return NodeIt(*this, e.idx+1); |
525 } |
528 } |
526 |
529 |
527 /// \brief Returns node iterator pointing to the tail node of the |
530 /// \brief Returns node iterator pointing to the tail node of the |
528 /// given edge iterator. |
531 /// given edge iterator. |
529 NodeIt tail(const EdgeIt& e) const { |
532 NodeIt tail(const EdgeIt& e) const { |
530 if( DM::range_check && !e.valid() ) |
533 if( !e.valid() ) |
531 fault("UndirPath::tail() on invalid iterator"); |
534 fault("UndirPath::tail() on invalid iterator"); |
532 return NodeIt(*this, e.idx); |
535 return NodeIt(*this, e.idx); |
533 } |
536 } |
534 |
537 |
535 |
538 |
611 bool valid() const { return idx!=-1; } |
614 bool valid() const { return idx!=-1; } |
612 |
615 |
613 ///Conversion to Graph::Node |
616 ///Conversion to Graph::Node |
614 operator const GraphNode& () const { |
617 operator const GraphNode& () const { |
615 if(idx >= p->length()) |
618 if(idx >= p->length()) |
616 return p->to(); |
619 return p->head(); |
617 else if(idx >= 0) |
620 else if(idx >= 0) |
618 return p->gr->tail(p->edges[idx]); |
621 return p->gr->tail(p->edges[idx]); |
619 else |
622 else |
620 return INVALID; |
623 return INVALID; |
621 } |
624 } |
671 ///Push a new edge to the front of the path |
674 ///Push a new edge to the front of the path |
672 |
675 |
673 ///Push a new edge to the front of the path. |
676 ///Push a new edge to the front of the path. |
674 ///\sa setStartNode |
677 ///\sa setStartNode |
675 void pushFront(const GraphEdge& e) { |
678 void pushFront(const GraphEdge& e) { |
676 if( DM::consistensy_check && !empty() && P.gr->head(e)!=from() ) { |
679 if( !empty() && P.gr->head(e)!=tail() ) { |
677 fault("UndirPath::Builder::pushFront: nonincident edge"); |
680 fault("UndirPath::Builder::pushFront: nonincident edge"); |
678 } |
681 } |
679 front.push_back(e); |
682 front.push_back(e); |
680 } |
683 } |
681 |
684 |
682 ///Push a new edge to the back of the path |
685 ///Push a new edge to the back of the path |
683 |
686 |
684 ///Push a new edge to the back of the path. |
687 ///Push a new edge to the back of the path. |
685 ///\sa setStartNode |
688 ///\sa setStartNode |
686 void pushBack(const GraphEdge& e) { |
689 void pushBack(const GraphEdge& e) { |
687 if( DM::consistensy_check && !empty() && P.gr->tail(e)!=to() ) { |
690 if( !empty() && P.gr->tail(e)!=head() ) { |
688 fault("UndirPath::Builder::pushBack: nonincident edge"); |
691 fault("UndirPath::Builder::pushBack: nonincident edge"); |
689 } |
692 } |
690 back.push_back(e); |
693 back.push_back(e); |
691 } |
694 } |
692 |
695 |
714 void reserve(size_t r) { |
717 void reserve(size_t r) { |
715 front.reserve(r); |
718 front.reserve(r); |
716 back.reserve(r); |
719 back.reserve(r); |
717 } |
720 } |
718 |
721 |
|
722 void reserveFront(size_t r) {} |
|
723 void reserveBack(size_t r) {} |
|
724 |
719 private: |
725 private: |
720 bool empty() { |
726 bool empty() { |
721 return front.empty() && back.empty() && P.empty(); |
727 return front.empty() && back.empty() && P.empty(); |
722 } |
728 } |
723 |
729 |
724 GraphNode from() const { |
730 GraphNode tail() const { |
725 if( ! front.empty() ) |
731 if( ! front.empty() ) |
726 return P.gr->tail(front[front.size()-1]); |
732 return P.gr->tail(front[front.size()-1]); |
727 else if( ! P.empty() ) |
733 else if( ! P.empty() ) |
728 return P.gr->tail(P.edges[0]); |
734 return P.gr->tail(P.edges[0]); |
729 else if( ! back.empty() ) |
735 else if( ! back.empty() ) |
730 return P.gr->tail(back[0]); |
736 return P.gr->tail(back[0]); |
731 else |
737 else |
732 return INVALID; |
738 return INVALID; |
733 } |
739 } |
734 GraphNode to() const { |
740 GraphNode head() const { |
735 if( ! back.empty() ) |
741 if( ! back.empty() ) |
736 return P.gr->head(back[back.size()-1]); |
742 return P.gr->head(back[back.size()-1]); |
737 else if( ! P.empty() ) |
743 else if( ! P.empty() ) |
738 return P.gr->head(P.edges[P.length()-1]); |
744 return P.gr->head(P.edges[P.length()-1]); |
739 else if( ! front.empty() ) |
745 else if( ! front.empty() ) |
789 /// Subpath defined by two edges. Contains edges in [a,b) |
795 /// Subpath defined by two edges. Contains edges in [a,b) |
790 /// It is an error if the two edges are not in order! |
796 /// It is an error if the two edges are not in order! |
791 DynamicPath(const DynamicPath &P, const EdgeIt &a, const EdgeIt &b); |
797 DynamicPath(const DynamicPath &P, const EdgeIt &a, const EdgeIt &b); |
792 |
798 |
793 size_t length() const { return edges.size(); } |
799 size_t length() const { return edges.size(); } |
794 GraphNode from() const { return _first; } |
800 GraphNode tail() const { return _first; } |
795 GraphNode to() const { return _last; } |
801 GraphNode head() const { return _last; } |
796 |
802 |
797 NodeIt& first(NodeIt &n) const { return nth(n, 0); } |
803 NodeIt& first(NodeIt &n) const { return nth(n, 0); } |
798 EdgeIt& first(EdgeIt &e) const { return nth(e, 0); } |
804 EdgeIt& first(EdgeIt &e) const { return nth(e, 0); } |
799 template<typename It> |
805 template<typename It> |
800 It first() const { |
806 It first() const { |