alpar@906
|
1 |
/* -*- C++ -*-
|
alpar@906
|
2 |
*
|
alpar@1956
|
3 |
* This file is a part of LEMON, a generic C++ optimization library
|
alpar@1956
|
4 |
*
|
alpar@1956
|
5 |
* Copyright (C) 2003-2006
|
alpar@1956
|
6 |
* Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
|
alpar@1359
|
7 |
* (Egervary Research Group on Combinatorial Optimization, EGRES).
|
alpar@906
|
8 |
*
|
alpar@906
|
9 |
* Permission to use, modify and distribute this software is granted
|
alpar@906
|
10 |
* provided that this copyright notice appears in all copies. For
|
alpar@906
|
11 |
* precise terms see the accompanying LICENSE file.
|
alpar@906
|
12 |
*
|
alpar@906
|
13 |
* This software is provided "AS IS" with no warranty of any kind,
|
alpar@906
|
14 |
* express or implied, and with no claim as to its suitability for any
|
alpar@906
|
15 |
* purpose.
|
alpar@906
|
16 |
*
|
alpar@906
|
17 |
*/
|
hegyi@819
|
18 |
|
hegyi@819
|
19 |
|
hegyi@819
|
20 |
///\ingroup paths
|
hegyi@819
|
21 |
///\file
|
hegyi@819
|
22 |
///\brief Classes for representing paths in graphs.
|
alpar@1151
|
23 |
///
|
hegyi@819
|
24 |
|
alpar@921
|
25 |
#ifndef LEMON_PATH_H
|
alpar@921
|
26 |
#define LEMON_PATH_H
|
hegyi@819
|
27 |
|
hegyi@819
|
28 |
#include <vector>
|
hegyi@819
|
29 |
#include <algorithm>
|
hegyi@819
|
30 |
|
deba@2247
|
31 |
#include <lemon/error.h>
|
deba@1993
|
32 |
#include <lemon/bits/invalid.h>
|
hegyi@819
|
33 |
|
alpar@921
|
34 |
namespace lemon {
|
hegyi@819
|
35 |
|
hegyi@819
|
36 |
/// \addtogroup paths
|
hegyi@819
|
37 |
/// @{
|
hegyi@819
|
38 |
|
hegyi@819
|
39 |
|
hegyi@819
|
40 |
//! \brief A structure for representing directed paths in a graph.
|
hegyi@819
|
41 |
//!
|
hegyi@819
|
42 |
//! A structure for representing directed path in a graph.
|
hegyi@819
|
43 |
//! \param Graph The graph type in which the path is.
|
hegyi@837
|
44 |
//!
|
hegyi@819
|
45 |
//! In a sense, the path can be treated as a graph, for is has \c NodeIt
|
hegyi@819
|
46 |
//! and \c EdgeIt with the same usage. These types converts to the \c Node
|
hegyi@819
|
47 |
//! and \c Edge of the original graph.
|
hegyi@819
|
48 |
//!
|
hegyi@819
|
49 |
//! \todo Thoroughfully check all the range and consistency tests.
|
hegyi@831
|
50 |
template<typename Graph>
|
deba@2247
|
51 |
class Path {
|
hegyi@819
|
52 |
public:
|
hegyi@819
|
53 |
/// Edge type of the underlying graph.
|
deba@2247
|
54 |
typedef typename Graph::Edge Edge;
|
hegyi@819
|
55 |
/// Node type of the underlying graph.
|
deba@2247
|
56 |
typedef typename Graph::Node Node;
|
deba@2247
|
57 |
|
hegyi@819
|
58 |
class NodeIt;
|
hegyi@819
|
59 |
class EdgeIt;
|
hegyi@819
|
60 |
|
deba@2247
|
61 |
struct PathError : public LogicError {
|
deba@2247
|
62 |
virtual const char* what() const throw() {
|
deba@2247
|
63 |
return "lemon::PathError";
|
deba@2247
|
64 |
}
|
deba@2247
|
65 |
};
|
hegyi@819
|
66 |
|
hegyi@819
|
67 |
public:
|
hegyi@819
|
68 |
|
deba@2247
|
69 |
/// \brief Constructor
|
deba@2247
|
70 |
///
|
deba@2247
|
71 |
/// Constructor
|
hegyi@819
|
72 |
/// \param _G The graph in which the path is.
|
deba@2247
|
73 |
Path(const Graph &_graph) : graph(&_graph), start(INVALID) {}
|
deba@2247
|
74 |
|
hegyi@819
|
75 |
/// \brief Subpath constructor.
|
hegyi@819
|
76 |
///
|
hegyi@819
|
77 |
/// Subpath defined by two nodes.
|
hegyi@819
|
78 |
/// \warning It is an error if the two edges are not in order!
|
deba@2247
|
79 |
Path(const Path &other, const NodeIt &a, const NodeIt &b) {
|
deba@2247
|
80 |
graph = other.graph;
|
deba@2247
|
81 |
start = a;
|
deba@2247
|
82 |
edges.insert(edges.end(),
|
deba@2247
|
83 |
other.edges.begin() + a.id, other.edges.begin() + b.id);
|
hegyi@819
|
84 |
}
|
hegyi@819
|
85 |
|
hegyi@819
|
86 |
/// \brief Subpath constructor.
|
hegyi@819
|
87 |
///
|
hegyi@819
|
88 |
/// Subpath defined by two edges. Contains edges in [a,b)
|
hegyi@819
|
89 |
/// \warning It is an error if the two edges are not in order!
|
deba@2247
|
90 |
Path(const Path &other, const EdgeIt &a, const EdgeIt &b) {
|
deba@2247
|
91 |
graph = other.graph;
|
deba@2247
|
92 |
start = graph->source(a);
|
deba@2247
|
93 |
edges.insert(edges.end(),
|
deba@2247
|
94 |
other.edges.begin() + a.id, other.edges.begin() + b.id);
|
hegyi@819
|
95 |
}
|
hegyi@819
|
96 |
|
deba@2247
|
97 |
/// \brief Length of the path.
|
deba@2247
|
98 |
///
|
deba@2247
|
99 |
/// The number of the edges in the path. It can be zero if the
|
deba@2247
|
100 |
/// path has only one node or it is empty.
|
alpar@1282
|
101 |
int length() const { return edges.size(); }
|
hegyi@819
|
102 |
|
deba@2247
|
103 |
/// \brief Returns whether the path is empty.
|
deba@2247
|
104 |
///
|
deba@2247
|
105 |
/// Returns true when the path does not contain neither edge nor
|
deba@2247
|
106 |
/// node.
|
deba@2247
|
107 |
bool empty() const { return start == INVALID; }
|
deba@2247
|
108 |
|
deba@2247
|
109 |
/// \brief Resets the path to an empty path.
|
deba@2247
|
110 |
///
|
hegyi@819
|
111 |
/// Resets the path to an empty path.
|
deba@2247
|
112 |
void clear() { edges.clear(); start = INVALID; }
|
hegyi@819
|
113 |
|
hegyi@819
|
114 |
/// \brief Starting point of the path.
|
hegyi@819
|
115 |
///
|
hegyi@819
|
116 |
/// Starting point of the path.
|
hegyi@819
|
117 |
/// Returns INVALID if the path is empty.
|
deba@2247
|
118 |
Node source() const {
|
deba@2247
|
119 |
return start;
|
hegyi@819
|
120 |
}
|
hegyi@819
|
121 |
/// \brief End point of the path.
|
hegyi@819
|
122 |
///
|
hegyi@819
|
123 |
/// End point of the path.
|
hegyi@819
|
124 |
/// Returns INVALID if the path is empty.
|
deba@2247
|
125 |
Node target() const {
|
deba@2247
|
126 |
return length() == 0 ? start : graph->target(edges[length()-1]);
|
hegyi@819
|
127 |
}
|
hegyi@819
|
128 |
|
deba@2247
|
129 |
/// \brief Gives back a node iterator to point to the node of a
|
deba@2247
|
130 |
/// given index.
|
hegyi@819
|
131 |
///
|
deba@2247
|
132 |
/// Gives back a node iterator to point to the node of a given
|
deba@2247
|
133 |
/// index.
|
deba@2247
|
134 |
/// \pre n should less or equal to \c length()
|
deba@2247
|
135 |
NodeIt nthNode(int n) const {
|
deba@2247
|
136 |
return NodeIt(*this, n);
|
hegyi@819
|
137 |
}
|
hegyi@819
|
138 |
|
deba@2247
|
139 |
/// \brief Gives back an edge iterator to point to the edge of a
|
deba@2247
|
140 |
/// given index.
|
deba@2247
|
141 |
///
|
deba@2247
|
142 |
/// Gives back an edge iterator to point to the node of a given
|
deba@2247
|
143 |
/// index.
|
deba@2247
|
144 |
/// \pre n should less than \c length()
|
deba@2247
|
145 |
EdgeIt nthEdge(int n) const {
|
deba@2247
|
146 |
return EdgeIt(*this, n);
|
deba@2247
|
147 |
}
|
deba@2247
|
148 |
|
deba@2247
|
149 |
/// \brief Returns node iterator pointing to the source node of the
|
deba@2247
|
150 |
/// given edge iterator.
|
deba@2247
|
151 |
///
|
deba@2247
|
152 |
/// Returns node iterator pointing to the source node of the given
|
deba@2247
|
153 |
/// edge iterator.
|
deba@2247
|
154 |
NodeIt source(const EdgeIt& e) const {
|
deba@2247
|
155 |
return NodeIt(*this, e.id);
|
hegyi@819
|
156 |
}
|
hegyi@819
|
157 |
|
alpar@986
|
158 |
/// \brief Returns node iterator pointing to the target node of the
|
hegyi@819
|
159 |
/// given edge iterator.
|
deba@2247
|
160 |
///
|
deba@2247
|
161 |
/// Returns node iterator pointing to the target node of the given
|
deba@2247
|
162 |
/// edge iterator.
|
alpar@986
|
163 |
NodeIt target(const EdgeIt& e) const {
|
deba@2247
|
164 |
return NodeIt(*this, e.id + 1);
|
hegyi@819
|
165 |
}
|
hegyi@819
|
166 |
|
hegyi@819
|
167 |
|
deba@2247
|
168 |
/// \brief Iterator class to iterate on the nodes of the paths
|
deba@2247
|
169 |
///
|
deba@2247
|
170 |
/// This class is used to iterate on the nodes of the paths
|
deba@2247
|
171 |
///
|
deba@2247
|
172 |
/// Of course it converts to Graph::Node
|
deba@2247
|
173 |
class NodeIt {
|
deba@2247
|
174 |
friend class Path;
|
deba@2247
|
175 |
public:
|
hegyi@819
|
176 |
|
deba@2247
|
177 |
/// \brief Default constructor
|
deba@2247
|
178 |
///
|
deba@2247
|
179 |
/// Default constructor
|
deba@2247
|
180 |
NodeIt() {}
|
hegyi@819
|
181 |
|
deba@2247
|
182 |
/// \brief Invalid constructor
|
deba@2247
|
183 |
///
|
deba@2247
|
184 |
/// Invalid constructor
|
deba@2247
|
185 |
NodeIt(Invalid) : id(-1), path(0) {}
|
deba@2247
|
186 |
|
deba@2247
|
187 |
/// \brief Constructor with starting point
|
deba@2247
|
188 |
///
|
deba@2247
|
189 |
/// Constructor with starting point
|
deba@2247
|
190 |
NodeIt(const Path &_path, int _id = 0) : id(_id), path(&_path) {
|
deba@2247
|
191 |
if (id > path->length()) id = -1;
|
deba@2247
|
192 |
}
|
deba@2247
|
193 |
|
deba@2247
|
194 |
/// \brief Conversion to Graph::Node
|
deba@2247
|
195 |
///
|
deba@2247
|
196 |
/// Conversion to Graph::Node
|
deba@2247
|
197 |
operator Node() const {
|
deba@2247
|
198 |
if (id > 0) {
|
deba@2247
|
199 |
return path->graph->target(path->edges[id - 1]);
|
deba@2247
|
200 |
} else if (id == 0) {
|
deba@2247
|
201 |
return path->start;
|
deba@2247
|
202 |
} else {
|
deba@2247
|
203 |
return INVALID;
|
deba@2247
|
204 |
}
|
deba@2247
|
205 |
}
|
deba@2247
|
206 |
|
deba@2247
|
207 |
/// \brief Steps to the next node
|
deba@2247
|
208 |
///
|
deba@2247
|
209 |
/// Steps to the next node
|
deba@2247
|
210 |
NodeIt& operator++() {
|
deba@2247
|
211 |
++id;
|
deba@2247
|
212 |
if (id > path->length()) id = -1;
|
deba@2247
|
213 |
return *this;
|
deba@2247
|
214 |
}
|
deba@2247
|
215 |
|
deba@2247
|
216 |
/// \brief Comparison operator
|
deba@2247
|
217 |
///
|
deba@2247
|
218 |
/// Comparison operator
|
deba@2247
|
219 |
bool operator==(const NodeIt& n) const { return id == n.id; }
|
deba@2247
|
220 |
|
deba@2247
|
221 |
/// \brief Comparison operator
|
deba@2247
|
222 |
///
|
deba@2247
|
223 |
/// Comparison operator
|
deba@2247
|
224 |
bool operator!=(const NodeIt& n) const { return id != n.id; }
|
deba@2247
|
225 |
|
deba@2247
|
226 |
/// \brief Comparison operator
|
deba@2247
|
227 |
///
|
deba@2247
|
228 |
/// Comparison operator
|
deba@2247
|
229 |
bool operator<(const NodeIt& n) const { return id < n.id; }
|
deba@2247
|
230 |
|
deba@2247
|
231 |
private:
|
deba@2247
|
232 |
int id;
|
deba@2247
|
233 |
const Path *path;
|
deba@2247
|
234 |
};
|
deba@2247
|
235 |
|
deba@2247
|
236 |
/// \brief Iterator class to iterate on the edges of the paths
|
deba@2247
|
237 |
///
|
deba@2247
|
238 |
/// This class is used to iterate on the edges of the paths
|
deba@2247
|
239 |
/// Of course it converts to Graph::Edge
|
hegyi@819
|
240 |
class EdgeIt {
|
deba@2247
|
241 |
friend class Path;
|
deba@2247
|
242 |
public:
|
hegyi@819
|
243 |
|
deba@2247
|
244 |
/// \brief Default constructor
|
deba@2247
|
245 |
///
|
hegyi@819
|
246 |
/// Default constructor
|
hegyi@819
|
247 |
EdgeIt() {}
|
deba@2247
|
248 |
|
deba@2247
|
249 |
/// \brief Invalid constructor
|
deba@2247
|
250 |
///
|
hegyi@819
|
251 |
/// Invalid constructor
|
deba@2247
|
252 |
EdgeIt(Invalid) : id(-1), path(0) {}
|
deba@2247
|
253 |
|
deba@2247
|
254 |
/// \brief Constructor with starting point
|
deba@2247
|
255 |
///
|
hegyi@819
|
256 |
/// Constructor with starting point
|
deba@2247
|
257 |
EdgeIt(const Path &_path, int _id = 0) : id(_id), path(&_path) {
|
deba@2247
|
258 |
if (id >= path->length()) id = -1;
|
hegyi@819
|
259 |
}
|
hegyi@819
|
260 |
|
deba@2247
|
261 |
/// \brief Conversion to Graph::Edge
|
deba@2247
|
262 |
///
|
deba@2247
|
263 |
/// Conversion to Graph::Edge
|
deba@2247
|
264 |
operator Edge() const {
|
deba@2247
|
265 |
return id != -1 ? path->edges[id] : INVALID;
|
deba@2247
|
266 |
}
|
hegyi@819
|
267 |
|
deba@2247
|
268 |
/// \brief Steps to the next edge
|
deba@2247
|
269 |
///
|
deba@2247
|
270 |
/// Steps to the next edge
|
deba@2247
|
271 |
EdgeIt& operator++() {
|
deba@2247
|
272 |
++id;
|
deba@2247
|
273 |
if (id >= path->length()) id = -1;
|
deba@2247
|
274 |
return *this;
|
deba@2247
|
275 |
}
|
deba@2247
|
276 |
|
deba@2247
|
277 |
/// \brief Comparison operator
|
deba@2247
|
278 |
///
|
hegyi@819
|
279 |
/// Comparison operator
|
deba@2247
|
280 |
bool operator==(const EdgeIt& e) const { return id == e.id; }
|
deba@2247
|
281 |
|
deba@2247
|
282 |
/// \brief Comparison operator
|
deba@2247
|
283 |
///
|
hegyi@819
|
284 |
/// Comparison operator
|
deba@2247
|
285 |
bool operator!=(const EdgeIt& e) const { return id != e.id; }
|
deba@2247
|
286 |
|
deba@2247
|
287 |
/// \brief Comparison operator
|
deba@2247
|
288 |
///
|
hegyi@819
|
289 |
/// Comparison operator
|
deba@2247
|
290 |
bool operator<(const EdgeIt& e) const { return id < e.id; }
|
hegyi@819
|
291 |
|
hegyi@819
|
292 |
private:
|
deba@2247
|
293 |
|
deba@2247
|
294 |
int id;
|
deba@2247
|
295 |
const Path *path;
|
hegyi@819
|
296 |
};
|
hegyi@819
|
297 |
|
deba@2247
|
298 |
protected:
|
hegyi@819
|
299 |
|
deba@2247
|
300 |
const Graph *graph;
|
hegyi@819
|
301 |
|
deba@2247
|
302 |
typedef std::vector<Edge> Container;
|
deba@2247
|
303 |
Container edges;
|
deba@2247
|
304 |
Node start;
|
hegyi@819
|
305 |
|
deba@2247
|
306 |
public:
|
hegyi@819
|
307 |
|
hegyi@837
|
308 |
friend class Builder;
|
hegyi@819
|
309 |
|
deba@2247
|
310 |
/// \brief Class to build paths
|
deba@2247
|
311 |
///
|
deba@2247
|
312 |
/// This class is used to fill a path with edges.
|
deba@2247
|
313 |
///
|
deba@2247
|
314 |
/// You can push new edges to the front and to the back of the
|
deba@2247
|
315 |
/// path in arbitrary order then you should commit these changes
|
deba@2247
|
316 |
/// to the graph.
|
deba@2247
|
317 |
///
|
deba@2247
|
318 |
/// Fundamentally, for most "Paths" (classes fulfilling the
|
deba@2247
|
319 |
/// PathConcept) while the builder is active (after the first
|
deba@2247
|
320 |
/// modifying operation and until the commit()) the original Path
|
deba@2247
|
321 |
/// is in a "transitional" state (operations on it have undefined
|
deba@2247
|
322 |
/// result). But in the case of Path the original path remains
|
deba@2247
|
323 |
/// unchanged until the commit. However we don't recomend that you
|
deba@2247
|
324 |
/// use this feature.
|
hegyi@819
|
325 |
class Builder {
|
deba@2247
|
326 |
public:
|
deba@2247
|
327 |
/// \brief Constructor
|
deba@2247
|
328 |
///
|
deba@2247
|
329 |
/// Constructor
|
deba@2247
|
330 |
/// \param _path the path you want to fill in.
|
deba@2247
|
331 |
Builder(Path &_path) : path(_path), start(INVALID) {}
|
hegyi@819
|
332 |
|
deba@2247
|
333 |
/// \brief Destructor
|
hegyi@819
|
334 |
///
|
deba@2247
|
335 |
/// Destructor
|
deba@2247
|
336 |
~Builder() {
|
deba@2247
|
337 |
LEMON_ASSERT(front.empty() && back.empty() && start == INVALID,
|
deba@2247
|
338 |
PathError());
|
deba@2247
|
339 |
}
|
hegyi@819
|
340 |
|
deba@2247
|
341 |
/// \brief Sets the starting node of the path.
|
deba@2247
|
342 |
///
|
deba@2247
|
343 |
/// Sets the starting node of the path. Edge added to the path
|
deba@2247
|
344 |
/// afterwards have to be incident to this node. It should be
|
deba@2247
|
345 |
/// called if and only if the path is empty and before any call
|
deba@2247
|
346 |
/// to \ref pushFront() or \ref pushBack()
|
deba@2247
|
347 |
void setStartNode(const Node &_start) {
|
deba@2247
|
348 |
LEMON_ASSERT(path.empty() && start == INVALID, PathError());
|
deba@2247
|
349 |
start = _start;
|
deba@2247
|
350 |
}
|
hegyi@837
|
351 |
|
deba@2247
|
352 |
/// \brief Push a new edge to the front of the path
|
deba@2247
|
353 |
///
|
deba@2247
|
354 |
/// Push a new edge to the front of the path.
|
deba@2247
|
355 |
/// \sa setStartNode
|
deba@2247
|
356 |
void pushFront(const Edge& e) {
|
deba@2247
|
357 |
LEMON_ASSERT(front.empty() ||
|
deba@2247
|
358 |
(path.graph->source(front.back()) ==
|
deba@2247
|
359 |
path.graph->target(e)), PathError());
|
deba@2247
|
360 |
LEMON_ASSERT(path.empty() ||
|
deba@2247
|
361 |
(path.source() == path.graph->target(e)), PathError());
|
deba@2247
|
362 |
LEMON_ASSERT(!path.empty() || !front.empty() ||
|
deba@2247
|
363 |
(start == path.graph->target(e)), PathError());
|
hegyi@819
|
364 |
front.push_back(e);
|
hegyi@819
|
365 |
}
|
hegyi@819
|
366 |
|
deba@2247
|
367 |
/// \brief Push a new edge to the back of the path
|
deba@2247
|
368 |
///
|
deba@2247
|
369 |
/// Push a new edge to the back of the path.
|
deba@2247
|
370 |
/// \sa setStartNode
|
deba@2247
|
371 |
void pushBack(const Edge& e) {
|
deba@2247
|
372 |
LEMON_ASSERT(back.empty() ||
|
deba@2247
|
373 |
(path.graph->target(back.back()) ==
|
deba@2247
|
374 |
path.graph->source(e)), PathError());
|
deba@2247
|
375 |
LEMON_ASSERT(path.empty() ||
|
deba@2247
|
376 |
(path.target() == path.graph->source(e)), PathError());
|
deba@2247
|
377 |
LEMON_ASSERT(!path.empty() || !back.empty() ||
|
deba@2247
|
378 |
(start == path.graph->source(e)), PathError());
|
hegyi@819
|
379 |
back.push_back(e);
|
hegyi@819
|
380 |
}
|
hegyi@819
|
381 |
|
deba@2247
|
382 |
/// \brief Commit the changes to the path.
|
deba@2247
|
383 |
///
|
deba@2247
|
384 |
/// Commit the changes to the path.
|
hegyi@819
|
385 |
void commit() {
|
deba@2247
|
386 |
if( !front.empty() || !back.empty() || start != INVALID) {
|
hegyi@819
|
387 |
Container tmp;
|
deba@2247
|
388 |
tmp.reserve(front.size() + back.size() + path.length());
|
hegyi@819
|
389 |
tmp.insert(tmp.end(), front.rbegin(), front.rend());
|
deba@2247
|
390 |
tmp.insert(tmp.end(), path.edges.begin(), path.edges.end());
|
hegyi@819
|
391 |
tmp.insert(tmp.end(), back.begin(), back.end());
|
deba@2247
|
392 |
path.edges.swap(tmp);
|
deba@2247
|
393 |
if (!front.empty()) {
|
deba@2247
|
394 |
path.start = path.graph->source(front.back());
|
deba@2247
|
395 |
} else {
|
deba@2247
|
396 |
path.start = start;
|
deba@2247
|
397 |
}
|
deba@2247
|
398 |
start = INVALID;
|
hegyi@819
|
399 |
front.clear();
|
hegyi@819
|
400 |
back.clear();
|
hegyi@819
|
401 |
}
|
hegyi@819
|
402 |
}
|
hegyi@819
|
403 |
|
deba@2247
|
404 |
/// \brief Reserve storage for the builder in advance.
|
deba@2247
|
405 |
///
|
deba@2247
|
406 |
/// If you know a reasonable upper bound of the number of the
|
deba@2247
|
407 |
/// edges to add to the front, using this function you can speed
|
deba@2247
|
408 |
/// up the building.
|
hegyi@837
|
409 |
void reserveFront(size_t r) {front.reserve(r);}
|
hegyi@837
|
410 |
|
deba@2247
|
411 |
/// \brief Reserve storage for the builder in advance.
|
deba@2247
|
412 |
///
|
deba@2247
|
413 |
/// If you know a reasonable upper bound of the number of the
|
deba@2247
|
414 |
/// edges to add to the back, using this function you can speed
|
deba@2247
|
415 |
/// up the building.
|
hegyi@837
|
416 |
void reserveBack(size_t r) {back.reserve(r);}
|
hegyi@831
|
417 |
|
hegyi@819
|
418 |
private:
|
hegyi@819
|
419 |
|
deba@2247
|
420 |
Path &path;
|
deba@2247
|
421 |
Container front, back;
|
deba@2247
|
422 |
Node start;
|
hegyi@819
|
423 |
|
hegyi@819
|
424 |
};
|
hegyi@819
|
425 |
|
hegyi@819
|
426 |
};
|
hegyi@819
|
427 |
|
hegyi@819
|
428 |
///@}
|
hegyi@819
|
429 |
|
alpar@921
|
430 |
} // namespace lemon
|
hegyi@819
|
431 |
|
alpar@921
|
432 |
#endif // LEMON_PATH_H
|