//======================================================================= // Copyright (c) Aaron Windsor 2007 // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) //======================================================================= #ifndef __BOYER_MYRVOLD_IMPL_HPP__ #define __BOYER_MYRVOLD_IMPL_HPP__ #include #include #include #include //for std::min macros #include #include #include #include #include #include #include #include namespace boost { namespace detail { enum bm_case_t { BM_NO_CASE_CHOSEN, BM_CASE_A, BM_CASE_B, BM_CASE_C, BM_CASE_D, BM_CASE_E }; } template < typename LowPointMap, typename DFSParentMap, typename DFSNumberMap, typename LeastAncestorMap, typename DFSParentEdgeMap, typename SizeType > struct planar_dfs_visitor : public dfs_visitor<> { planar_dfs_visitor(LowPointMap lpm, DFSParentMap dfs_p, DFSNumberMap dfs_n, LeastAncestorMap lam, DFSParentEdgeMap dfs_edge) : low(lpm) , parent(dfs_p) , df_number(dfs_n) , least_ancestor(lam) , df_edge(dfs_edge) , count(0) { } template < typename Vertex, typename Graph > void start_vertex(const Vertex& u, Graph&) { put(parent, u, u); put(least_ancestor, u, count); } template < typename Vertex, typename Graph > void discover_vertex(const Vertex& u, Graph&) { put(low, u, count); put(df_number, u, count); ++count; } template < typename Edge, typename Graph > void tree_edge(const Edge& e, Graph& g) { typedef typename graph_traits< Graph >::vertex_descriptor vertex_t; vertex_t s(source(e, g)); vertex_t t(target(e, g)); put(parent, t, s); put(df_edge, t, e); put(least_ancestor, t, get(df_number, s)); } template < typename Edge, typename Graph > void back_edge(const Edge& e, Graph& g) { typedef typename graph_traits< Graph >::vertex_descriptor vertex_t; typedef typename graph_traits< Graph >::vertices_size_type v_size_t; vertex_t s(source(e, g)); vertex_t t(target(e, g)); BOOST_USING_STD_MIN(); if (t != get(parent, s)) { v_size_t s_low_df_number = get(low, s); v_size_t t_df_number = get(df_number, t); v_size_t s_least_ancestor_df_number = get(least_ancestor, s); put(low, s, min BOOST_PREVENT_MACRO_SUBSTITUTION( s_low_df_number, t_df_number)); put(least_ancestor, s, min BOOST_PREVENT_MACRO_SUBSTITUTION( s_least_ancestor_df_number, t_df_number)); } } template < typename Vertex, typename Graph > void finish_vertex(const Vertex& u, Graph&) { typedef typename graph_traits< Graph >::vertices_size_type v_size_t; Vertex u_parent = get(parent, u); v_size_t u_parent_lowpoint = get(low, u_parent); v_size_t u_lowpoint = get(low, u); BOOST_USING_STD_MIN(); if (u_parent != u) { put(low, u_parent, min BOOST_PREVENT_MACRO_SUBSTITUTION( u_lowpoint, u_parent_lowpoint)); } } LowPointMap low; DFSParentMap parent; DFSNumberMap df_number; LeastAncestorMap least_ancestor; DFSParentEdgeMap df_edge; SizeType count; }; template < typename Graph, typename VertexIndexMap, typename StoreOldHandlesPolicy = graph::detail::store_old_handles, typename StoreEmbeddingPolicy = graph::detail::recursive_lazy_list > class boyer_myrvold_impl { typedef typename graph_traits< Graph >::vertices_size_type v_size_t; typedef typename graph_traits< Graph >::vertex_descriptor vertex_t; typedef typename graph_traits< Graph >::edge_descriptor edge_t; typedef typename graph_traits< Graph >::vertex_iterator vertex_iterator_t; typedef typename graph_traits< Graph >::edge_iterator edge_iterator_t; typedef typename graph_traits< Graph >::out_edge_iterator out_edge_iterator_t; typedef graph::detail::face_handle< Graph, StoreOldHandlesPolicy, StoreEmbeddingPolicy > face_handle_t; typedef std::vector< vertex_t > vertex_vector_t; typedef std::vector< edge_t > edge_vector_t; typedef std::list< vertex_t > vertex_list_t; typedef std::list< face_handle_t > face_handle_list_t; typedef boost::shared_ptr< face_handle_list_t > face_handle_list_ptr_t; typedef boost::shared_ptr< vertex_list_t > vertex_list_ptr_t; typedef boost::tuple< vertex_t, bool, bool > merge_stack_frame_t; typedef std::vector< merge_stack_frame_t > merge_stack_t; template < typename T > struct map_vertex_to_ { typedef iterator_property_map< typename std::vector< T >::iterator, VertexIndexMap > type; }; typedef typename map_vertex_to_< v_size_t >::type vertex_to_v_size_map_t; typedef typename map_vertex_to_< vertex_t >::type vertex_to_vertex_map_t; typedef typename map_vertex_to_< edge_t >::type vertex_to_edge_map_t; typedef typename map_vertex_to_< vertex_list_ptr_t >::type vertex_to_vertex_list_ptr_map_t; typedef typename map_vertex_to_< edge_vector_t >::type vertex_to_edge_vector_map_t; typedef typename map_vertex_to_< bool >::type vertex_to_bool_map_t; typedef typename map_vertex_to_< face_handle_t >::type vertex_to_face_handle_map_t; typedef typename map_vertex_to_< face_handle_list_ptr_t >::type vertex_to_face_handle_list_ptr_map_t; typedef typename map_vertex_to_< typename vertex_list_t::iterator >::type vertex_to_separated_node_map_t; template < typename BicompSideToTraverse = single_side, typename VisitorType = lead_visitor, typename Time = current_iteration > struct face_vertex_iterator { typedef face_iterator< Graph, vertex_to_face_handle_map_t, vertex_t, BicompSideToTraverse, VisitorType, Time > type; }; template < typename BicompSideToTraverse = single_side, typename Time = current_iteration > struct face_edge_iterator { typedef face_iterator< Graph, vertex_to_face_handle_map_t, edge_t, BicompSideToTraverse, lead_visitor, Time > type; }; public: boyer_myrvold_impl(const Graph& arg_g, VertexIndexMap arg_vm) : g(arg_g) , vm(arg_vm) , low_point_vector(num_vertices(g)) , dfs_parent_vector(num_vertices(g)) , dfs_number_vector(num_vertices(g)) , least_ancestor_vector(num_vertices(g)) , pertinent_roots_vector(num_vertices(g)) , backedge_flag_vector(num_vertices(g), num_vertices(g) + 1) , visited_vector(num_vertices(g), num_vertices(g) + 1) , face_handles_vector(num_vertices(g)) , dfs_child_handles_vector(num_vertices(g)) , separated_dfs_child_list_vector(num_vertices(g)) , separated_node_in_parent_list_vector(num_vertices(g)) , canonical_dfs_child_vector(num_vertices(g)) , flipped_vector(num_vertices(g), false) , backedges_vector(num_vertices(g)) , dfs_parent_edge_vector(num_vertices(g)) , vertices_by_dfs_num(num_vertices(g)) , low_point(low_point_vector.begin(), vm) , dfs_parent(dfs_parent_vector.begin(), vm) , dfs_number(dfs_number_vector.begin(), vm) , least_ancestor(least_ancestor_vector.begin(), vm) , pertinent_roots(pertinent_roots_vector.begin(), vm) , backedge_flag(backedge_flag_vector.begin(), vm) , visited(visited_vector.begin(), vm) , face_handles(face_handles_vector.begin(), vm) , dfs_child_handles(dfs_child_handles_vector.begin(), vm) , separated_dfs_child_list(separated_dfs_child_list_vector.begin(), vm) , separated_node_in_parent_list( separated_node_in_parent_list_vector.begin(), vm) , canonical_dfs_child(canonical_dfs_child_vector.begin(), vm) , flipped(flipped_vector.begin(), vm) , backedges(backedges_vector.begin(), vm) , dfs_parent_edge(dfs_parent_edge_vector.begin(), vm) { planar_dfs_visitor< vertex_to_v_size_map_t, vertex_to_vertex_map_t, vertex_to_v_size_map_t, vertex_to_v_size_map_t, vertex_to_edge_map_t, v_size_t > vis(low_point, dfs_parent, dfs_number, least_ancestor, dfs_parent_edge); // Perform a depth-first search to find each vertex's low point, least // ancestor, and dfs tree information depth_first_search(g, visitor(vis).vertex_index_map(vm)); // Sort vertices by their lowpoint - need this later in the constructor vertex_vector_t vertices_by_lowpoint(num_vertices(g)); std::copy(vertices(g).first, vertices(g).second, vertices_by_lowpoint.begin()); bucket_sort(vertices_by_lowpoint.begin(), vertices_by_lowpoint.end(), low_point, num_vertices(g)); // Sort vertices by their dfs number - need this to iterate by reverse // DFS number in the main loop. std::copy( vertices(g).first, vertices(g).second, vertices_by_dfs_num.begin()); bucket_sort(vertices_by_dfs_num.begin(), vertices_by_dfs_num.end(), dfs_number, num_vertices(g)); // Initialize face handles. A face handle is an abstraction that serves // two uses in our implementation - it allows us to efficiently move // along the outer face of embedded bicomps in a partially embedded // graph, and it provides storage for the planar embedding. Face // handles are implemented by a sequence of edges and are associated // with a particular vertex - the sequence of edges represents the // current embedding of edges around that vertex, and the first and // last edges in the sequence represent the pair of edges on the outer // face that are adjacent to the associated vertex. This lets us embed // edges in the graph by just pushing them on the front or back of the // sequence of edges held by the face handles. // // Our algorithm starts with a DFS tree of edges (where every vertex is // an articulation point and every edge is a singleton bicomp) and // repeatedly merges bicomps by embedding additional edges. Note that // any bicomp at any point in the algorithm can be associated with a // unique edge connecting the vertex of that bicomp with the lowest DFS // number (which we refer to as the "root" of the bicomp) with its DFS // child in the bicomp: the existence of two such edges would contradict // the properties of a DFS tree. We refer to the DFS child of the root // of a bicomp as the "canonical DFS child" of the bicomp. Note that a // vertex can be the root of more than one bicomp. // // We move around the external faces of a bicomp using a few property // maps, which we'll initialize presently: // // - face_handles: maps a vertex to a face handle that can be used to // move "up" a bicomp. For a vertex that isn't an articulation point, // this holds the face handles that can be used to move around that // vertex's unique bicomp. For a vertex that is an articulation point, // this holds the face handles associated with the unique bicomp that // the vertex is NOT the root of. These handles can therefore be used // to move from any point on the outer face of the tree of bicomps // around the current outer face towards the root of the DFS tree. // // - dfs_child_handles: these are used to hold face handles for // vertices that are articulation points - dfs_child_handles[v] holds // the face handles corresponding to vertex u in the bicomp with root // u and canonical DFS child v. // // - canonical_dfs_child: this property map allows one to determine the // canonical DFS child of a bicomp while traversing the outer face. // This property map is only valid when applied to one of the two // vertices adjacent to the root of the bicomp on the outer face. To // be more precise, if v is the canonical DFS child of a bicomp, // canonical_dfs_child[dfs_child_handles[v].first_vertex()] == v and // canonical_dfs_child[dfs_child_handles[v].second_vertex()] == v. // // - pertinent_roots: given a vertex v, pertinent_roots[v] contains a // list of face handles pointing to the top of bicomps that need to // be visited by the current walkdown traversal (since they lead to // backedges that need to be embedded). These lists are populated by // the walkup and consumed by the walkdown. vertex_iterator_t vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_t v(*vi); vertex_t parent = dfs_parent[v]; if (parent != v) { edge_t parent_edge = dfs_parent_edge[v]; add_to_embedded_edges(parent_edge, StoreOldHandlesPolicy()); face_handles[v] = face_handle_t(v, parent_edge, g); dfs_child_handles[v] = face_handle_t(parent, parent_edge, g); } else { face_handles[v] = face_handle_t(v); dfs_child_handles[v] = face_handle_t(parent); } canonical_dfs_child[v] = v; pertinent_roots[v] = face_handle_list_ptr_t(new face_handle_list_t); separated_dfs_child_list[v] = vertex_list_ptr_t(new vertex_list_t); } // We need to create a list of not-yet-merged depth-first children for // each vertex that will be updated as bicomps get merged. We sort each // list by ascending lowpoint, which allows the externally_active // function to run in constant time, and we keep a pointer to each // vertex's representation in its parent's list, which allows merging // in constant time. for (typename vertex_vector_t::iterator itr = vertices_by_lowpoint.begin(); itr != vertices_by_lowpoint.end(); ++itr) { vertex_t v(*itr); vertex_t parent(dfs_parent[v]); if (v != parent) { separated_node_in_parent_list[v] = separated_dfs_child_list[parent]->insert( separated_dfs_child_list[parent]->end(), v); } } // The merge stack holds path information during a walkdown iteration merge_stack.reserve(num_vertices(g)); } bool is_planar() { // This is the main algorithm: starting with a DFS tree of embedded // edges (which, since it's a tree, is planar), iterate through all // vertices by reverse DFS number, attempting to embed all backedges // connecting the current vertex to vertices with higher DFS numbers. // // The walkup is a procedure that examines all such backedges and sets // up the required data structures so that they can be searched by the // walkdown in linear time. The walkdown does the actual work of // embedding edges and flipping bicomps, and can identify when it has // come across a kuratowski subgraph. // // store_old_face_handles caches face handles from the previous // iteration - this is used only for the kuratowski subgraph isolation, // and is therefore dispatched based on the StoreOldHandlesPolicy. // // clean_up_embedding does some clean-up and fills in values that have // to be computed lazily during the actual execution of the algorithm // (for instance, whether or not a bicomp is flipped in the final // embedding). It's dispatched on the the StoreEmbeddingPolicy, since // it's not needed if an embedding isn't desired. typename vertex_vector_t::reverse_iterator vi, vi_end; vi_end = vertices_by_dfs_num.rend(); for (vi = vertices_by_dfs_num.rbegin(); vi != vi_end; ++vi) { store_old_face_handles(StoreOldHandlesPolicy()); vertex_t v(*vi); walkup(v); if (!walkdown(v)) return false; } clean_up_embedding(StoreEmbeddingPolicy()); return true; } private: void walkup(vertex_t v) { // The point of the walkup is to follow all backedges from v to // vertices with higher DFS numbers, and update pertinent_roots // for the bicomp roots on the path from backedge endpoints up // to v. This will set the stage for the walkdown to efficiently // traverse the graph of bicomps down from v. typedef typename face_vertex_iterator< both_sides >::type walkup_iterator_t; out_edge_iterator_t oi, oi_end; for (boost::tie(oi, oi_end) = out_edges(v, g); oi != oi_end; ++oi) { edge_t e(*oi); vertex_t e_source(source(e, g)); vertex_t e_target(target(e, g)); if (e_source == e_target) { self_loops.push_back(e); continue; } vertex_t w(e_source == v ? e_target : e_source); // continue if not a back edge or already embedded if (dfs_number[w] < dfs_number[v] || e == dfs_parent_edge[w]) continue; backedges[w].push_back(e); v_size_t timestamp = dfs_number[v]; backedge_flag[w] = timestamp; walkup_iterator_t walkup_itr(w, face_handles); walkup_iterator_t walkup_end; vertex_t lead_vertex = w; while (true) { // Move to the root of the current bicomp or the first visited // vertex on the bicomp by going up each side in parallel while (walkup_itr != walkup_end && visited[*walkup_itr] != timestamp) { lead_vertex = *walkup_itr; visited[lead_vertex] = timestamp; ++walkup_itr; } // If we've found the root of a bicomp through a path we haven't // seen before, update pertinent_roots with a handle to the // current bicomp. Otherwise, we've just seen a path we've been // up before, so break out of the main while loop. if (walkup_itr == walkup_end) { vertex_t dfs_child = canonical_dfs_child[lead_vertex]; vertex_t parent = dfs_parent[dfs_child]; visited[dfs_child_handles[dfs_child].first_vertex()] = timestamp; visited[dfs_child_handles[dfs_child].second_vertex()] = timestamp; if (low_point[dfs_child] < dfs_number[v] || least_ancestor[dfs_child] < dfs_number[v]) { pertinent_roots[parent]->push_back( dfs_child_handles[dfs_child]); } else { pertinent_roots[parent]->push_front( dfs_child_handles[dfs_child]); } if (parent != v && visited[parent] != timestamp) { walkup_itr = walkup_iterator_t(parent, face_handles); lead_vertex = parent; } else break; } else break; } } } bool walkdown(vertex_t v) { // This procedure is where all of the action is - pertinent_roots // has already been set up by the walkup, so we just need to move // down bicomps from v until we find vertices that have been // labeled as backedge endpoints. Once we find such a vertex, we // embed the corresponding edge and glue together the bicomps on // the path connecting the two vertices in the edge. This may // involve flipping bicomps along the way. vertex_t w; // the other endpoint of the edge we're embedding while (!pertinent_roots[v]->empty()) { face_handle_t root_face_handle = pertinent_roots[v]->front(); face_handle_t curr_face_handle = root_face_handle; pertinent_roots[v]->pop_front(); merge_stack.clear(); while (true) { typename face_vertex_iterator<>::type first_face_itr, second_face_itr, face_end; vertex_t first_side_vertex = graph_traits< Graph >::null_vertex(); vertex_t second_side_vertex = graph_traits< Graph >::null_vertex(); vertex_t first_tail, second_tail; first_tail = second_tail = curr_face_handle.get_anchor(); first_face_itr = typename face_vertex_iterator<>::type( curr_face_handle, face_handles, first_side()); second_face_itr = typename face_vertex_iterator<>::type( curr_face_handle, face_handles, second_side()); for (; first_face_itr != face_end; ++first_face_itr) { vertex_t face_vertex(*first_face_itr); if (pertinent(face_vertex, v) || externally_active(face_vertex, v)) { first_side_vertex = face_vertex; second_side_vertex = face_vertex; break; } first_tail = face_vertex; } if (first_side_vertex == graph_traits< Graph >::null_vertex() || first_side_vertex == curr_face_handle.get_anchor()) break; for (; second_face_itr != face_end; ++second_face_itr) { vertex_t face_vertex(*second_face_itr); if (pertinent(face_vertex, v) || externally_active(face_vertex, v)) { second_side_vertex = face_vertex; break; } second_tail = face_vertex; } vertex_t chosen; bool chose_first_upper_path; if (internally_active(first_side_vertex, v)) { chosen = first_side_vertex; chose_first_upper_path = true; } else if (internally_active(second_side_vertex, v)) { chosen = second_side_vertex; chose_first_upper_path = false; } else if (pertinent(first_side_vertex, v)) { chosen = first_side_vertex; chose_first_upper_path = true; } else if (pertinent(second_side_vertex, v)) { chosen = second_side_vertex; chose_first_upper_path = false; } else { // If there's a pertinent vertex on the lower face // between the first_face_itr and the second_face_itr, // this graph isn't planar. for (; *first_face_itr != second_side_vertex; ++first_face_itr) { vertex_t p(*first_face_itr); if (pertinent(p, v)) { // Found a Kuratowski subgraph kuratowski_v = v; kuratowski_x = first_side_vertex; kuratowski_y = second_side_vertex; return false; } } // Otherwise, the fact that we didn't find a pertinent // vertex on this face is fine - we should set the // short-circuit edges and break out of this loop to // start looking at a different pertinent root. if (first_side_vertex == second_side_vertex) { if (first_tail != v) { vertex_t first = face_handles[first_tail].first_vertex(); vertex_t second = face_handles[first_tail].second_vertex(); boost::tie(first_side_vertex, first_tail) = make_tuple(first_tail, first == first_side_vertex ? second : first); } else if (second_tail != v) { vertex_t first = face_handles[second_tail].first_vertex(); vertex_t second = face_handles[second_tail].second_vertex(); boost::tie(second_side_vertex, second_tail) = make_tuple(second_tail, first == second_side_vertex ? second : first); } else break; } canonical_dfs_child[first_side_vertex] = canonical_dfs_child[root_face_handle.first_vertex()]; canonical_dfs_child[second_side_vertex] = canonical_dfs_child[root_face_handle.second_vertex()]; root_face_handle.set_first_vertex(first_side_vertex); root_face_handle.set_second_vertex(second_side_vertex); if (face_handles[first_side_vertex].first_vertex() == first_tail) face_handles[first_side_vertex].set_first_vertex(v); else face_handles[first_side_vertex].set_second_vertex(v); if (face_handles[second_side_vertex].first_vertex() == second_tail) face_handles[second_side_vertex].set_first_vertex(v); else face_handles[second_side_vertex].set_second_vertex(v); break; } // When we unwind the stack, we need to know which direction // we came down from on the top face handle bool chose_first_lower_path = (chose_first_upper_path && face_handles[chosen].first_vertex() == first_tail) || (!chose_first_upper_path && face_handles[chosen].first_vertex() == second_tail); // If there's a backedge at the chosen vertex, embed it now if (backedge_flag[chosen] == dfs_number[v]) { w = chosen; backedge_flag[chosen] = num_vertices(g) + 1; add_to_merge_points(chosen, StoreOldHandlesPolicy()); typename edge_vector_t::iterator ei, ei_end; ei_end = backedges[chosen].end(); for (ei = backedges[chosen].begin(); ei != ei_end; ++ei) { edge_t e(*ei); add_to_embedded_edges(e, StoreOldHandlesPolicy()); if (chose_first_lower_path) face_handles[chosen].push_first(e, g); else face_handles[chosen].push_second(e, g); } } else { merge_stack.push_back(make_tuple(chosen, chose_first_upper_path, chose_first_lower_path)); curr_face_handle = *pertinent_roots[chosen]->begin(); continue; } // Unwind the merge stack to the root, merging all bicomps bool bottom_path_follows_first; bool top_path_follows_first; bool next_bottom_follows_first = chose_first_upper_path; vertex_t merge_point = chosen; while (!merge_stack.empty()) { bottom_path_follows_first = next_bottom_follows_first; boost::tie(merge_point, next_bottom_follows_first, top_path_follows_first) = merge_stack.back(); merge_stack.pop_back(); face_handle_t top_handle(face_handles[merge_point]); face_handle_t bottom_handle( *pertinent_roots[merge_point]->begin()); vertex_t bottom_dfs_child = canonical_dfs_child [pertinent_roots[merge_point]->begin()->first_vertex()]; remove_vertex_from_separated_dfs_child_list( canonical_dfs_child[pertinent_roots[merge_point] ->begin() ->first_vertex()]); pertinent_roots[merge_point]->pop_front(); add_to_merge_points( top_handle.get_anchor(), StoreOldHandlesPolicy()); if (top_path_follows_first && bottom_path_follows_first) { bottom_handle.flip(); top_handle.glue_first_to_second(bottom_handle); } else if (!top_path_follows_first && bottom_path_follows_first) { flipped[bottom_dfs_child] = true; top_handle.glue_second_to_first(bottom_handle); } else if (top_path_follows_first && !bottom_path_follows_first) { flipped[bottom_dfs_child] = true; top_handle.glue_first_to_second(bottom_handle); } else //! top_path_follows_first && //! !bottom_path_follows_first { bottom_handle.flip(); top_handle.glue_second_to_first(bottom_handle); } } // Finally, embed all edges (v,w) at their upper end points canonical_dfs_child[w] = canonical_dfs_child[root_face_handle.first_vertex()]; add_to_merge_points( root_face_handle.get_anchor(), StoreOldHandlesPolicy()); typename edge_vector_t::iterator ei, ei_end; ei_end = backedges[chosen].end(); for (ei = backedges[chosen].begin(); ei != ei_end; ++ei) { if (next_bottom_follows_first) root_face_handle.push_first(*ei, g); else root_face_handle.push_second(*ei, g); } backedges[chosen].clear(); curr_face_handle = root_face_handle; } // while(true) } // while(!pertinent_roots[v]->empty()) return true; } void store_old_face_handles(graph::detail::no_old_handles) {} void store_old_face_handles(graph::detail::store_old_handles) { for (typename std::vector< vertex_t >::iterator mp_itr = current_merge_points.begin(); mp_itr != current_merge_points.end(); ++mp_itr) { face_handles[*mp_itr].store_old_face_handles(); } current_merge_points.clear(); } void add_to_merge_points(vertex_t, graph::detail::no_old_handles) {} void add_to_merge_points(vertex_t v, graph::detail::store_old_handles) { current_merge_points.push_back(v); } void add_to_embedded_edges(edge_t, graph::detail::no_old_handles) {} void add_to_embedded_edges(edge_t e, graph::detail::store_old_handles) { embedded_edges.push_back(e); } void clean_up_embedding(graph::detail::no_embedding) {} void clean_up_embedding(graph::detail::store_embedding) { // If the graph isn't biconnected, we'll still have entries // in the separated_dfs_child_list for some vertices. Since // these represent articulation points, we can obtain a // planar embedding no matter what order we embed them in. vertex_iterator_t xi, xi_end; for (boost::tie(xi, xi_end) = vertices(g); xi != xi_end; ++xi) { if (!separated_dfs_child_list[*xi]->empty()) { typename vertex_list_t::iterator yi, yi_end; yi_end = separated_dfs_child_list[*xi]->end(); for (yi = separated_dfs_child_list[*xi]->begin(); yi != yi_end; ++yi) { dfs_child_handles[*yi].flip(); face_handles[*xi].glue_first_to_second( dfs_child_handles[*yi]); } } } // Up until this point, we've flipped bicomps lazily by setting // flipped[v] to true if the bicomp rooted at v was flipped (the // lazy aspect of this flip is that all descendents of that vertex // need to have their orientations reversed as well). Now, we // traverse the DFS tree by DFS number and perform the actual // flipping as needed typedef typename vertex_vector_t::iterator vertex_vector_itr_t; vertex_vector_itr_t vi_end = vertices_by_dfs_num.end(); for (vertex_vector_itr_t vi = vertices_by_dfs_num.begin(); vi != vi_end; ++vi) { vertex_t v(*vi); bool v_flipped = flipped[v]; bool p_flipped = flipped[dfs_parent[v]]; if (v_flipped && !p_flipped) { face_handles[v].flip(); } else if (p_flipped && !v_flipped) { face_handles[v].flip(); flipped[v] = true; } else { flipped[v] = false; } } // If there are any self-loops in the graph, they were flagged // during the walkup, and we should add them to the embedding now. // Adding a self loop anywhere in the embedding could never // invalidate the embedding, but they would complicate the traversal // if they were added during the walkup/walkdown. typename edge_vector_t::iterator ei, ei_end; ei_end = self_loops.end(); for (ei = self_loops.begin(); ei != ei_end; ++ei) { edge_t e(*ei); face_handles[source(e, g)].push_second(e, g); } } bool pertinent(vertex_t w, vertex_t v) { // w is pertinent with respect to v if there is a backedge (v,w) or if // w is the root of a bicomp that contains a pertinent vertex. return backedge_flag[w] == dfs_number[v] || !pertinent_roots[w]->empty(); } bool externally_active(vertex_t w, vertex_t v) { // Let a be any proper depth-first search ancestor of v. w is externally // active with respect to v if there exists a backedge (a,w) or a // backedge (a,w_0) for some w_0 in a descendent bicomp of w. v_size_t dfs_number_of_v = dfs_number[v]; return (least_ancestor[w] < dfs_number_of_v) || (!separated_dfs_child_list[w]->empty() && low_point[separated_dfs_child_list[w]->front()] < dfs_number_of_v); } bool internally_active(vertex_t w, vertex_t v) { return pertinent(w, v) && !externally_active(w, v); } void remove_vertex_from_separated_dfs_child_list(vertex_t v) { typename vertex_list_t::iterator to_delete = separated_node_in_parent_list[v]; garbage.splice(garbage.end(), *separated_dfs_child_list[dfs_parent[v]], to_delete, boost::next(to_delete)); } // End of the implementation of the basic Boyer-Myrvold Algorithm. The rest // of the code below implements the isolation of a Kuratowski subgraph in // the case that the input graph is not planar. This is by far the most // complicated part of the implementation. public: template < typename EdgeToBoolPropertyMap, typename EdgeContainer > vertex_t kuratowski_walkup(vertex_t v, EdgeToBoolPropertyMap forbidden_edge, EdgeToBoolPropertyMap goal_edge, EdgeToBoolPropertyMap is_embedded, EdgeContainer& path_edges) { vertex_t current_endpoint; bool seen_goal_edge = false; out_edge_iterator_t oi, oi_end; for (boost::tie(oi, oi_end) = out_edges(v, g); oi != oi_end; ++oi) forbidden_edge[*oi] = true; for (boost::tie(oi, oi_end) = out_edges(v, g); oi != oi_end; ++oi) { path_edges.clear(); edge_t e(*oi); current_endpoint = target(*oi, g) == v ? source(*oi, g) : target(*oi, g); if (dfs_number[current_endpoint] < dfs_number[v] || is_embedded[e] || v == current_endpoint // self-loop ) { // Not a backedge continue; } path_edges.push_back(e); if (goal_edge[e]) { return current_endpoint; } typedef typename face_edge_iterator<>::type walkup_itr_t; walkup_itr_t walkup_itr( current_endpoint, face_handles, first_side()); walkup_itr_t walkup_end; seen_goal_edge = false; while (true) { if (walkup_itr != walkup_end && forbidden_edge[*walkup_itr]) break; while (walkup_itr != walkup_end && !goal_edge[*walkup_itr] && !forbidden_edge[*walkup_itr]) { edge_t f(*walkup_itr); forbidden_edge[f] = true; path_edges.push_back(f); current_endpoint = source(f, g) == current_endpoint ? target(f, g) : source(f, g); ++walkup_itr; } if (walkup_itr != walkup_end && goal_edge[*walkup_itr]) { path_edges.push_back(*walkup_itr); seen_goal_edge = true; break; } walkup_itr = walkup_itr_t( current_endpoint, face_handles, first_side()); } if (seen_goal_edge) break; } if (seen_goal_edge) return current_endpoint; else return graph_traits< Graph >::null_vertex(); } template < typename OutputIterator, typename EdgeIndexMap > void extract_kuratowski_subgraph(OutputIterator o_itr, EdgeIndexMap em) { // If the main algorithm has failed to embed one of the back-edges from // a vertex v, we can use the current state of the algorithm to isolate // a Kuratowksi subgraph. The isolation process breaks down into five // cases, A - E. The general configuration of all five cases is shown in // figure 1. There is a vertex v from which the planar // v embedding process could not proceed. This means that // | there exists some bicomp containing three vertices // ----- x,y, and z as shown such that x and y are externally // | | active with respect to v (which means that there are // x y two vertices x_0 and y_0 such that (1) both x_0 and // | | y_0 are proper depth-first search ancestors of v and // --z-- (2) there are two disjoint paths, one connecting x // and x_0 and one connecting y and y_0, both // consisting // fig. 1 entirely of unembedded edges). Furthermore, there // exists a vertex z_0 such that z is a depth-first // search ancestor of z_0 and (v,z_0) is an unembedded back-edge from v. // x,y and z all exist on the same bicomp, which consists entirely of // embedded edges. The five subcases break down as follows, and are // handled by the algorithm logically in the order A-E: First, if v is // not on the same bicomp as x,y, and z, a K_3_3 can be isolated - this // is case A. So, we'll assume that v is on the same bicomp as x,y, and // z. If z_0 is on a different bicomp than x,y, and z, a K_3_3 can also // be isolated - this is a case B - so we'll assume from now on that v // is on the same bicomp as x, y, and z=z_0. In this case, one can use // properties of the Boyer-Myrvold algorithm to show the existence of an // "x-y path" connecting some vertex on the "left side" of the x,y,z // bicomp with some vertex on the "right side" of the bicomp (where the // left and right are split by a line drawn through v and z.If either of // the endpoints of the x-y path is above x or y on the bicomp, a K_3_3 // can be isolated - this is a case C. Otherwise, both endpoints are at // or below x and y on the bicomp. If there is a vertex alpha on the x-y // path such that alpha is not x or y and there's a path from alpha to v // that's disjoint from any of the edges on the bicomp and the x-y path, // a K_3_3 can be isolated - this is a case D. Otherwise, properties of // the Boyer-Myrvold algorithm can be used to show that another vertex // w exists on the lower half of the bicomp such that w is externally // active with respect to v. w can then be used to isolate a K_5 - this // is the configuration of case E. vertex_iterator_t vi, vi_end; edge_iterator_t ei, ei_end; out_edge_iterator_t oei, oei_end; typename std::vector< edge_t >::iterator xi, xi_end; // Clear the short-circuit edges - these are needed for the planar // testing/embedding algorithm to run in linear time, but they'll // complicate the kuratowski subgraph isolation for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { face_handles[*vi].reset_vertex_cache(); dfs_child_handles[*vi].reset_vertex_cache(); } vertex_t v = kuratowski_v; vertex_t x = kuratowski_x; vertex_t y = kuratowski_y; typedef iterator_property_map< typename std::vector< bool >::iterator, EdgeIndexMap > edge_to_bool_map_t; std::vector< bool > is_in_subgraph_vector(num_edges(g), false); edge_to_bool_map_t is_in_subgraph(is_in_subgraph_vector.begin(), em); std::vector< bool > is_embedded_vector(num_edges(g), false); edge_to_bool_map_t is_embedded(is_embedded_vector.begin(), em); typename std::vector< edge_t >::iterator embedded_itr, embedded_end; embedded_end = embedded_edges.end(); for (embedded_itr = embedded_edges.begin(); embedded_itr != embedded_end; ++embedded_itr) is_embedded[*embedded_itr] = true; // upper_face_vertex is true for x,y, and all vertices above x and y in // the bicomp std::vector< bool > upper_face_vertex_vector(num_vertices(g), false); vertex_to_bool_map_t upper_face_vertex( upper_face_vertex_vector.begin(), vm); std::vector< bool > lower_face_vertex_vector(num_vertices(g), false); vertex_to_bool_map_t lower_face_vertex( lower_face_vertex_vector.begin(), vm); // These next few variable declarations are all things that we need // to find. vertex_t z = graph_traits< Graph >::null_vertex(); vertex_t bicomp_root; vertex_t w = graph_traits< Graph >::null_vertex(); face_handle_t w_handle; face_handle_t v_dfchild_handle; vertex_t first_x_y_path_endpoint = graph_traits< Graph >::null_vertex(); vertex_t second_x_y_path_endpoint = graph_traits< Graph >::null_vertex(); vertex_t w_ancestor = v; detail::bm_case_t chosen_case = detail::BM_NO_CASE_CHOSEN; std::vector< edge_t > x_external_path; std::vector< edge_t > y_external_path; std::vector< edge_t > case_d_edges; std::vector< edge_t > z_v_path; std::vector< edge_t > w_path; // first, use a walkup to find a path from V that starts with a // backedge from V, then goes up until it hits either X or Y //(but doesn't find X or Y as the root of a bicomp) typename face_vertex_iterator<>::type x_upper_itr( x, face_handles, first_side()); typename face_vertex_iterator<>::type x_lower_itr( x, face_handles, second_side()); typename face_vertex_iterator<>::type face_itr, face_end; // Don't know which path from x is the upper or lower path - // we'll find out here for (face_itr = x_upper_itr; face_itr != face_end; ++face_itr) { if (*face_itr == y) { std::swap(x_upper_itr, x_lower_itr); break; } } upper_face_vertex[x] = true; vertex_t current_vertex = x; vertex_t previous_vertex; for (face_itr = x_upper_itr; face_itr != face_end; ++face_itr) { previous_vertex = current_vertex; current_vertex = *face_itr; upper_face_vertex[current_vertex] = true; } v_dfchild_handle = dfs_child_handles[canonical_dfs_child[previous_vertex]]; for (face_itr = x_lower_itr; *face_itr != y; ++face_itr) { vertex_t current_vertex(*face_itr); lower_face_vertex[current_vertex] = true; typename face_handle_list_t::iterator roots_itr, roots_end; if (w == graph_traits< Graph >::null_vertex()) // haven't found a w // yet { roots_end = pertinent_roots[current_vertex]->end(); for (roots_itr = pertinent_roots[current_vertex]->begin(); roots_itr != roots_end; ++roots_itr) { if (low_point [canonical_dfs_child[roots_itr->first_vertex()]] < dfs_number[v]) { w = current_vertex; w_handle = *roots_itr; break; } } } } for (; face_itr != face_end; ++face_itr) { vertex_t current_vertex(*face_itr); upper_face_vertex[current_vertex] = true; bicomp_root = current_vertex; } typedef typename face_edge_iterator<>::type walkup_itr_t; std::vector< bool > outer_face_edge_vector(num_edges(g), false); edge_to_bool_map_t outer_face_edge(outer_face_edge_vector.begin(), em); walkup_itr_t walkup_end; for (walkup_itr_t walkup_itr(x, face_handles, first_side()); walkup_itr != walkup_end; ++walkup_itr) { outer_face_edge[*walkup_itr] = true; is_in_subgraph[*walkup_itr] = true; } for (walkup_itr_t walkup_itr(x, face_handles, second_side()); walkup_itr != walkup_end; ++walkup_itr) { outer_face_edge[*walkup_itr] = true; is_in_subgraph[*walkup_itr] = true; } std::vector< bool > forbidden_edge_vector(num_edges(g), false); edge_to_bool_map_t forbidden_edge(forbidden_edge_vector.begin(), em); std::vector< bool > goal_edge_vector(num_edges(g), false); edge_to_bool_map_t goal_edge(goal_edge_vector.begin(), em); // Find external path to x and to y for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { edge_t e(*ei); goal_edge[e] = !outer_face_edge[e] && (source(e, g) == x || target(e, g) == x); forbidden_edge[*ei] = outer_face_edge[*ei]; } vertex_t x_ancestor = v; vertex_t x_endpoint = graph_traits< Graph >::null_vertex(); while (x_endpoint == graph_traits< Graph >::null_vertex()) { x_ancestor = dfs_parent[x_ancestor]; x_endpoint = kuratowski_walkup(x_ancestor, forbidden_edge, goal_edge, is_embedded, x_external_path); } for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { edge_t e(*ei); goal_edge[e] = !outer_face_edge[e] && (source(e, g) == y || target(e, g) == y); forbidden_edge[*ei] = outer_face_edge[*ei]; } vertex_t y_ancestor = v; vertex_t y_endpoint = graph_traits< Graph >::null_vertex(); while (y_endpoint == graph_traits< Graph >::null_vertex()) { y_ancestor = dfs_parent[y_ancestor]; y_endpoint = kuratowski_walkup(y_ancestor, forbidden_edge, goal_edge, is_embedded, y_external_path); } vertex_t parent, child; // If v isn't on the same bicomp as x and y, it's a case A if (bicomp_root != v) { chosen_case = detail::BM_CASE_A; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) if (lower_face_vertex[*vi]) for (boost::tie(oei, oei_end) = out_edges(*vi, g); oei != oei_end; ++oei) if (!outer_face_edge[*oei]) goal_edge[*oei] = true; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) forbidden_edge[*ei] = outer_face_edge[*ei]; z = kuratowski_walkup( v, forbidden_edge, goal_edge, is_embedded, z_v_path); } else if (w != graph_traits< Graph >::null_vertex()) { chosen_case = detail::BM_CASE_B; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { edge_t e(*ei); goal_edge[e] = false; forbidden_edge[e] = outer_face_edge[e]; } goal_edge[w_handle.first_edge()] = true; goal_edge[w_handle.second_edge()] = true; z = kuratowski_walkup( v, forbidden_edge, goal_edge, is_embedded, z_v_path); for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { forbidden_edge[*ei] = outer_face_edge[*ei]; } typename std::vector< edge_t >::iterator pi, pi_end; pi_end = z_v_path.end(); for (pi = z_v_path.begin(); pi != pi_end; ++pi) { goal_edge[*pi] = true; } w_ancestor = v; vertex_t w_endpoint = graph_traits< Graph >::null_vertex(); while (w_endpoint == graph_traits< Graph >::null_vertex()) { w_ancestor = dfs_parent[w_ancestor]; w_endpoint = kuratowski_walkup( w_ancestor, forbidden_edge, goal_edge, is_embedded, w_path); } // We really want both the w walkup and the z walkup to finish on // exactly the same edge, but for convenience (since we don't have // control over which side of a bicomp a walkup moves up) we've // defined the walkup to either end at w_handle.first_edge() or // w_handle.second_edge(). If both walkups ended at different edges, // we'll do a little surgery on the w walkup path to make it follow // the other side of the final bicomp. if ((w_path.back() == w_handle.first_edge() && z_v_path.back() == w_handle.second_edge()) || (w_path.back() == w_handle.second_edge() && z_v_path.back() == w_handle.first_edge())) { walkup_itr_t wi, wi_end; edge_t final_edge = w_path.back(); vertex_t anchor = source(final_edge, g) == w_handle.get_anchor() ? target(final_edge, g) : source(final_edge, g); if (face_handles[anchor].first_edge() == final_edge) wi = walkup_itr_t(anchor, face_handles, second_side()); else wi = walkup_itr_t(anchor, face_handles, first_side()); w_path.pop_back(); for (; wi != wi_end; ++wi) { edge_t e(*wi); if (w_path.back() == e) w_path.pop_back(); else w_path.push_back(e); } } } else { // We need to find a valid z, since the x-y path re-defines the // lower face, and the z we found earlier may now be on the upper // face. chosen_case = detail::BM_CASE_E; // The z we've used so far is just an externally active vertex on // the lower face path, but may not be the z we need for a case C, // D, or E subgraph. the z we need now is any externally active // vertex on the lower face path with both old_face_handles edges on // the outer face. Since we know an x-y path exists, such a z must // also exist. // TODO: find this z in the first place. // find the new z for (face_itr = x_lower_itr; *face_itr != y; ++face_itr) { vertex_t possible_z(*face_itr); if (pertinent(possible_z, v) && outer_face_edge[face_handles[possible_z] .old_first_edge()] && outer_face_edge[face_handles[possible_z] .old_second_edge()]) { z = possible_z; break; } } // find x-y path, and a w if one exists. if (externally_active(z, v)) w = z; typedef typename face_edge_iterator< single_side, previous_iteration >::type old_face_iterator_t; old_face_iterator_t first_old_face_itr( z, face_handles, first_side()); old_face_iterator_t second_old_face_itr( z, face_handles, second_side()); old_face_iterator_t old_face_itr, old_face_end; std::vector< old_face_iterator_t > old_face_iterators; old_face_iterators.push_back(first_old_face_itr); old_face_iterators.push_back(second_old_face_itr); std::vector< bool > x_y_path_vertex_vector(num_vertices(g), false); vertex_to_bool_map_t x_y_path_vertex( x_y_path_vertex_vector.begin(), vm); typename std::vector< old_face_iterator_t >::iterator of_itr, of_itr_end; of_itr_end = old_face_iterators.end(); for (of_itr = old_face_iterators.begin(); of_itr != of_itr_end; ++of_itr) { old_face_itr = *of_itr; vertex_t previous_vertex; bool seen_x_or_y = false; vertex_t current_vertex = z; for (; old_face_itr != old_face_end; ++old_face_itr) { edge_t e(*old_face_itr); previous_vertex = current_vertex; current_vertex = source(e, g) == current_vertex ? target(e, g) : source(e, g); if (current_vertex == x || current_vertex == y) seen_x_or_y = true; if (w == graph_traits< Graph >::null_vertex() && externally_active(current_vertex, v) && outer_face_edge[e] && outer_face_edge[*boost::next(old_face_itr)] && !seen_x_or_y) { w = current_vertex; } if (!outer_face_edge[e]) { if (!upper_face_vertex[current_vertex] && !lower_face_vertex[current_vertex]) { x_y_path_vertex[current_vertex] = true; } is_in_subgraph[e] = true; if (upper_face_vertex[source(e, g)] || lower_face_vertex[source(e, g)]) { if (first_x_y_path_endpoint == graph_traits< Graph >::null_vertex()) first_x_y_path_endpoint = source(e, g); else second_x_y_path_endpoint = source(e, g); } if (upper_face_vertex[target(e, g)] || lower_face_vertex[target(e, g)]) { if (first_x_y_path_endpoint == graph_traits< Graph >::null_vertex()) first_x_y_path_endpoint = target(e, g); else second_x_y_path_endpoint = target(e, g); } } else if (previous_vertex == x || previous_vertex == y) { chosen_case = detail::BM_CASE_C; } } } // Look for a case D - one of v's embedded edges will connect to the // x-y path along an inner face path. // First, get a list of all of v's embedded child edges out_edge_iterator_t v_edge_itr, v_edge_end; for (boost::tie(v_edge_itr, v_edge_end) = out_edges(v, g); v_edge_itr != v_edge_end; ++v_edge_itr) { edge_t embedded_edge(*v_edge_itr); if (!is_embedded[embedded_edge] || embedded_edge == dfs_parent_edge[v]) continue; case_d_edges.push_back(embedded_edge); vertex_t current_vertex = source(embedded_edge, g) == v ? target(embedded_edge, g) : source(embedded_edge, g); typename face_edge_iterator<>::type internal_face_itr, internal_face_end; if (face_handles[current_vertex].first_vertex() == v) { internal_face_itr = typename face_edge_iterator<>::type( current_vertex, face_handles, second_side()); } else { internal_face_itr = typename face_edge_iterator<>::type( current_vertex, face_handles, first_side()); } while (internal_face_itr != internal_face_end && !outer_face_edge[*internal_face_itr] && !x_y_path_vertex[current_vertex]) { edge_t e(*internal_face_itr); case_d_edges.push_back(e); current_vertex = source(e, g) == current_vertex ? target(e, g) : source(e, g); ++internal_face_itr; } if (x_y_path_vertex[current_vertex]) { chosen_case = detail::BM_CASE_D; break; } else { case_d_edges.clear(); } } } if (chosen_case != detail::BM_CASE_B && chosen_case != detail::BM_CASE_A) { // Finding z and w. for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { edge_t e(*ei); goal_edge[e] = !outer_face_edge[e] && (source(e, g) == z || target(e, g) == z); forbidden_edge[e] = outer_face_edge[e]; } kuratowski_walkup( v, forbidden_edge, goal_edge, is_embedded, z_v_path); if (chosen_case == detail::BM_CASE_E) { for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { forbidden_edge[*ei] = outer_face_edge[*ei]; goal_edge[*ei] = !outer_face_edge[*ei] && (source(*ei, g) == w || target(*ei, g) == w); } for (boost::tie(oei, oei_end) = out_edges(w, g); oei != oei_end; ++oei) { if (!outer_face_edge[*oei]) goal_edge[*oei] = true; } typename std::vector< edge_t >::iterator pi, pi_end; pi_end = z_v_path.end(); for (pi = z_v_path.begin(); pi != pi_end; ++pi) { goal_edge[*pi] = true; } w_ancestor = v; vertex_t w_endpoint = graph_traits< Graph >::null_vertex(); while (w_endpoint == graph_traits< Graph >::null_vertex()) { w_ancestor = dfs_parent[w_ancestor]; w_endpoint = kuratowski_walkup(w_ancestor, forbidden_edge, goal_edge, is_embedded, w_path); } } } // We're done isolating the Kuratowski subgraph at this point - // but there's still some cleaning up to do. // Update is_in_subgraph with the paths we just found xi_end = x_external_path.end(); for (xi = x_external_path.begin(); xi != xi_end; ++xi) is_in_subgraph[*xi] = true; xi_end = y_external_path.end(); for (xi = y_external_path.begin(); xi != xi_end; ++xi) is_in_subgraph[*xi] = true; xi_end = z_v_path.end(); for (xi = z_v_path.begin(); xi != xi_end; ++xi) is_in_subgraph[*xi] = true; xi_end = case_d_edges.end(); for (xi = case_d_edges.begin(); xi != xi_end; ++xi) is_in_subgraph[*xi] = true; xi_end = w_path.end(); for (xi = w_path.begin(); xi != xi_end; ++xi) is_in_subgraph[*xi] = true; child = bicomp_root; parent = dfs_parent[child]; while (child != parent) { is_in_subgraph[dfs_parent_edge[child]] = true; boost::tie(parent, child) = std::make_pair(dfs_parent[parent], parent); } // At this point, we've already isolated the Kuratowski subgraph and // collected all of the edges that compose it in the is_in_subgraph // property map. But we want the verification of such a subgraph to be // a deterministic process, and we can simplify the function // is_kuratowski_subgraph by cleaning up some edges here. if (chosen_case == detail::BM_CASE_B) { is_in_subgraph[dfs_parent_edge[v]] = false; } else if (chosen_case == detail::BM_CASE_C) { // In a case C subgraph, at least one of the x-y path endpoints // (call it alpha) is above either x or y on the outer face. The // other endpoint may be attached at x or y OR above OR below. In // any of these three cases, we can form a K_3_3 by removing the // edge attached to v on the outer face that is NOT on the path to // alpha. typename face_vertex_iterator< single_side, follow_visitor >::type face_itr, face_end; if (face_handles[v_dfchild_handle.first_vertex()].first_edge() == v_dfchild_handle.first_edge()) { face_itr = typename face_vertex_iterator< single_side, follow_visitor >::type(v_dfchild_handle.first_vertex(), face_handles, second_side()); } else { face_itr = typename face_vertex_iterator< single_side, follow_visitor >::type(v_dfchild_handle.first_vertex(), face_handles, first_side()); } for (; true; ++face_itr) { vertex_t current_vertex(*face_itr); if (current_vertex == x || current_vertex == y) { is_in_subgraph[v_dfchild_handle.first_edge()] = false; break; } else if (current_vertex == first_x_y_path_endpoint || current_vertex == second_x_y_path_endpoint) { is_in_subgraph[v_dfchild_handle.second_edge()] = false; break; } } } else if (chosen_case == detail::BM_CASE_D) { // Need to remove both of the edges adjacent to v on the outer face. // remove the connecting edges from v to bicomp, then // is_kuratowski_subgraph will shrink vertices of degree 1 // automatically... is_in_subgraph[v_dfchild_handle.first_edge()] = false; is_in_subgraph[v_dfchild_handle.second_edge()] = false; } else if (chosen_case == detail::BM_CASE_E) { // Similarly to case C, if the endpoints of the x-y path are both // below x and y, we should remove an edge to allow the subgraph to // contract to a K_3_3. if ((first_x_y_path_endpoint != x && first_x_y_path_endpoint != y) || (second_x_y_path_endpoint != x && second_x_y_path_endpoint != y)) { is_in_subgraph[dfs_parent_edge[v]] = false; vertex_t deletion_endpoint, other_endpoint; if (lower_face_vertex[first_x_y_path_endpoint]) { deletion_endpoint = second_x_y_path_endpoint; other_endpoint = first_x_y_path_endpoint; } else { deletion_endpoint = first_x_y_path_endpoint; other_endpoint = second_x_y_path_endpoint; } typename face_edge_iterator<>::type face_itr, face_end; bool found_other_endpoint = false; for (face_itr = typename face_edge_iterator<>::type( deletion_endpoint, face_handles, first_side()); face_itr != face_end; ++face_itr) { edge_t e(*face_itr); if (source(e, g) == other_endpoint || target(e, g) == other_endpoint) { found_other_endpoint = true; break; } } if (found_other_endpoint) { is_in_subgraph[face_handles[deletion_endpoint].first_edge()] = false; } else { is_in_subgraph[face_handles[deletion_endpoint] .second_edge()] = false; } } } for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) if (is_in_subgraph[*ei]) *o_itr = *ei; } template < typename EdgePermutation > void make_edge_permutation(EdgePermutation perm) { vertex_iterator_t vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_t v(*vi); perm[v].clear(); face_handles[v].get_list(std::back_inserter(perm[v])); } } private: const Graph& g; VertexIndexMap vm; vertex_t kuratowski_v; vertex_t kuratowski_x; vertex_t kuratowski_y; vertex_list_t garbage; // we delete items from linked lists by // splicing them into garbage // only need these two for kuratowski subgraph isolation std::vector< vertex_t > current_merge_points; std::vector< edge_t > embedded_edges; // property map storage std::vector< v_size_t > low_point_vector; std::vector< vertex_t > dfs_parent_vector; std::vector< v_size_t > dfs_number_vector; std::vector< v_size_t > least_ancestor_vector; std::vector< face_handle_list_ptr_t > pertinent_roots_vector; std::vector< v_size_t > backedge_flag_vector; std::vector< v_size_t > visited_vector; std::vector< face_handle_t > face_handles_vector; std::vector< face_handle_t > dfs_child_handles_vector; std::vector< vertex_list_ptr_t > separated_dfs_child_list_vector; std::vector< typename vertex_list_t::iterator > separated_node_in_parent_list_vector; std::vector< vertex_t > canonical_dfs_child_vector; std::vector< bool > flipped_vector; std::vector< edge_vector_t > backedges_vector; edge_vector_t self_loops; std::vector< edge_t > dfs_parent_edge_vector; vertex_vector_t vertices_by_dfs_num; // property maps vertex_to_v_size_map_t low_point; vertex_to_vertex_map_t dfs_parent; vertex_to_v_size_map_t dfs_number; vertex_to_v_size_map_t least_ancestor; vertex_to_face_handle_list_ptr_map_t pertinent_roots; vertex_to_v_size_map_t backedge_flag; vertex_to_v_size_map_t visited; vertex_to_face_handle_map_t face_handles; vertex_to_face_handle_map_t dfs_child_handles; vertex_to_vertex_list_ptr_map_t separated_dfs_child_list; vertex_to_separated_node_map_t separated_node_in_parent_list; vertex_to_vertex_map_t canonical_dfs_child; vertex_to_bool_map_t flipped; vertex_to_edge_vector_map_t backedges; vertex_to_edge_map_t dfs_parent_edge; // only need for kuratowski merge_stack_t merge_stack; }; } // namespace boost #endif //__BOYER_MYRVOLD_IMPL_HPP__