1 | //======================================================================= |
2 | // Copyright 1997, 1998, 1999, 2000 University of Notre Dame. |
3 | // Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek |
4 | // |
5 | // Distributed under the Boost Software License, Version 1.0. (See |
6 | // accompanying file LICENSE_1_0.txt or copy at |
7 | // http://www.boost.org/LICENSE_1_0.txt) |
8 | //======================================================================= |
9 | |
10 | #ifndef BOOST_FILTERED_GRAPH_HPP |
11 | #define BOOST_FILTERED_GRAPH_HPP |
12 | |
13 | #include <boost/graph/graph_traits.hpp> |
14 | #include <boost/graph/properties.hpp> |
15 | #include <boost/graph/adjacency_iterator.hpp> |
16 | #include <boost/graph/detail/set_adaptor.hpp> |
17 | #include <boost/iterator/filter_iterator.hpp> |
18 | |
19 | namespace boost { |
20 | |
21 | //========================================================================= |
22 | // Some predicate classes. |
23 | |
24 | struct keep_all { |
25 | template <typename T> |
26 | bool operator()(const T&) const { return true; } |
27 | }; |
28 | |
29 | // Keep residual edges (used in maximum-flow algorithms). |
30 | template <typename ResidualCapacityEdgeMap> |
31 | struct is_residual_edge { |
32 | is_residual_edge() { } |
33 | is_residual_edge(ResidualCapacityEdgeMap rcap) : m_rcap(rcap) { } |
34 | template <typename Edge> |
35 | bool operator()(const Edge& e) const { |
36 | return 0 < get(m_rcap, e); |
37 | } |
38 | ResidualCapacityEdgeMap m_rcap; |
39 | }; |
40 | |
41 | template <typename Set> |
42 | struct is_in_subset { |
43 | is_in_subset() : m_s(0) { } |
44 | is_in_subset(const Set& s) : m_s(&s) { } |
45 | |
46 | template <typename Elt> |
47 | bool operator()(const Elt& x) const { |
48 | return set_contains(*m_s, x); |
49 | } |
50 | const Set* m_s; |
51 | }; |
52 | |
53 | template <typename Set> |
54 | struct is_not_in_subset { |
55 | is_not_in_subset() : m_s(0) { } |
56 | is_not_in_subset(const Set& s) : m_s(&s) { } |
57 | |
58 | template <typename Elt> |
59 | bool operator()(const Elt& x) const { |
60 | return !set_contains(*m_s, x); |
61 | } |
62 | const Set* m_s; |
63 | }; |
64 | |
65 | namespace detail { |
66 | |
67 | template <typename EdgePredicate, typename VertexPredicate, typename Graph> |
68 | struct out_edge_predicate { |
69 | out_edge_predicate() { } |
70 | out_edge_predicate(EdgePredicate ep, VertexPredicate vp, |
71 | const Graph& g) |
72 | : m_edge_pred(ep), m_vertex_pred(vp), m_g(&g) { } |
73 | |
74 | template <typename Edge> |
75 | bool operator()(const Edge& e) const { |
76 | return m_edge_pred(e) && m_vertex_pred(target(e, *m_g)); |
77 | } |
78 | EdgePredicate m_edge_pred; |
79 | VertexPredicate m_vertex_pred; |
80 | const Graph* m_g; |
81 | }; |
82 | |
83 | template <typename EdgePredicate, typename VertexPredicate, typename Graph> |
84 | struct in_edge_predicate { |
85 | in_edge_predicate() { } |
86 | in_edge_predicate(EdgePredicate ep, VertexPredicate vp, |
87 | const Graph& g) |
88 | : m_edge_pred(ep), m_vertex_pred(vp), m_g(&g) { } |
89 | |
90 | template <typename Edge> |
91 | bool operator()(const Edge& e) const { |
92 | return m_edge_pred(e) && m_vertex_pred(source(e, *m_g)); |
93 | } |
94 | EdgePredicate m_edge_pred; |
95 | VertexPredicate m_vertex_pred; |
96 | const Graph* m_g; |
97 | }; |
98 | |
99 | template <typename EdgePredicate, typename VertexPredicate, typename Graph> |
100 | struct edge_predicate { |
101 | edge_predicate() { } |
102 | edge_predicate(EdgePredicate ep, VertexPredicate vp, |
103 | const Graph& g) |
104 | : m_edge_pred(ep), m_vertex_pred(vp), m_g(&g) { } |
105 | |
106 | template <typename Edge> |
107 | bool operator()(const Edge& e) const { |
108 | return m_edge_pred(e) |
109 | && m_vertex_pred(source(e, *m_g)) && m_vertex_pred(target(e, *m_g)); |
110 | } |
111 | EdgePredicate m_edge_pred; |
112 | VertexPredicate m_vertex_pred; |
113 | const Graph* m_g; |
114 | }; |
115 | |
116 | } // namespace detail |
117 | |
118 | |
119 | //=========================================================================== |
120 | // Filtered Graph |
121 | |
122 | struct filtered_graph_tag { }; |
123 | |
124 | // This base class is a stupid hack to change overload resolution |
125 | // rules for the source and target functions so that they are a |
126 | // worse match than the source and target functions defined for |
127 | // pairs in graph_traits.hpp. I feel dirty. -JGS |
128 | template <class G> |
129 | struct filtered_graph_base { |
130 | typedef graph_traits<G> Traits; |
131 | typedef typename Traits::vertex_descriptor vertex_descriptor; |
132 | typedef typename Traits::edge_descriptor edge_descriptor; |
133 | filtered_graph_base(const G& g) : m_g(g) { } |
134 | //protected: |
135 | const G& m_g; |
136 | }; |
137 | |
138 | template <typename Graph, |
139 | typename EdgePredicate, |
140 | typename VertexPredicate = keep_all> |
141 | class filtered_graph : public filtered_graph_base<Graph> { |
142 | typedef filtered_graph_base<Graph> Base; |
143 | typedef graph_traits<Graph> Traits; |
144 | typedef filtered_graph self; |
145 | public: |
146 | typedef Graph graph_type; |
147 | typedef detail::out_edge_predicate<EdgePredicate, |
148 | VertexPredicate, self> OutEdgePred; |
149 | typedef detail::in_edge_predicate<EdgePredicate, |
150 | VertexPredicate, self> InEdgePred; |
151 | typedef detail::edge_predicate<EdgePredicate, |
152 | VertexPredicate, self> EdgePred; |
153 | |
154 | // Constructors |
155 | filtered_graph(const Graph& g, EdgePredicate ep) |
156 | : Base(g), m_edge_pred(ep) { } |
157 | |
158 | filtered_graph(const Graph& g, EdgePredicate ep, VertexPredicate vp) |
159 | : Base(g), m_edge_pred(ep), m_vertex_pred(vp) { } |
160 | |
161 | // Graph requirements |
162 | typedef typename Traits::vertex_descriptor vertex_descriptor; |
163 | typedef typename Traits::edge_descriptor edge_descriptor; |
164 | typedef typename Traits::directed_category directed_category; |
165 | typedef typename Traits::edge_parallel_category edge_parallel_category; |
166 | typedef typename Traits::traversal_category traversal_category; |
167 | |
168 | // IncidenceGraph requirements |
169 | typedef filter_iterator< |
170 | OutEdgePred, typename Traits::out_edge_iterator |
171 | > out_edge_iterator; |
172 | |
173 | typedef typename Traits::degree_size_type degree_size_type; |
174 | |
175 | // AdjacencyGraph requirements |
176 | typedef typename adjacency_iterator_generator<self, |
177 | vertex_descriptor, out_edge_iterator>::type adjacency_iterator; |
178 | |
179 | // BidirectionalGraph requirements |
180 | typedef filter_iterator< |
181 | InEdgePred, typename Traits::in_edge_iterator |
182 | > in_edge_iterator; |
183 | |
184 | // VertexListGraph requirements |
185 | typedef filter_iterator< |
186 | VertexPredicate, typename Traits::vertex_iterator |
187 | > vertex_iterator; |
188 | typedef typename Traits::vertices_size_type vertices_size_type; |
189 | |
190 | // EdgeListGraph requirements |
191 | typedef filter_iterator< |
192 | EdgePred, typename Traits::edge_iterator |
193 | > edge_iterator; |
194 | typedef typename Traits::edges_size_type edges_size_type; |
195 | |
196 | typedef filtered_graph_tag graph_tag; |
197 | |
198 | // Bundled properties support |
199 | template<typename Descriptor> |
200 | typename graph::detail::bundled_result<Graph, Descriptor>::type& |
201 | operator[](Descriptor x) |
202 | { return const_cast<Graph&>(this->m_g)[x]; } |
203 | |
204 | template<typename Descriptor> |
205 | typename graph::detail::bundled_result<Graph, Descriptor>::type const& |
206 | operator[](Descriptor x) const |
207 | { return this->m_g[x]; } |
208 | |
209 | static vertex_descriptor null_vertex() |
210 | { |
211 | return Traits::null_vertex(); |
212 | } |
213 | |
214 | //private: |
215 | EdgePredicate m_edge_pred; |
216 | VertexPredicate m_vertex_pred; |
217 | }; |
218 | |
219 | // Do not instantiate these unless needed |
220 | template <typename Graph, |
221 | typename EdgePredicate, |
222 | typename VertexPredicate> |
223 | struct vertex_property_type<filtered_graph<Graph, EdgePredicate, VertexPredicate> >: |
224 | vertex_property_type<Graph> {}; |
225 | |
226 | template <typename Graph, |
227 | typename EdgePredicate, |
228 | typename VertexPredicate> |
229 | struct edge_property_type<filtered_graph<Graph, EdgePredicate, VertexPredicate> >: |
230 | edge_property_type<Graph> {}; |
231 | |
232 | template <typename Graph, |
233 | typename EdgePredicate, |
234 | typename VertexPredicate> |
235 | struct graph_property_type<filtered_graph<Graph, EdgePredicate, VertexPredicate> >: |
236 | graph_property_type<Graph> {}; |
237 | |
238 | template<typename Graph, typename EdgePredicate, typename VertexPredicate> |
239 | struct vertex_bundle_type<filtered_graph<Graph, EdgePredicate, |
240 | VertexPredicate> > |
241 | : vertex_bundle_type<Graph> { }; |
242 | |
243 | template<typename Graph, typename EdgePredicate, typename VertexPredicate> |
244 | struct edge_bundle_type<filtered_graph<Graph, EdgePredicate, |
245 | VertexPredicate> > |
246 | : edge_bundle_type<Graph> { }; |
247 | |
248 | template<typename Graph, typename EdgePredicate, typename VertexPredicate> |
249 | struct graph_bundle_type<filtered_graph<Graph, EdgePredicate, |
250 | VertexPredicate> > |
251 | : graph_bundle_type<Graph> { }; |
252 | |
253 | //=========================================================================== |
254 | // Non-member functions for the Filtered Edge Graph |
255 | |
256 | // Helper functions |
257 | template <typename Graph, typename EdgePredicate> |
258 | inline filtered_graph<Graph, EdgePredicate> |
259 | make_filtered_graph(Graph& g, EdgePredicate ep) { |
260 | return filtered_graph<Graph, EdgePredicate>(g, ep); |
261 | } |
262 | template <typename Graph, typename EdgePredicate, typename VertexPredicate> |
263 | inline filtered_graph<Graph, EdgePredicate, VertexPredicate> |
264 | make_filtered_graph(Graph& g, EdgePredicate ep, VertexPredicate vp) { |
265 | return filtered_graph<Graph, EdgePredicate, VertexPredicate>(g, ep, vp); |
266 | } |
267 | |
268 | template <typename Graph, typename EdgePredicate> |
269 | inline filtered_graph<const Graph, EdgePredicate> |
270 | make_filtered_graph(const Graph& g, EdgePredicate ep) { |
271 | return filtered_graph<const Graph, EdgePredicate>(g, ep); |
272 | } |
273 | template <typename Graph, typename EdgePredicate, typename VertexPredicate> |
274 | inline filtered_graph<const Graph, EdgePredicate, VertexPredicate> |
275 | make_filtered_graph(const Graph& g, EdgePredicate ep, VertexPredicate vp) { |
276 | return filtered_graph<const Graph, EdgePredicate, VertexPredicate>(g, ep, vp); |
277 | } |
278 | |
279 | template <typename G, typename EP, typename VP> |
280 | std::pair<typename filtered_graph<G, EP, VP>::vertex_iterator, |
281 | typename filtered_graph<G, EP, VP>::vertex_iterator> |
282 | vertices(const filtered_graph<G, EP, VP>& g) |
283 | { |
284 | typedef filtered_graph<G, EP, VP> Graph; |
285 | typename graph_traits<G>::vertex_iterator f, l; |
286 | boost::tie(f, l) = vertices(g.m_g); |
287 | typedef typename Graph::vertex_iterator iter; |
288 | return std::make_pair(iter(g.m_vertex_pred, f, l), |
289 | iter(g.m_vertex_pred, l, l)); |
290 | } |
291 | |
292 | template <typename G, typename EP, typename VP> |
293 | std::pair<typename filtered_graph<G, EP, VP>::edge_iterator, |
294 | typename filtered_graph<G, EP, VP>::edge_iterator> |
295 | edges(const filtered_graph<G, EP, VP>& g) |
296 | { |
297 | typedef filtered_graph<G, EP, VP> Graph; |
298 | typename Graph::EdgePred pred(g.m_edge_pred, g.m_vertex_pred, g); |
299 | typename graph_traits<G>::edge_iterator f, l; |
300 | boost::tie(f, l) = edges(g.m_g); |
301 | typedef typename Graph::edge_iterator iter; |
302 | return std::make_pair(iter(pred, f, l), iter(pred, l, l)); |
303 | } |
304 | |
305 | // An alternative for num_vertices() and num_edges() would be to |
306 | // count the number in the filtered graph. This is problematic |
307 | // because of the interaction with the vertex indices... they would |
308 | // no longer go from 0 to num_vertices(), which would cause trouble |
309 | // for algorithms allocating property storage in an array. We could |
310 | // try to create a mapping to new recalibrated indices, but I don't |
311 | // see an efficient way to do this. |
312 | // |
313 | // However, the current solution is still unsatisfactory because |
314 | // the following semantic constraints no longer hold: |
315 | // boost::tie(vi, viend) = vertices(g); |
316 | // assert(std::distance(vi, viend) == num_vertices(g)); |
317 | |
318 | template <typename G, typename EP, typename VP> |
319 | typename filtered_graph<G, EP, VP>::vertices_size_type |
320 | num_vertices(const filtered_graph<G, EP, VP>& g) { |
321 | return num_vertices(g.m_g); |
322 | } |
323 | |
324 | template <typename G, typename EP, typename VP> |
325 | typename filtered_graph<G, EP, VP>::edges_size_type |
326 | num_edges(const filtered_graph<G, EP, VP>& g) { |
327 | return num_edges(g.m_g); |
328 | } |
329 | |
330 | template <typename G> |
331 | typename filtered_graph_base<G>::vertex_descriptor |
332 | source(typename filtered_graph_base<G>::edge_descriptor e, |
333 | const filtered_graph_base<G>& g) |
334 | { |
335 | return source(e, g.m_g); |
336 | } |
337 | |
338 | template <typename G> |
339 | typename filtered_graph_base<G>::vertex_descriptor |
340 | target(typename filtered_graph_base<G>::edge_descriptor e, |
341 | const filtered_graph_base<G>& g) |
342 | { |
343 | return target(e, g.m_g); |
344 | } |
345 | |
346 | template <typename G, typename EP, typename VP> |
347 | std::pair<typename filtered_graph<G, EP, VP>::out_edge_iterator, |
348 | typename filtered_graph<G, EP, VP>::out_edge_iterator> |
349 | out_edges(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
350 | const filtered_graph<G, EP, VP>& g) |
351 | { |
352 | typedef filtered_graph<G, EP, VP> Graph; |
353 | typename Graph::OutEdgePred pred(g.m_edge_pred, g.m_vertex_pred, g); |
354 | typedef typename Graph::out_edge_iterator iter; |
355 | typename graph_traits<G>::out_edge_iterator f, l; |
356 | boost::tie(f, l) = out_edges(u, g.m_g); |
357 | return std::make_pair(iter(pred, f, l), iter(pred, l, l)); |
358 | } |
359 | |
360 | template <typename G, typename EP, typename VP> |
361 | typename filtered_graph<G, EP, VP>::degree_size_type |
362 | out_degree(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
363 | const filtered_graph<G, EP, VP>& g) |
364 | { |
365 | typename filtered_graph<G, EP, VP>::degree_size_type n = 0; |
366 | typename filtered_graph<G, EP, VP>::out_edge_iterator f, l; |
367 | for (boost::tie(f, l) = out_edges(u, g); f != l; ++f) |
368 | ++n; |
369 | return n; |
370 | } |
371 | |
372 | template <typename G, typename EP, typename VP> |
373 | std::pair<typename filtered_graph<G, EP, VP>::adjacency_iterator, |
374 | typename filtered_graph<G, EP, VP>::adjacency_iterator> |
375 | adjacent_vertices(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
376 | const filtered_graph<G, EP, VP>& g) |
377 | { |
378 | typedef filtered_graph<G, EP, VP> Graph; |
379 | typedef typename Graph::adjacency_iterator adjacency_iterator; |
380 | typename Graph::out_edge_iterator f, l; |
381 | boost::tie(f, l) = out_edges(u, g); |
382 | return std::make_pair(adjacency_iterator(f, const_cast<Graph*>(&g)), |
383 | adjacency_iterator(l, const_cast<Graph*>(&g))); |
384 | } |
385 | |
386 | template <typename G, typename EP, typename VP> |
387 | std::pair<typename filtered_graph<G, EP, VP>::in_edge_iterator, |
388 | typename filtered_graph<G, EP, VP>::in_edge_iterator> |
389 | in_edges(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
390 | const filtered_graph<G, EP, VP>& g) |
391 | { |
392 | typedef filtered_graph<G, EP, VP> Graph; |
393 | typename Graph::InEdgePred pred(g.m_edge_pred, g.m_vertex_pred, g); |
394 | typedef typename Graph::in_edge_iterator iter; |
395 | typename graph_traits<G>::in_edge_iterator f, l; |
396 | boost::tie(f, l) = in_edges(u, g.m_g); |
397 | return std::make_pair(iter(pred, f, l), iter(pred, l, l)); |
398 | } |
399 | |
400 | template <typename G, typename EP, typename VP> |
401 | typename filtered_graph<G, EP, VP>::degree_size_type |
402 | in_degree(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
403 | const filtered_graph<G, EP, VP>& g) |
404 | { |
405 | typename filtered_graph<G, EP, VP>::degree_size_type n = 0; |
406 | typename filtered_graph<G, EP, VP>::in_edge_iterator f, l; |
407 | for (boost::tie(f, l) = in_edges(u, g); f != l; ++f) |
408 | ++n; |
409 | return n; |
410 | } |
411 | |
412 | template <typename G, typename EP, typename VP> |
413 | std::pair<typename filtered_graph<G, EP, VP>::edge_descriptor, bool> |
414 | edge(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
415 | typename filtered_graph<G, EP, VP>::vertex_descriptor v, |
416 | const filtered_graph<G, EP, VP>& g) |
417 | { |
418 | typename graph_traits<G>::edge_descriptor e; |
419 | bool exists; |
420 | boost::tie(e, exists) = edge(u, v, g.m_g); |
421 | return std::make_pair(e, exists && g.m_edge_pred(e)); |
422 | } |
423 | |
424 | template <typename G, typename EP, typename VP> |
425 | std::pair<typename filtered_graph<G, EP, VP>::out_edge_iterator, |
426 | typename filtered_graph<G, EP, VP>::out_edge_iterator> |
427 | edge_range(typename filtered_graph<G, EP, VP>::vertex_descriptor u, |
428 | typename filtered_graph<G, EP, VP>::vertex_descriptor v, |
429 | const filtered_graph<G, EP, VP>& g) |
430 | { |
431 | typedef filtered_graph<G, EP, VP> Graph; |
432 | typename Graph::OutEdgePred pred(g.m_edge_pred, g.m_vertex_pred, g); |
433 | typedef typename Graph::out_edge_iterator iter; |
434 | typename graph_traits<G>::out_edge_iterator f, l; |
435 | boost::tie(f, l) = edge_range(u, v, g.m_g); |
436 | return std::make_pair(iter(pred, f, l), iter(pred, l, l)); |
437 | } |
438 | |
439 | |
440 | //=========================================================================== |
441 | // Property map |
442 | |
443 | template <typename G, typename EP, typename VP, typename Property> |
444 | struct property_map<filtered_graph<G, EP, VP>, Property> |
445 | : property_map<G, Property> {}; |
446 | |
447 | template <typename G, typename EP, typename VP, typename Property> |
448 | typename property_map<G, Property>::type |
449 | get(Property p, filtered_graph<G, EP, VP>& g) |
450 | { |
451 | return get(p, const_cast<G&>(g.m_g)); |
452 | } |
453 | |
454 | template <typename G, typename EP, typename VP,typename Property> |
455 | typename property_map<G, Property>::const_type |
456 | get(Property p, const filtered_graph<G, EP, VP>& g) |
457 | { |
458 | return get(p, (const G&)g.m_g); |
459 | } |
460 | |
461 | template <typename G, typename EP, typename VP, typename Property, |
462 | typename Key> |
463 | typename property_map_value<G, Property>::type |
464 | get(Property p, const filtered_graph<G, EP, VP>& g, const Key& k) |
465 | { |
466 | return get(p, (const G&)g.m_g, k); |
467 | } |
468 | |
469 | template <typename G, typename EP, typename VP, typename Property, |
470 | typename Key, typename Value> |
471 | void |
472 | put(Property p, const filtered_graph<G, EP, VP>& g, const Key& k, |
473 | const Value& val) |
474 | { |
475 | put(p, const_cast<G&>(g.m_g), k, val); |
476 | } |
477 | |
478 | //=========================================================================== |
479 | // Some filtered subgraph specializations |
480 | |
481 | template <typename Graph, typename Set> |
482 | struct vertex_subset_filter { |
483 | typedef filtered_graph<Graph, keep_all, is_in_subset<Set> > type; |
484 | }; |
485 | template <typename Graph, typename Set> |
486 | inline typename vertex_subset_filter<Graph, Set>::type |
487 | make_vertex_subset_filter(Graph& g, const Set& s) { |
488 | typedef typename vertex_subset_filter<Graph, Set>::type Filter; |
489 | is_in_subset<Set> p(s); |
490 | return Filter(g, keep_all(), p); |
491 | } |
492 | |
493 | // This is misspelled, but present for backwards compatibility; new code |
494 | // should use the version below that has the correct spelling. |
495 | template <typename Graph, typename Set> |
496 | struct vertex_subset_compliment_filter { |
497 | typedef filtered_graph<Graph, keep_all, is_not_in_subset<Set> > type; |
498 | }; |
499 | template <typename Graph, typename Set> |
500 | inline typename vertex_subset_compliment_filter<Graph, Set>::type |
501 | make_vertex_subset_compliment_filter(Graph& g, const Set& s) { |
502 | typedef typename vertex_subset_compliment_filter<Graph, Set>::type Filter; |
503 | is_not_in_subset<Set> p(s); |
504 | return Filter(g, keep_all(), p); |
505 | } |
506 | |
507 | template <typename Graph, typename Set> |
508 | struct vertex_subset_complement_filter { |
509 | typedef filtered_graph<Graph, keep_all, is_not_in_subset<Set> > type; |
510 | }; |
511 | template <typename Graph, typename Set> |
512 | inline typename vertex_subset_complement_filter<Graph, Set>::type |
513 | make_vertex_subset_complement_filter(Graph& g, const Set& s) { |
514 | typedef typename vertex_subset_complement_filter<Graph, Set>::type Filter; |
515 | is_not_in_subset<Set> p(s); |
516 | return Filter(g, keep_all(), p); |
517 | } |
518 | |
519 | // Filter that uses a property map whose value_type is a boolean |
520 | template <typename PropertyMap> |
521 | struct property_map_filter { |
522 | |
523 | property_map_filter() { } |
524 | |
525 | property_map_filter(const PropertyMap& property_map) : |
526 | m_property_map(property_map) { } |
527 | |
528 | template <typename Key> |
529 | bool operator()(const Key& key) const { |
530 | return (get(m_property_map, key)); |
531 | } |
532 | |
533 | private : |
534 | PropertyMap m_property_map; |
535 | }; |
536 | |
537 | } // namespace boost |
538 | |
539 | |
540 | #endif // BOOST_FILTERED_GRAPH_HPP |
541 | |