/* * Copyright (c) 2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * \file * \brief Adaptor that presents an undirected view of a bidirectional BGL graph. * * Analogous to the reverse_graph adapter. You can construct one of these for * bidirectional graph g with: * * auto ug = make_undirected_graph(g); * * The vertex descriptor type is the same as that of the underlying graph, but * the edge descriptor is different. */ #ifndef GRAPH_UNDIRECTED_H #define GRAPH_UNDIRECTED_H #include "util/operators.h" #include <boost/graph/adjacency_iterator.hpp> #include <boost/graph/graph_traits.hpp> #include <boost/graph/properties.hpp> #include <boost/iterator/iterator_facade.hpp> #include <type_traits> #include <utility> namespace ue2 { struct undirected_graph_tag {}; template <class BidirectionalGraph, class GraphRef> class undirected_graph; namespace undirected_detail { template <typename BidirectionalGraph> class undirected_graph_edge_descriptor : totally_ordered<undirected_graph_edge_descriptor<BidirectionalGraph>> { using base_graph_type = BidirectionalGraph; using base_graph_traits = typename boost::graph_traits<base_graph_type>; using base_edge_type = typename base_graph_traits::edge_descriptor; using base_vertex_type = typename base_graph_traits::vertex_descriptor; base_edge_type underlying_edge; const base_graph_type *g = nullptr; bool reverse = false; // if true, reverse vertices in source() and target() inline std::pair<base_vertex_type, base_vertex_type> canonical_edge() const { auto u = std::min(source(underlying_edge, *g), target(underlying_edge, *g)); auto v = std::max(source(underlying_edge, *g), target(underlying_edge, *g)); return std::make_pair(u, v); } template <class BidiGraph, class GraphRef> friend class ::ue2::undirected_graph; public: undirected_graph_edge_descriptor() = default; undirected_graph_edge_descriptor(base_edge_type edge, const base_graph_type &g_in, bool reverse_in) : underlying_edge(std::move(edge)), g(&g_in), reverse(reverse_in) {} bool operator==(const undirected_graph_edge_descriptor &other) const { return canonical_edge() == other.canonical_edge(); } bool operator<(const undirected_graph_edge_descriptor &other) const { return canonical_edge() < other.canonical_edge(); } base_vertex_type get_source() const { return reverse ? target(underlying_edge, *g) : source(underlying_edge, *g); } base_vertex_type get_target() const { return reverse ? source(underlying_edge, *g) : target(underlying_edge, *g); } }; } // namespace undirected_detail template <class BidirectionalGraph, class GraphRef = const BidirectionalGraph &> class undirected_graph { private: using Self = undirected_graph<BidirectionalGraph, GraphRef>; using Traits = boost::graph_traits<BidirectionalGraph>; public: using base_type = BidirectionalGraph; using base_ref_type = GraphRef; explicit undirected_graph(GraphRef g_in) : g(g_in) {} // Graph requirements using vertex_descriptor = typename Traits::vertex_descriptor; using edge_descriptor = undirected_detail::undirected_graph_edge_descriptor<base_type>; using directed_category = boost::undirected_tag; using edge_parallel_category = boost::disallow_parallel_edge_tag; using traversal_category = typename Traits::traversal_category; // IncidenceGraph requirements /** * \brief Templated iterator used for out_edge_iterator and * in_edge_iterator, depending on the value of Reverse. */ template <bool Reverse> class adj_edge_iterator : public boost::iterator_facade< adj_edge_iterator<Reverse>, edge_descriptor, boost::forward_traversal_tag, edge_descriptor> { vertex_descriptor u; const base_type *g; typename Traits::in_edge_iterator in_it; typename Traits::out_edge_iterator out_it; bool done_in = false; public: adj_edge_iterator() = default; adj_edge_iterator(vertex_descriptor u_in, const base_type &g_in, bool end_iter) : u(std::move(u_in)), g(&g_in) { auto pi = in_edges(u, *g); auto po = out_edges(u, *g); if (end_iter) { in_it = pi.second; out_it = po.second; done_in = true; } else { in_it = pi.first; out_it = po.first; if (in_it == pi.second) { done_in = true; find_first_valid_out(); } } } private: friend class boost::iterator_core_access; void find_first_valid_out() { auto out_end = out_edges(u, *g).second; for (; out_it != out_end; ++out_it) { auto v = target(*out_it, *g); if (!edge(v, u, *g).second) { break; } } } void increment() { if (!done_in) { auto in_end = in_edges(u, *g).second; assert(in_it != in_end); ++in_it; if (in_it == in_end) { done_in = true; find_first_valid_out(); } } else { ++out_it; find_first_valid_out(); } } bool equal(const adj_edge_iterator &other) const { return in_it == other.in_it && out_it == other.out_it; } edge_descriptor dereference() const { if (done_in) { return edge_descriptor(*out_it, *g, Reverse); } else { return edge_descriptor(*in_it, *g, !Reverse); } } }; using out_edge_iterator = adj_edge_iterator<false>; using in_edge_iterator = adj_edge_iterator<true>; using degree_size_type = typename Traits::degree_size_type; // AdjacencyGraph requirements using adjacency_iterator = typename boost::adjacency_iterator_generator<Self, vertex_descriptor, out_edge_iterator>::type; using inv_adjacency_iterator = typename boost::inv_adjacency_iterator_generator< Self, vertex_descriptor, in_edge_iterator>::type; // VertexListGraph requirements using vertex_iterator = typename Traits::vertex_iterator; // EdgeListGraph requirements enum { is_edge_list = std::is_convertible<traversal_category, boost::edge_list_graph_tag>::value }; /** \brief Iterator used for edges(). */ class edge_iterator : public boost::iterator_facade<edge_iterator, edge_descriptor, boost::forward_traversal_tag, edge_descriptor> { const base_type *g; typename Traits::edge_iterator it; public: edge_iterator() = default; edge_iterator(typename Traits::edge_iterator it_in, const base_type &g_in) : g(&g_in), it(std::move(it_in)) { find_first_valid_edge(); } private: friend class boost::iterator_core_access; void find_first_valid_edge() { const auto end = edges(*g).second; for (; it != end; ++it) { const auto &u = source(*it, *g); const auto &v = target(*it, *g); if (!edge(v, u, *g).second) { break; // No reverse edge, we must visit this one } if (u <= v) { // We have a reverse edge, but we'll return this one (and // skip the other). Note that (u, u) shouldn't be skipped. break; } } } void increment() { assert(it != edges(*g).second); ++it; find_first_valid_edge(); } bool equal(const edge_iterator &other) const { return it == other.it; } edge_descriptor dereference() const { return edge_descriptor(*it, *g, false); } }; using vertices_size_type = typename Traits::vertices_size_type; using edges_size_type = typename Traits::edges_size_type; using graph_tag = undirected_graph_tag; using vertex_bundle_type = typename boost::vertex_bundle_type<base_type>::type; using edge_bundle_type = typename boost::edge_bundle_type<base_type>::type; vertex_bundle_type &operator[](const vertex_descriptor &d) { return const_cast<base_type &>(g)[d]; } const vertex_bundle_type &operator[](const vertex_descriptor &d) const { return g[d]; } edge_bundle_type &operator[](const edge_descriptor &d) { return const_cast<base_type &>(g)[d.underlying_edge]; } const edge_bundle_type &operator[](const edge_descriptor &d) const { return g[d.underlying_edge]; } static vertex_descriptor null_vertex() { return Traits::null_vertex(); } // Accessor free functions follow friend std::pair<vertex_iterator, vertex_iterator> vertices(const undirected_graph &ug) { return vertices(ug.g); } friend std::pair<edge_iterator, edge_iterator> edges(const undirected_graph &ug) { auto e = edges(ug.g); return std::make_pair(edge_iterator(e.first, ug.g), edge_iterator(e.second, ug.g)); } friend std::pair<out_edge_iterator, out_edge_iterator> out_edges(const vertex_descriptor &u, const undirected_graph &ug) { return std::make_pair(out_edge_iterator(u, ug.g, false), out_edge_iterator(u, ug.g, true)); } friend vertices_size_type num_vertices(const undirected_graph &ug) { return num_vertices(ug.g); } friend edges_size_type num_edges(const undirected_graph &ug) { auto p = edges(ug); return std::distance(p.first, p.second); } friend degree_size_type out_degree(const vertex_descriptor &u, const undirected_graph &ug) { return degree(u, ug); } friend vertex_descriptor vertex(vertices_size_type n, const undirected_graph &ug) { return vertex(n, ug.g); } friend std::pair<edge_descriptor, bool> edge(const vertex_descriptor &u, const vertex_descriptor &v, const undirected_graph &ug) { auto e = edge(u, v, ug.g); if (e.second) { return std::make_pair(edge_descriptor(e.first, ug.g, false), true); } auto e_rev = edge(v, u, ug.g); if (e_rev.second) { return std::make_pair(edge_descriptor(e_rev.first, ug.g, true), true); } return std::make_pair(edge_descriptor(), false); } friend std::pair<in_edge_iterator, in_edge_iterator> in_edges(const vertex_descriptor &v, const undirected_graph &ug) { return std::make_pair(in_edge_iterator(v, ug.g, false), in_edge_iterator(v, ug.g, true)); } friend std::pair<adjacency_iterator, adjacency_iterator> adjacent_vertices(const vertex_descriptor &u, const undirected_graph &ug) { out_edge_iterator oi, oe; std::tie(oi, oe) = out_edges(u, ug); return std::make_pair(adjacency_iterator(oi, &ug), adjacency_iterator(oe, &ug)); } friend std::pair<inv_adjacency_iterator, inv_adjacency_iterator> inv_adjacent_vertices(const vertex_descriptor &v, const undirected_graph &ug) { in_edge_iterator ei, ee; std::tie(ei, ee) = in_edges(v, ug); return std::make_pair(inv_adjacency_iterator(ei, &ug), inv_adjacency_iterator(ee, &ug)); } friend degree_size_type in_degree(const vertex_descriptor &v, const undirected_graph &ug) { return degree(v, ug); } friend vertex_descriptor source(const edge_descriptor &e, const undirected_graph &) { return e.get_source(); } friend vertex_descriptor target(const edge_descriptor &e, const undirected_graph &) { return e.get_target(); } friend degree_size_type degree(const vertex_descriptor &u, const undirected_graph &ug) { auto p = out_edges(u, ug); return std::distance(p.first, p.second); } // Property accessors. template <typename Property> using prop_map = typename boost::property_map<undirected_graph, Property>; template <typename Property> friend typename prop_map<Property>::type get(Property p, undirected_graph &ug) { return get(p, ug.g); } template <typename Property> friend typename prop_map<Property>::const_type get(Property p, const undirected_graph &ug) { return get(p, ug.g); } template <typename Property, typename Key> friend typename boost::property_traits< typename prop_map<Property>::const_type>::value_type get(Property p, const undirected_graph &ug, const Key &k) { return get(p, ug.g, get_underlying_descriptor(k)); } template <typename Property, typename Value, typename Key> friend void put(Property p, const undirected_graph &ug, const Key &k, const Value &val) { put(p, const_cast<BidirectionalGraph &>(ug.g), get_underlying_descriptor(k), val); } private: // Accessors are here because our free friend functions (above) cannot see // edge_descriptor's private members. static typename base_type::vertex_descriptor get_underlying_descriptor(const vertex_descriptor &v) { return v; } static typename base_type::edge_descriptor get_underlying_descriptor(const edge_descriptor &e) { return e.underlying_edge; } // Reference to underlying bidirectional graph GraphRef g; }; template <class BidirectionalGraph> undirected_graph<BidirectionalGraph> make_undirected_graph(const BidirectionalGraph &g) { return undirected_graph<BidirectionalGraph>(g); } } // namespace ue2 namespace boost { /* Derive all the property map specializations from the underlying * bidirectional graph. */ template <typename BidirectionalGraph, typename GraphRef, typename Property> struct property_map<ue2::undirected_graph<BidirectionalGraph, GraphRef>, Property> { using base_map_type = property_map<BidirectionalGraph, Property>; using type = typename base_map_type::type; using const_type = typename base_map_type::const_type; }; template <class BidirectionalGraph, class GraphRef> struct vertex_property_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : vertex_property_type<BidirectionalGraph> {}; template <class BidirectionalGraph, class GraphRef> struct edge_property_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : edge_property_type<BidirectionalGraph> {}; template <class BidirectionalGraph, class GraphRef> struct graph_property_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : graph_property_type<BidirectionalGraph> {}; template <typename BidirectionalGraph, typename GraphRef> struct vertex_bundle_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : vertex_bundle_type<BidirectionalGraph> {}; template <typename BidirectionalGraph, typename GraphRef> struct edge_bundle_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : edge_bundle_type<BidirectionalGraph> {}; template <typename BidirectionalGraph, typename GraphRef> struct graph_bundle_type<ue2::undirected_graph<BidirectionalGraph, GraphRef>> : graph_bundle_type<BidirectionalGraph> {}; } // namespace boost #endif // GRAPH_UNDIRECTED_H