//======================================================================= // 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 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 void start_vertex(const Vertex& u, Graph&) { put(parent, u, u); put(least_ancestor, u, count); } template void discover_vertex(const Vertex& u, Graph&) { put(low, u, count); put(df_number, u, count); ++count; } template void tree_edge(const Edge& e, Graph& g) { typedef typename graph_traits::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 void back_edge(const Edge& e, Graph& g) { typedef typename graph_traits::vertex_descriptor vertex_t; typedef typename graph_traits::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 void finish_vertex(const Vertex& u, Graph&) { typedef typename graph_traits::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 class boyer_myrvold_impl { typedef typename graph_traits::vertices_size_type v_size_t; typedef typename graph_traits::vertex_descriptor vertex_t; typedef typename graph_traits::edge_descriptor edge_t; typedef typename graph_traits::vertex_iterator vertex_iterator_t; typedef typename graph_traits::edge_iterator edge_iterator_t; typedef typename graph_traits::out_edge_iterator out_edge_iterator_t; typedef graph::detail::face_handle face_handle_t; typedef std::vector vertex_vector_t; typedef std::vector edge_vector_t; typedef std::list 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 merge_stack_frame_t; typedef std::vector merge_stack_t; template struct map_vertex_to_ { typedef iterator_property_map ::iterator, VertexIndexMap> type; }; typedef typename map_vertex_to_::type vertex_to_v_size_map_t; typedef typename map_vertex_to_::type vertex_to_vertex_map_t; typedef typename map_vertex_to_::type vertex_to_edge_map_t; typedef typename map_vertex_to_::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_::type vertex_to_bool_map_t; typedef typename map_vertex_to_::type vertex_to_face_handle_map_t; typedef typename map_vertex_to_::type vertex_to_face_handle_list_ptr_map_t; typedef typename map_vertex_to_::type vertex_to_separated_node_map_t; template struct face_vertex_iterator { typedef face_iterator type; }; template struct face_edge_iterator { typedef face_iterator 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 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::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::null_vertex(); vertex_t second_side_vertex = graph_traits::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::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::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 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::null_vertex(); } template 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::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 ::iterator, EdgeIndexMap> edge_to_bool_map_t; std::vector is_in_subgraph_vector(num_edges(g), false); edge_to_bool_map_t is_in_subgraph(is_in_subgraph_vector.begin(), em); std::vector is_embedded_vector(num_edges(g), false); edge_to_bool_map_t is_embedded(is_embedded_vector.begin(), em); typename std::vector::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 upper_face_vertex_vector(num_vertices(g), false); vertex_to_bool_map_t upper_face_vertex (upper_face_vertex_vector.begin(), vm); std::vector 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::null_vertex(); vertex_t bicomp_root; vertex_t w = graph_traits::null_vertex(); face_handle_t w_handle; face_handle_t v_dfchild_handle; vertex_t first_x_y_path_endpoint = graph_traits::null_vertex(); vertex_t second_x_y_path_endpoint = graph_traits::null_vertex(); vertex_t w_ancestor = v; detail::bm_case_t chosen_case = detail::BM_NO_CASE_CHOSEN; std::vector x_external_path; std::vector y_external_path; std::vector case_d_edges; std::vector z_v_path; std::vector 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::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 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 forbidden_edge_vector(num_edges(g), false); edge_to_bool_map_t forbidden_edge(forbidden_edge_vector.begin(), em); std::vector 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::null_vertex(); while(x_endpoint == graph_traits::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::null_vertex(); while(y_endpoint == graph_traits::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::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::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::null_vertex(); while(w_endpoint == graph_traits::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 ::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_iterators; old_face_iterators.push_back(first_old_face_itr); old_face_iterators.push_back(second_old_face_itr); std::vector 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::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::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::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::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::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::null_vertex(); while(w_endpoint == graph_traits::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::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 ::type (v_dfchild_handle.first_vertex(), face_handles, second_side()); } else { face_itr = typename face_vertex_iterator ::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 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 current_merge_points; std::vector embedded_edges; //property map storage std::vector low_point_vector; std::vector dfs_parent_vector; std::vector dfs_number_vector; std::vector least_ancestor_vector; std::vector pertinent_roots_vector; std::vector backedge_flag_vector; std::vector 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 canonical_dfs_child_vector; std::vector flipped_vector; std::vector backedges_vector; edge_vector_t self_loops; std::vector 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__