/* * Copyright (c) 2016-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. */ #ifndef UE2_GRAPH_H #define UE2_GRAPH_H #include "ue2common.h" #include "util/graph_range.h" #include "util/noncopyable.h" #include "util/operators.h" #include /* vertex_index_t, ... */ #include /* no_property */ #include #include #include #include #include /* hash */ #include /* tie */ #include /* is_same, etc */ #include /* pair, declval */ /* * Basic design of ue2_graph: * * Fairly standard adjacency list type graph structure. The main internal * structures are vertex_node and edge_node. * * Each vertex_node maintains lists of incoming and outgoing edge_nodes, a * serial number and the vertex properties. * * Each edge_node contains pointers to the source and target vertex as well as * the serial number and edge properties. * * Every time an edge_node or vertex_node is created in the graph, it is given a * unique serial number by increasing a private counter in the graph. * * The main thing to note is that the in and out edge lists are intrusive lists * with the edge_node containing the necessary hooks. This means that we can * easily convert the edge_node to iterators of the in_edge_list and * out_edge_list and remove them from the lists. * * vertex_descriptor and edge_descriptor structures both just wrap pointers to * the relevant node structure along with the serial number. operator<() for the * descriptors is overridden to look at the serial member of the node. * We do not use: * - the address of the node structure as this would lead to an unstable * ordering of vertices between runs. * - the index field as this would mean that the generation of new index * values (during say renumbering of vertex nodes after removing some * vertices) would potentially reorder vertices and corrupt containers * such as std::set<>. * The serial number is copied into the descriptors so that we can still have * descriptors in a container (such as set or unordered_set) after removing the * underlying node. * * Hashing of descriptors is based on the serial field for similar reasons. * * * * Main differences from boost::adjacency_list<> with listS: * * (1) Deterministic ordering for vertices and edges * boost::adjacency_list<> uses pointer ordering for vertex_descriptors. As * a result, ordering of vertices and edges between runs is * non-deterministic unless containers, etc use custom comparators. * * (2) Proper types for descriptors, etc. * No more void * for vertex_descriptors and trying to use it for the wrong * graph type. * * (3) Constant time num_edges(), num_vertices(), degree(), in_degree() and * out_degree() * std::list is meant to have constant time in C++11 ::size(), but this is * not always implemented as people want to keep ABI compatibility with * existing C++98 standard libraries (gcc 4.8). As ue2_graph_h uses * intrusive lists rather than std::list this is not an issue for us. * * (4) Constant time remove_edge(e, g) * ue2_graph uses boost::intrusive_lists internally so we can easily unlink * an edge from the in and out edgelist of its source and target. * * (5) More efficient edge(u, v, g) and remove_edge(u, v, g) * ue2_graph will check which of u and v has the smallest relevant degree * and use that to search for the edge(s). * * (6) Automatically populate the index field of vertex and edge bundles. * Saves us from doing it manually. Naturally there is nothing to prevent * the user from stuffing up the index properties later. * * (7) Different edge iteration order * ue2_graph does not maintain an explicit global edge list, so the * edge_iterator is constructed out of vertex_iterator and * out_edge_iterators by iterating the out_edges of each vertices. This * means that edge iteration order is not insertion order like for * adjacency_list. * * (8) null_edge() * Because why not? * * (9) vertex and edge properties must have an index field. * We generally need them so the effort has not been put into specialising * for when they are not present. * * * * Possible Future Work: * * (1) Improve edge(u, v, g) performance * This function sees a fair amount of use and is O(n) in the smallest of * the source out_degree or target in_degree. This could be improved by * changes on of the edge containers to be something similar to a multiset. * * (2) 'Lie' about the number of edges / vertices * * One of the main uses of num_edges() and num_vertices() is to allocate a * vector, etc so that it can be indexed by edge or vertex index. If * num_edges() and num_vertices() returned the appropriate size for such a * vector (at least one more than the largest index), we would be able to * avoid some renumbering operations. Functions would have to be provided to * get the real number of vertices and edges. Having num_vertices() and * num_edges() return an over-estimate is not without precedence in the BGL * - the filtered_graph adaptor does the same thing and is compatible with * various (all?) BGL algorithms. It is not clear that this was done * deliberately for the same reason or because it is difficult for * filtered_graph to get the true counts. * * (3) Investigate slab/pooled allocation schemes for nodes. */ namespace ue2 { namespace graph_detail { class graph_base : noncopyable { }; struct default_edge_property { size_t index; }; struct default_vertex_property { size_t index; }; template class vertex_descriptor : totally_ordered> { using vertex_node = typename Graph::vertex_node; public: vertex_descriptor() : p(nullptr), serial(0) {} explicit vertex_descriptor(vertex_node *pp) : p(pp), serial(pp->serial) {} explicit operator bool() const { return p; } bool operator<(const vertex_descriptor b) const { if (p && b.p) { /* no vertices in the same graph can have the same serial */ assert(p == b.p || serial != b.serial); return serial < b.serial; } else { return p < b.p; } } bool operator==(const vertex_descriptor b) const { return p == b.p; } size_t hash() const { return std::hash()(serial); } private: vertex_node *raw(void) { return p; } vertex_node *p; u64a serial; friend Graph; }; template class edge_descriptor : totally_ordered> { using edge_node = typename Graph::edge_node; public: edge_descriptor() : p(nullptr), serial(0) {} explicit edge_descriptor(edge_node *pp) : p(pp), serial(pp->serial) {} /* Convenience ctor to allow us to directly get an edge_descriptor from * edge() and add_edge(). As we have null_edges and we always allow * parallel edges, the bool component of the return from these functions is * not required. */ explicit edge_descriptor(const std::pair &tup) : p(tup.first.p), serial(tup.first.serial) { assert(tup.second == (bool)tup.first); } operator bool() const { return p; } bool operator<(const edge_descriptor b) const { if (p && b.p) { /* no edges in the same graph can have the same serial */ assert(p == b.p || serial != b.serial); return serial < b.serial; } else { return p < b.p; } } bool operator==(const edge_descriptor b) const { return p == b.p; } size_t hash() const { return std::hash()(serial); } private: edge_node *raw(void) { return p; } edge_node *p; u64a serial; friend Graph; }; } // namespace graph_detail template class ue2_graph : graph_detail::graph_base { private: struct in_edge_tag { }; struct out_edge_tag { }; struct vertex_node; using out_edge_hook = boost::intrusive::list_base_hook >; /* in_edge_hook does not use safe mode as during graph destruction we do not * maintain the in edge lists */ using in_edge_hook = boost::intrusive::list_base_hook, boost::intrusive::link_mode >; struct edge_node : public out_edge_hook, public in_edge_hook { explicit edge_node(u64a serial_in) : serial(serial_in) { } vertex_node *source = nullptr; vertex_node *target = nullptr; const u64a serial; /*< used to order edges. We do not use props.index so * that there is no danger of invalidating sets or * other containers by changing the index due to * renumbering */ EdgePropertyType props; }; template using vertex_edge_list = boost::intrusive::list >; struct vertex_node : public boost::intrusive::list_base_hook<> { explicit vertex_node(u64a serial_in) : serial(serial_in) { } VertexPropertyType props; const u64a serial; /*< used to order vertices. We do not use props.index * so that there is no danger of invalidating sets or * other containers by changing the index due to * renumbering */ /* The incoming edges are not considered owned by the vertex */ vertex_edge_list in_edge_list; /* The out going edges are considered owned by the vertex and * need to be freed when the graph is being destroyed */ vertex_edge_list out_edge_list; /* The destructor only frees memory owned by the vertex and will leave * the neighbour's edges in a bad state. If a vertex is being removed * (rather than the graph being destroyed), then the more gentle clean * up of clear_vertex() is required to be called first */ ~vertex_node() { out_edge_list.clear_and_dispose(delete_disposer()); } }; struct delete_disposer { template void operator()(const T *d) const { delete d; } }; struct in_edge_disposer { void operator()(edge_node *e) const { /* remove from source's out edge list before deleting */ vertex_node *u = e->source; u->out_edge_list.erase(u->out_edge_list.iterator_to(*e)); delete e; } }; struct out_edge_disposer { void operator()(edge_node *e) const { /* remove from target's in edge list before deleting */ vertex_node *v = e->target; v->in_edge_list.erase(v->in_edge_list.iterator_to(*e)); delete e; } }; using vertices_list_type = boost::intrusive::list > >; vertices_list_type vertices_list; protected: /* to allow renumbering */ static const size_t N_SPECIAL_VERTICES = 0; /* override in derived class */ size_t next_vertex_index = 0; size_t next_edge_index = 0; private: size_t graph_edge_count = 0; /* maintained explicitly as we have no global edge list */ u64a next_serial = 0; u64a new_serial() { u64a serial = next_serial++; if (!next_serial) { /* if we have created enough graph edges/vertices to overflow a u64a * we must have spent close to an eternity adding to this graph so * something must have gone very wrong and we will not be producing * a final bytecode in a reasonable amount of time. Or, more likely, * the next_serial value has become corrupt. */ throw std::overflow_error("too many graph edges/vertices created"); } return serial; } public: using vertex_descriptor = graph_detail::vertex_descriptor; using edge_descriptor = graph_detail::edge_descriptor; friend vertex_descriptor; friend edge_descriptor; using vertices_size_type = typename vertices_list_type::size_type; using degree_size_type = typename vertex_edge_list::size_type; using edges_size_type = size_t; using vertex_property_type = VertexPropertyType; using edge_property_type = EdgePropertyType; using graph_bundled = boost::no_property; using vertex_bundled = VertexPropertyType; using edge_bundled = EdgePropertyType; private: /* Note: apparently, nested class templates cannot be fully specialised but * they can be partially specialised. Sigh, ... */ template struct bundle_key_type { }; template struct bundle_key_type { using type = vertex_descriptor; }; template struct bundle_key_type { using type = edge_descriptor; }; public: class out_edge_iterator : public boost::iterator_adaptor< out_edge_iterator, typename vertex_edge_list::const_iterator, edge_descriptor, boost::bidirectional_traversal_tag, edge_descriptor> { using super = typename out_edge_iterator::iterator_adaptor_; public: out_edge_iterator() : super() { } explicit out_edge_iterator( typename vertex_edge_list::const_iterator it) : super(it) { } edge_descriptor dereference() const { /* :( const_cast makes me sad but constness is defined by the graph * parameter of bgl api calls */ return edge_descriptor(const_cast(&*super::base())); } }; class in_edge_iterator : public boost::iterator_adaptor< in_edge_iterator, typename vertex_edge_list::const_iterator, edge_descriptor, boost::bidirectional_traversal_tag, edge_descriptor> { using super = typename in_edge_iterator::iterator_adaptor_; public: in_edge_iterator() : super() { } explicit in_edge_iterator( typename vertex_edge_list::const_iterator it) : super(it) { } edge_descriptor dereference() const { /* :( const_cast makes me sad but constness is defined by the graph * parameter of bgl api calls */ return edge_descriptor(const_cast(&*super::base())); } }; class adjacency_iterator : public boost::iterator_adaptor< adjacency_iterator, out_edge_iterator, vertex_descriptor, boost::bidirectional_traversal_tag, vertex_descriptor> { using super = typename adjacency_iterator::iterator_adaptor_; public: explicit adjacency_iterator(out_edge_iterator a) : super(std::move(a)) { } adjacency_iterator() { } vertex_descriptor dereference() const { return vertex_descriptor(super::base()->p->target); } }; class inv_adjacency_iterator : public boost::iterator_adaptor< inv_adjacency_iterator, in_edge_iterator, vertex_descriptor, boost::bidirectional_traversal_tag, vertex_descriptor> { using super = typename inv_adjacency_iterator::iterator_adaptor_; public: explicit inv_adjacency_iterator(in_edge_iterator a) : super(std::move(a)) { } inv_adjacency_iterator() { } vertex_descriptor dereference() const { return vertex_descriptor(super::base()->p->source); } }; class vertex_iterator : public boost::iterator_adaptor< vertex_iterator, typename vertices_list_type::const_iterator, vertex_descriptor, boost::bidirectional_traversal_tag, vertex_descriptor> { using super = typename vertex_iterator::iterator_adaptor_; public: vertex_iterator() : super() { } explicit vertex_iterator(typename vertices_list_type::const_iterator it) : super(it) { } vertex_descriptor dereference() const { /* :( const_cast makes me sad but constness is defined by the graph * parameter of bgl api calls */ return vertex_descriptor( const_cast(&*super::base())); } }; class edge_iterator : public boost::iterator_facade< edge_iterator, edge_descriptor, boost::forward_traversal_tag, /* TODO: make bidi */ edge_descriptor> { public: using main_base_iter_type = vertex_iterator; using aux_base_iter_type = out_edge_iterator; edge_iterator(main_base_iter_type b, main_base_iter_type e) : main(std::move(b)), main_end(std::move(e)) { if (main == main_end) { return; } std::tie(aux, aux_end) = out_edges_impl(*main); while (aux == aux_end) { ++main; if (main == main_end) { break; } std::tie(aux, aux_end) = out_edges_impl(*main); } } edge_iterator() { } friend class boost::iterator_core_access; void increment() { ++aux; while (aux == aux_end) { ++main; if (main == main_end) { break; } std::tie(aux, aux_end) = out_edges_impl(*main); } } bool equal(const edge_iterator &other) const { return main == other.main && (main == main_end || aux == other.aux); } edge_descriptor dereference() const { return *aux; } main_base_iter_type main; main_base_iter_type main_end; aux_base_iter_type aux; aux_base_iter_type aux_end; }; public: static vertex_descriptor null_vertex() { return vertex_descriptor(); } vertex_descriptor add_vertex_impl() { vertex_node *v = new vertex_node(new_serial()); v->props.index = next_vertex_index++; vertices_list.push_back(*v); return vertex_descriptor(v); } void remove_vertex_impl(vertex_descriptor v) { vertex_node *vv = v.raw(); assert(vv->in_edge_list.empty()); assert(vv->out_edge_list.empty()); vertices_list.erase_and_dispose(vertices_list.iterator_to(*vv), delete_disposer()); } void clear_in_edges_impl(vertex_descriptor v) { graph_edge_count -= v.raw()->in_edge_list.size(); v.raw()->in_edge_list.clear_and_dispose(in_edge_disposer()); } void clear_out_edges_impl(vertex_descriptor v) { graph_edge_count -= v.raw()->out_edge_list.size(); v.raw()->out_edge_list.clear_and_dispose(out_edge_disposer()); } /* IncidenceGraph concept functions */ static vertex_descriptor source_impl(edge_descriptor e) { return vertex_descriptor(e.raw()->source); } static vertex_descriptor target_impl(edge_descriptor e) { return vertex_descriptor(e.raw()->target); } static degree_size_type out_degree_impl(vertex_descriptor v) { return v.raw()->out_edge_list.size(); } static std::pair out_edges_impl(vertex_descriptor v) { return {out_edge_iterator(v.raw()->out_edge_list.begin()), out_edge_iterator(v.raw()->out_edge_list.end())}; } /* BidirectionalGraph concept functions */ static degree_size_type in_degree_impl(vertex_descriptor v) { return v.raw()->in_edge_list.size(); } static std::pair in_edges_impl(vertex_descriptor v) { return {in_edge_iterator(v.raw()->in_edge_list.begin()), in_edge_iterator(v.raw()->in_edge_list.end())}; } /* Note: this is defined so that self loops are counted twice - which may or * may not be what you want. Actually, you probably don't want this at * all. */ static degree_size_type degree_impl(vertex_descriptor v) { return in_degree_impl(v) + out_degree_impl(v); } /* AdjacencyList concept functions */ static std::pair adjacent_vertices_impl(vertex_descriptor v) { auto out_edge_its = out_edges_impl(v); return {adjacency_iterator(out_edge_its.first), adjacency_iterator(out_edge_its.second)}; } /* AdjacencyMatrix concept functions * (Note: complexity guarantee is not met) */ std::pair edge_impl(vertex_descriptor u, vertex_descriptor v) const { if (in_degree_impl(v) < out_degree_impl(u)) { for (const edge_descriptor &e : in_edges_range(v, *this)) { // cppcheck-suppress useStlAlgorithm if (source_impl(e) == u) { return {e, true}; } } } else { for (const edge_descriptor &e : out_edges_range(u, *this)) { // cppcheck-suppress useStlAlgorithm if (target_impl(e) == v) { return {e, true}; } } } return {edge_descriptor(), false}; } /* Misc functions that don't actually seem to belong to a formal BGL concept. */ static edge_descriptor null_edge() { return edge_descriptor(); } static std::pair inv_adjacent_vertices_impl(vertex_descriptor v) { auto in_edge_its = in_edges_impl(v); return {inv_adjacency_iterator(in_edge_its.first), inv_adjacency_iterator(in_edge_its.second)}; } /* MutableGraph concept functions */ std::pair add_edge_impl(vertex_descriptor u, vertex_descriptor v) { bool added = true; /* we always allow parallel edges */ edge_node *e = new edge_node(new_serial()); e->source = u.raw(); e->target = v.raw(); e->props.index = next_edge_index++; u.raw()->out_edge_list.push_back(*e); v.raw()->in_edge_list.push_back(*e); graph_edge_count++; return {edge_descriptor(e), added}; } void remove_edge_impl(edge_descriptor e) { graph_edge_count--; vertex_node *u = e.raw()->source; vertex_node *v = e.raw()->target; v->in_edge_list.erase(v->in_edge_list.iterator_to(*e.raw())); u->out_edge_list.erase(u->out_edge_list.iterator_to(*e.raw())); delete e.raw(); } template void remove_out_edge_if_impl(vertex_descriptor v, Predicate pred) { out_edge_iterator it, ite; std::tie(it, ite) = out_edges_impl(v); while (it != ite) { auto jt = it; ++it; if (pred(*jt)) { this->remove_edge_impl(*jt); } } } template void remove_in_edge_if_impl(vertex_descriptor v, Predicate pred) { in_edge_iterator it, ite; std::tie(it, ite) = in_edges_impl(v); while (it != ite) { auto jt = it; ++it; if (pred(*jt)) { remove_edge_impl(*jt); } } } template void remove_edge_if_impl(Predicate pred) { edge_iterator it, ite; std::tie(it, ite) = edges_impl(); while (it != ite) { auto jt = it; ++it; if (pred(*jt)) { remove_edge_impl(*jt); } } } private: /* GCC 4.8 has bugs with lambdas in templated friend functions, so: */ struct source_match { explicit source_match(const vertex_descriptor &uu) : u(uu) { } bool operator()(edge_descriptor e) const { return source_impl(e) == u; } const vertex_descriptor &u; }; struct target_match { explicit target_match(const vertex_descriptor &vv) : v(vv) { } bool operator()(edge_descriptor e) const { return target_impl(e) == v; } const vertex_descriptor &v; }; public: /* Note: (u,v) variant needs to remove all (parallel) edges between (u,v). * * The edge_descriptor version should be strongly preferred if the * edge_descriptor is available. */ void remove_edge_impl(const vertex_descriptor &u, const vertex_descriptor &v) { if (in_degree_impl(v) < out_degree_impl(u)) { remove_in_edge_if_impl(v, source_match(u)); } else { remove_out_edge_if_impl(u, target_match(v)); } } /* VertexListGraph concept functions */ vertices_size_type num_vertices_impl() const { return vertices_list.size(); } std::pair vertices_impl() const { return {vertex_iterator(vertices_list.begin()), vertex_iterator(vertices_list.end())}; } /* EdgeListGraph concept functions (aside from those in IncidenceGraph) */ edges_size_type num_edges_impl() const { return graph_edge_count; } std::pair edges_impl() const { vertex_iterator vi, ve; std::tie(vi, ve) = vertices_impl(); return {edge_iterator(vi, ve), edge_iterator(ve, ve)}; } /* bundled properties functions */ vertex_property_type &operator[](vertex_descriptor v) { return v.raw()->props; } const vertex_property_type &operator[](vertex_descriptor v) const { return v.raw()->props; } edge_property_type &operator[](edge_descriptor e) { return e.raw()->props; } const edge_property_type &operator[](edge_descriptor e) const { return e.raw()->props; } /* PropertyGraph concept functions & helpers */ template struct prop_map : public boost::put_get_helper > { using value_type = typename std::decay::type; using reference = R; using key_type = typename bundle_key_type::type; typedef typename boost::lvalue_property_map_tag category; explicit prop_map(value_type P_of::*m_in) : member(m_in) { } reference operator[](key_type k) const { return k.raw()->props.*member; //NOLINT (clang-analyzer-core.uninitialized.UndefReturn) } reference operator()(key_type k) const { return (*this)[k]; } private: value_type P_of::*member; }; template struct prop_map_all : public boost::put_get_helper > { using value_type = typename std::decay::type; using reference = R; using key_type = typename bundle_key_type::type; typedef typename boost::lvalue_property_map_tag category; reference operator[](key_type k) const { return k.raw()->props; } reference operator()(key_type k) const { return (*this)[k]; } }; template friend prop_map get(P_type P_of::*t, Graph &) { return prop_map(t); } template friend prop_map get(P_type P_of::*t, const Graph &) { return prop_map(t); } /* We can't seem to use auto/decltype returns here as it seems that the * templated member functions are not yet visible when the compile is * evaluating the decltype for the return value. We could probably work * around it by making this a dummy templated function. */ friend prop_map get(boost::vertex_index_t, Graph &g) { return get(&VertexPropertyType::index, g); } friend prop_map get(boost::vertex_index_t, const Graph &g) { return get(&VertexPropertyType::index, g); } friend prop_map get(boost::edge_index_t, Graph &g) { return get(&EdgePropertyType::index, g); } friend prop_map get(boost::edge_index_t, const Graph &g) { return get(&EdgePropertyType::index, g); } friend prop_map_all get(boost::vertex_all_t, Graph &) { return {}; } friend prop_map_all get(boost::vertex_all_t, const Graph &) { return {}; } friend prop_map_all get(boost::edge_all_t, Graph &) { return {}; } friend prop_map_all get(boost::edge_all_t, const Graph &) { return {}; } friend prop_map_all get(boost::vertex_bundle_t, Graph &) { return {}; } friend prop_map_all get(boost::vertex_bundle_t, const Graph &) { return {}; } friend prop_map_all get(boost::edge_bundle_t, Graph &) { return {}; } friend prop_map_all get(boost::edge_bundle_t, const Graph &) { return {}; } template friend auto get(Prop p, Graph &g, K key) -> decltype(get(p, g)[key]) { return get(p, g)[key]; } template friend auto get(Prop p, const Graph &g, K key) -> decltype(get(p, g)[key]) { return get(p, g)[key]; } template friend void put(Prop p, Graph &g, K key, const V &value) { get(p, g)[key] = value; } /* MutablePropertyGraph concept functions */ /* Note: add_vertex(g, vp) allocates a next index value for the vertex * rather than using the index in vp. i.e., except for in rare coincidences: * g[add_vertex(g, vp)].index != vp.index */ vertex_descriptor add_vertex_impl(const VertexPropertyType &vp) { vertex_descriptor v = add_vertex_impl(); auto i = (*this)[v].index; (*this)[v] = vp; (*this)[v].index = i; return v; } /* Note: add_edge(u, v, g, vp) allocates a next index value for the edge * rather than using the index in ep. i.e., except for in rare coincidences: * g[add_edge(u, v, g, ep)].index != ep.index */ std::pair add_edge_impl(vertex_descriptor u, vertex_descriptor v, const EdgePropertyType &ep) { auto e = add_edge_impl(u, v); auto i = (*this)[e.first].index; (*this)[e.first] = ep; (*this)[e.first].index = i; return e; } /* End MutablePropertyGraph */ /** Pack the edge index into a contiguous range [ 0, num_edges(g) ). */ void renumber_edges_impl() { next_edge_index = 0; edge_iterator it; edge_iterator ite; for (std::tie(it, ite) = edges_impl(); it != ite; ++it) { (*this)[*it].index = next_edge_index++; } } /** Pack the vertex index into a contiguous range [ 0, num_vertices(g) ). * Vertices with indices less than N_SPECIAL_VERTICES are not renumbered. */ void renumber_vertices_impl() { DEBUG_PRINTF("renumbering above %zu\n", Graph::N_SPECIAL_VERTICES); next_vertex_index = Graph::N_SPECIAL_VERTICES; vertex_iterator it; vertex_iterator ite; for (std::tie(it, ite) = vertices_impl(); it != ite; ++it) { if ((*this)[*it].index < Graph::N_SPECIAL_VERTICES) { continue; } (*this)[*it].index = next_vertex_index++; } } /** Returns what the next allocated vertex index will be. This is an upper * on the values of index for vertices (vertex removal means that there may * be gaps). */ vertices_size_type vertex_index_upper_bound_impl() const { return next_vertex_index; } /** Returns what the next allocated edge index will be. This is an upper on * the values of index for edges (edge removal means that there may be * gaps). */ vertices_size_type edge_index_upper_bound_impl() const { return next_edge_index; } using directed_category = boost::directed_tag; using edge_parallel_category = boost::allow_parallel_edge_tag; struct traversal_category : public virtual boost::bidirectional_graph_tag, public virtual boost::adjacency_graph_tag, public virtual boost::vertex_list_graph_tag, public virtual boost::edge_list_graph_tag { }; ue2_graph() = default; ue2_graph(ue2_graph &&old) : next_vertex_index(old.next_vertex_index), next_edge_index(old.next_edge_index), graph_edge_count(old.graph_edge_count), next_serial(old.next_serial) { using std::swap; swap(vertices_list, old.vertices_list); } ue2_graph &operator=(ue2_graph &&old) { next_vertex_index = old.next_vertex_index; next_edge_index = old.next_edge_index; graph_edge_count = old.graph_edge_count; next_serial = old.next_serial; using std::swap; swap(vertices_list, old.vertices_list); return *this; } ~ue2_graph() { vertices_list.clear_and_dispose(delete_disposer()); } }; /** \brief Type trait to enable on whether the Graph is an ue2_graph. */ template struct is_ue2_graph : public ::std::integral_constant< bool, std::is_base_of::value> {}; template typename std::enable_if::value, typename Graph::vertex_descriptor>::type add_vertex(Graph &g) { return g.add_vertex_impl(); } template typename std::enable_if::value>::type remove_vertex(typename Graph::vertex_descriptor v, Graph &g) { g.remove_vertex_impl(v); } template typename std::enable_if::value>::type clear_in_edges(typename Graph::vertex_descriptor v, Graph &g) { g.clear_in_edges_impl(v); } template typename std::enable_if::value>::type clear_out_edges(typename Graph::vertex_descriptor v, Graph &g) { g.clear_out_edges_impl(v); } template typename std::enable_if::value>::type clear_vertex(typename Graph::vertex_descriptor v, Graph &g) { g.clear_in_edges_impl(v); g.clear_out_edges_impl(v); } template typename std::enable_if::value, typename Graph::vertex_descriptor>::type source(typename Graph::edge_descriptor e, const Graph &) { return Graph::source_impl(e); } template typename std::enable_if::value, typename Graph::vertex_descriptor>::type target(typename Graph::edge_descriptor e, const Graph &) { return Graph::target_impl(e); } template typename std::enable_if::value, typename Graph::degree_size_type>::type out_degree(typename Graph::vertex_descriptor v, const Graph &) { return Graph::out_degree_impl(v); } template typename std::enable_if::value, std::pair>::type out_edges(typename Graph::vertex_descriptor v, const Graph &) { return Graph::out_edges_impl(v); } template typename std::enable_if::value, typename Graph::degree_size_type>::type in_degree(typename Graph::vertex_descriptor v, const Graph &) { return Graph::in_degree_impl(v); } template typename std::enable_if::value, std::pair>::type in_edges(typename Graph::vertex_descriptor v, const Graph &) { return Graph::in_edges_impl(v); } template typename std::enable_if::value, typename Graph::degree_size_type>::type degree(typename Graph::vertex_descriptor v, const Graph &) { return Graph::degree_impl(v); } template typename std::enable_if::value, std::pair>::type adjacent_vertices(typename Graph::vertex_descriptor v, const Graph &) { return Graph::adjacent_vertices_impl(v); } template typename std::enable_if::value, std::pair>::type edge(typename Graph::vertex_descriptor u, typename Graph::vertex_descriptor v, const Graph &g) { return g.edge_impl(u, v); } template typename std::enable_if::value, std::pair>::type inv_adjacent_vertices(typename Graph::vertex_descriptor v, const Graph &) { return Graph::inv_adjacent_vertices_impl(v); } template typename std::enable_if::value, std::pair>::type add_edge(typename Graph::vertex_descriptor u, typename Graph::vertex_descriptor v, Graph &g) { return g.add_edge_impl(u, v); } template typename std::enable_if::value>::type remove_edge(typename Graph::edge_descriptor e, Graph &g) { g.remove_edge_impl(e); } template typename std::enable_if< !std::is_convertible::value && is_ue2_graph::value>::type remove_edge(Iter it, Graph &g) { g.remove_edge_impl(*it); } template typename std::enable_if::value>::type remove_out_edge_if(typename Graph::vertex_descriptor v, Predicate pred, Graph &g) { g.remove_out_edge_if_impl(v, pred); } template typename std::enable_if::value>::type remove_in_edge_if(typename Graph::vertex_descriptor v, Predicate pred, Graph &g) { g.remove_in_edge_if_impl(v, pred); } template typename std::enable_if::value>::type remove_edge_if(Predicate pred, Graph &g) { g.remove_edge_if_impl(pred); } template typename std::enable_if::value>::type remove_edge(const typename Graph::vertex_descriptor &u, const typename Graph::vertex_descriptor &v, Graph &g) { g.remove_edge_impl(u, v); } template typename std::enable_if::value, typename Graph::vertices_size_type>::type num_vertices(const Graph &g) { return g.num_vertices_impl(); } template typename std::enable_if::value, std::pair>::type vertices(const Graph &g) { return g.vertices_impl(); } template typename std::enable_if::value, typename Graph::edges_size_type>::type num_edges(const Graph &g) { return g.num_edges_impl(); } template typename std::enable_if::value, std::pair>::type edges(const Graph &g) { return g.edges_impl(); } template typename std::enable_if::value, typename Graph::vertex_descriptor>::type add_vertex(const typename Graph::vertex_property_type &vp, Graph &g) { return g.add_vertex_impl(vp); } template typename std::enable_if::value, std::pair>::type add_edge(typename Graph::vertex_descriptor u, typename Graph::vertex_descriptor v, const typename Graph::edge_property_type &ep, Graph &g) { return g.add_edge_impl(u, v, ep); } template typename std::enable_if::value>::type renumber_edges(Graph &g) { g.renumber_edges_impl(); } template typename std::enable_if::value>::type renumber_vertices(Graph &g) { g.renumber_vertices_impl(); } template typename std::enable_if::value, typename Graph::vertices_size_type>::type vertex_index_upper_bound(const Graph &g) { return g.vertex_index_upper_bound_impl(); } template typename std::enable_if::value, typename Graph::edges_size_type>::type edge_index_upper_bound(const Graph &g) { return g.edge_index_upper_bound_impl(); } template struct pointer_to_member_traits {}; template struct pointer_to_member_traits { using member_type = Return; using class_type = Class; }; template struct is_ue2_vertex_or_edge_property { static constexpr bool value = false; }; template struct is_ue2_vertex_or_edge_property< Graph, Property, typename std::enable_if::value && std::is_member_object_pointer< Property>::value>::type> { private: using class_type = typename pointer_to_member_traits::class_type; using vertex_type = typename Graph::vertex_property_type; using edge_type = typename Graph::edge_property_type; public: static constexpr bool value = std::is_same::value || std::is_same::value; }; using boost::vertex_index; using boost::edge_index; } // namespace ue2 namespace boost { /* Install partial specialisation of property_map - this is required for * adaptors (like filtered_graph) to know the type of the property maps */ template struct property_map::value && ue2::is_ue2_vertex_or_edge_property< Graph, Prop>::value>::type> { private: using prop_traits = ue2::pointer_to_member_traits; using member_type = typename prop_traits::member_type; using class_type = typename prop_traits::class_type; public: using type = typename Graph::template prop_map; using const_type = typename Graph::template prop_map; }; template struct property_map::value>::type> { using v_prop_type = typename Graph::vertex_property_type; using type = typename Graph::template prop_map; using const_type = typename Graph::template prop_map; }; template struct property_map::value>::type> { using e_prop_type = typename Graph::edge_property_type; using type = typename Graph::template prop_map; using const_type = typename Graph::template prop_map; }; template struct property_map::value>::type> { using v_prop_type = typename Graph::vertex_property_type; using type = typename Graph::template prop_map_all; using const_type = typename Graph::template prop_map_all; }; template struct property_map::value>::type> { using e_prop_type = typename Graph::edge_property_type; using type = typename Graph::template prop_map_all; using const_type = typename Graph::template prop_map_all; }; } // namespace boost namespace std { /* Specialization of std::hash so that vertex_descriptor can be used in * unordered containers. */ template struct hash> { using vertex_descriptor = ue2::graph_detail::vertex_descriptor; std::size_t operator()(const vertex_descriptor &v) const { return v.hash(); } }; /* Specialization of std::hash so that edge_descriptor can be used in * unordered containers. */ template struct hash> { using edge_descriptor = ue2::graph_detail::edge_descriptor; std::size_t operator()(const edge_descriptor &e) const { return e.hash(); } }; } // namespace std #endif