//==========================================================================
//
// planarity.h
//
//==========================================================================
// $Id: planarity.h,v 1.14 2000/01/05 16:32:39 raitner Exp $
#ifndef PLANARITY_H
#define PLANARITY_H
#include <GTL/GTL.h>
#include <GTL/graph.h>
#include <GTL/algorithm.h>
#include <GTL/st_number.h>
#include <GTL/embedding.h>
#include <GTL/biconnectivity.h>
#include <GTL/pq_node.h>
__GTL_BEGIN_NAMESPACE
/**
* @short Tests if a graph can be drawn on a plane without any edge crossings
*
* This class implements the Lempel-Even-Cederbaum planarity test using
* PQ-trees. In case the graph is planar a planar embedding is obtained, i.e.
* for each node in the graph an ordered adjacency list is calculated, such
* that there exists a planar drawing in which all adjacent edges around a node
* apply to this order.
*
* <p> If the graph is not planar Kuratowski's famous theorem states that it
* must contain a subgraph hoemeomorphic to either K5 (the complete graph with
* five nodes) or K3,3 (the complete bipartite graph with three nodes each side).
* In this case the nodes and edges of the tested graph that form either of these
* two are calculated.
*
* <p> In case the graph is planar and has N nodes the algorithm needs
* <code>O(N)</code> time for the test (including the planar embedding). In case
* the graph isn't planar it needs at most <code>O(E)</code> time if E is the
* number of edges for both the test and the detection of K5 or K3,3.
*/
class GTL_EXTERN planarity : public algorithm
{
public:
/**
* Creates an object of the planarity test algorithm.
*
* @see algorithm
*/
planarity () : algorithm (), emp (false), kup (false), bip (true) { };
/**
* Checks whether planarity test can be applied to <code>G</code>. This should
* return always <code>GTL_OK</code>. There aren't any restrictions on
* <code>G</code>, even multiple edges and selfloops are tolerated. <em>Note:</em>
* Selfloops and multiple edges will not be added to the planar embedding.
* @ref planar_embedding#selfloops and @ref planar_embedding#multiple_edges can
* be used to get these.
*
* @param <code>G</code> arbitrary graph
* @return <code>GTL_OK</code> if planarity test can be applied
* or <code>GTL_ERROR</code> if not.
* @see algorithm#check
*/
int check (graph& G);
/**
* Runs planarity test on <code>G</code>. This should return always
* <code>GTL_OK</code>. The return value only tracks errors that might
* occur, it is definitly <em>not</em> the result of the test itself.
* The result of the test is stored in a member variable and can be
* accessed via @ref planarity#is_planar .
*
* @param <code>G</code> arbitrary graph
* @return <code>GTL_OK</code> if planarity test was sucessfully applied
* or <code>GTL_ERROR</code> if not.
* @see algorithm#run
*/
int run (graph& G);
/**
* Resets algorithm object, such that it can be applied to another graph.
*
* @see algorithm#reset
*/
void reset ();
/**
* If <code>p</code> is true a planar embedding will be calculated in
* the next run.
*
* @param <code>p</code> true iff embedding should be calculated.
* @see planarity#get_embedding
* @see planar_embedding
*/
void calc_embedding (bool p)
{ emp = p; if (!emp) kup = false; }
/**
* Returns true if a planar embedding will be calculated in
* the next run.
*
* @return true iff embedding will be calculated.
* @see planarity#get_embedding
* @see planar_embedding
*/
bool calc_embedding () const
{ return emp; }
/**
* If <code>p</code> is true the obstructions to planarity will be calculated
* in the next run. This implies the calculation of an embedding.
*
* @param <code>p</code> true iff obstructions to planarity should be calculated.
* @see planarity#get_obstruction_edges
* @see planarity#get_obstruction_nodes
*/
void calc_obstruction (bool p)
{ kup = p; if (kup) emp = true; }
/**
* Returns true if the obstructions to planarity will be calculated
* in the next run.
*
* @return true iff obstructions to planarity will be calculated.
* @see planarity#get_obstruction_edges
* @see planarity#get_obstruction_nodes
*/
bool calc_obstruction () const
{ return kup; }
/**
* Determines the strategy used to test a graph which is not biconnected
* If this is enabled the graph will be made biconnected by adding some
* new edges. This is usually faster than testing the biconnected components
* one by one, which is done if this option is disabled. By default this is
* enabled.
* <p>
* Please note that this is not fully tested, i.e. at the moment this feature
* should be used only for the test without embedding or kuratowski graphs.
*
* @param <code>p</code> true iff graph should be made biconnected.
* @see biconnectivity#make_biconnected
*/
void make_biconnected (bool p)
{ bip = p; }
/**
* Returns strategy for testing graphs, which are not biconnected.
*
* @return true iff graph will be made biconnected before test
* @see biconnectivity#make_biconnected
*/
bool make_biconnected () const
{ return bip; }
/**
* Result of last test.
*
* @return true iff graph in last <code>run</code> was planar.
*/
bool is_planar () const
{ return planar; }
/**
* If graph in last <code>run</code> was planar a planar embedding is
* calculated during the reductions. This function gives access to it.
*
* @return planar embedding of graph in last <code>run</code>.
* @see planarity#calc_embedding
*/
planar_embedding& get_embedding ()
{ return embedding; }
/**
* Returns the edges of a subgraph homeomorphic to either K3,3 or K5 if
* graph in last <code>run</code> was not planar.
*
* @return edges of subgraph homeomorphic to either K3,3 or K5
* @see planarity#get_obstruction_nodes
* @see planarity#calc_obstruction
*/
list<edge>& get_obstruction_edges ()
{ return ob_edges; }
/**
* Returns the nodes of a subgraph homeomorphic to either K3,3 or K5 if
* graph in last <code>run</code> was not planar.
*
* @return nodes of subgraph homeomorphic to either K3,3 or K5
* @see planarity#get_obstruction_edges
* @see planarity#calc_obstruction
*/
list<node>& get_obstruction_nodes ()
{ return ob_nodes; }
private:
/**
* Main procedure for planarity test. Assumes <code>G</code> to be
* undirected and biconnected. Used to test whether the biconnected
* components of a graph are planar.
*
* @param <code>G</code> biconnected, undirected graph
* @param <code>em</code> planar embedding (should be empty)
* @return <code>true</code> if <code>G</code> is planar
*/
bool run_on_biconnected (
graph& G,
planar_embedding& em);
/**
* Adds the embedding for component <code>G</code> to the embedding of
* the whole graph.
*
* @param <code>G</code> biconnected graph
* @param <code>em</code> embedding obtained through testing <code>G</code>
*/
void add_to_embedding (
graph& G,
planar_embedding& em);
/**
* The so called upward embedding can be obtained from the list of edges one gets
* in the reduction steps of the algorithm. The only problem is that some of these
* lists may be turned later in the algorithm. This procedure corrects the
* reversions according to the information stored in <code>dirs</code>.
*
* @param <code>em</code> embedding
* @param <code>st</code> st-numbers of biconnected graph
* @param <code>dirs</code> direction indicators obtained after each reduction.
*/
void correct_embedding (
planar_embedding& em,
st_number& st,
node_map<list<direction_indicator> >& dirs);
/**
* After the embedding has been corrected by the above procedure, we have
* a so called upward embedding, this means only the edges leading to
* nodes with smaller st-number than itself are in the adjacency list for
* some node. This procedure extends the upward embedding <code>em</code>
* to a full embedding. This is a recursive procedure (well basically
* it's a DFS starting at the node with the highest st-number).
*
* @param <code>n</code> current node (used for recursion)
* @param <code>em</code> embedding (at the beginning an upward embedding)
* @param <code>mark</code> marks used nodes in DFS.
* @param <code>upward_begin</code> marks the beginning of the upward embedding
*/
void extend_embedding (
node n,
planar_embedding& em,
node_map<int>& mark,
node_map<symlist<edge>::iterator >& upward_begin);
/**
* Make <code>G</code> the component specified in <code>it</code> by hiding
* everything not in this subgraph. For the sake of efficiency the whole graph
* is hidden at the beginning and then only what is in this component is
* restored.
*
* @param <code>G</code> whole graph; partially hidden
* @param <code>it</code> component to highlight
* @see graph#hide
*/
void switch_to_component (
graph& G,
biconnectivity::component_iterator it);
/**
* Main procedure for detecting K5 or K3,3. Many cases have to be taken
* into account so it is split in a lot of subroutines decribed below.
*
* @param <code>G</code> biconnected graph.
* @param <code>st</code> st-numbers of <code>G</code>
* @param <code>act</code> node for which the reduction failed
* @param <code>fail</code> (PQ-) node at which no matching could be applied
* @param <code>failed_at_root</code> true iff <code>fail</code> is the
* root of the pertinent subtree.
* @param <code>em</code> planar embedding obtained up to the moment the
* matchings stopped
* @param <code>dirs</code> direction indicators obtained up to the moment
* the matchings stopped
*/
void examine_obstruction (
graph& G,
st_number& st,
node act,
pq_node* fail,
bool failed_at_root,
planar_embedding& em,
node_map<list<direction_indicator> >& dirs);
/**
* Calculates a DFS-tree for the so called bush-form for the node with
* st-number <code>stop</code>, i.e. the induced subgraph consisting
* of all nodes with st-number smaller than <code>stop</code> and all edges
* from one of these to a higher numbered node lead to a virtual node with that
* number (there may be duplicates).
*
* @param <code>act</code> used in recursion; starts with node numbered 1
* @param <code>mark</code> marks for DFS; initially for all nodes 0
* @param <code>st</code> st-numbers for graph
* @param <code>stop</code> lowest st-number of virtual nodes
* @param <code>to_father</code> stores the edge to predecessor of each node
*/
void dfs_bushform (
node act,
node_map<int>& mark,
st_number& st,
int stop,
node_map<edge>& to_father);
/**
* In case the reduction failed at a Q-node the boundary of the biconnected
* component the Q-node represents can be obtained from <code>em</code>.
* No return value is needed, since all the edges on the boundary are added
* to the obstruction edges (although some of them have to be deleted in some
* cases).
*
* @param <code>n</code> node with lowest st-number in biconnected component
* @param <code>em</code> planar embedding (at least for this component)
*/
void attachment_cycle (node n, planar_embedding& em);
/**
* Marks all edges represented by leaves in the subtree rooted at <code>n</code>.
* In some cases where the reduction fails at a Q-node, which is not the root of
* the pertinent subtree, an adjacent edge of the node for which the
* reduction failed, which does not lead to that component has to be found.
*
* @param <code>n</code> root of subtree
* @param <code>mark</code> edges in subtree recieve 1, all other are unchanged.
*/
void mark_all_edges_to_leaves (pq_node* n, edge_map<int>& mark);
/**
* Searches one full and one empty leaf beneath <code>partial</code>. The join of
* these leaves and the node on the boundary to which <code>partial</code>
* is attached is added to the obstruction nodes. All edges that form this
* join are added to the obstruction edges.
*
* @param <code>partial</code> partial node
* @param <code>mark</code> nodes already used
* @param <code>to_father</code> predecessor relation in DFS tree
* @return empty leaf
*/
pq_leaf* run_through_partial (
q_node* partial,
node_map<int>& mark,
node_map<edge>& to_father);
/**
* Uses <code>to_father</code> to determine an already marked predecessor.
*
* @param <code>act</code> node
* @param <code>mark</code> nodes already used
* @param <code>to_father</code> predecessor relation in DFS tree
* @return marked node
*/
node up_until_marked (
node act,
node_map<int>& mark,
node_map<edge>& to_father);
/**
* Always uses a adjacent node with higher st-number as predecessor. Searches
* marked predecessor.
*
* @param <code>act</code> node
* @param <code>mark</code> nodes already used
* @param <code>st</code> used to determine predecessor
* @return marked node
*/
node up_until_marked (
node act,
node_map<int>& mark,
st_number& st);
/**
* Assumes that <code>n</code> is non empty. Searches full leaf beneath n.
*
* @param <code>n</code> (PQ-) node
* @return full leaf in subtree of <code>n</code>
*/
pq_leaf* search_full_leaf (pq_node* n);
/**
* Assumes that <code>n</code> is non full. Searches empty leaf beneath n.
*
* @param <code>n</code> (PQ-) node
* @return empty leaf in subtree of <code>n</code>
*/
pq_leaf* search_empty_leaf (pq_node* n);
/**
* Reduction failed at a P-node, which had at least three pertial sons.
*
* @param <code>p_fail</code> P-node at which reduction failed
* @param <code>act</code> node for which reduction failed
* @param <code>_st</code> st-numbers of graph
* @param <code>to_father</code> predecessors in DFS-tree of bushform
* @param <code>G</code> graph tested
*/
void case_A (
p_node* p_fail,
node act,
st_number& _st,
node_map<edge> to_father,
graph& G);
/**
* Reduction failed at a P-node, which isn't the root of the pertinent subtree
* and had at least two partial children.
*
* @param <code>p_fail</code> P-node at which reduction failed
* @param <code>act</code> node for which reduction failed
* @param <code>_st</code> st-numbers of graph
* @param <code>to_father</code> predecessors in DFS-tree of bushform
* @param <code>G</code> graph tested
*/
void case_B (
p_node* p_fail,
node act,
st_number& _st,
node_map<edge> to_father,
graph& G);
/**
* Reduction failed at a Q-node, such that there exist
* children a < b < c and a and c are both non-empty and b is non-full.
*
* @param <code>nodes</code> nodes on the boundary of <code>q_fail</code> to which
* the sons a, b, c are attached.
* @param <code>leaves</code> leaves in the subtrees of a, b, c. For a and c full
* leaves and an empty one for b.
* @param <code>_st</code> st-numbers of graph
* @param <code>to_father</code> predecessors in DFS-tree of bushform
* @param <code>G</code> graph tested
* @param <code>q_fail</code> Q-node at which reduction failed
*/
void case_C (
node* nodes,
pq_leaf** leaves,
st_number& _st,
node_map<edge> to_father,
graph& G,
q_node* q_fail);
/**
* Reduction failed at a non-root Q-node, such that there exist children
* a < b < c and a and c are both non-full and b is non-empty.
*
* @param <code>nodes</code> nodes on the boundary of <code>q_fail</code> to which
* the sons a, b, c are attached.
* @param <code>leaves</code> leaves in the subtrees of a, b, c. For a and c full
* leaves and an empty one for b.
* @param <code>_st</code> st-numbers of graph
* @param <code>to_father</code> predecessors in DFS-tree of bushform
* @param <code>G</code> graph tested
* @param <code>q_fail</code> Q-node at which reduction failed
*/
void case_D (
node* nodes,
pq_leaf** leaves,
st_number& _st,
node_map<edge> to_father,
graph& G,
q_node* q_fail);
/**
* Reduction failed at a non-root Q-node which has only two children,
* both partial.
*
* @param <code>nodes</code> nodes on the boundary of <code>q_fail</code> to which
* the two partial sons are attached.
* @param <code>leaves</code> two leaves in each subtree of a partial son. One
* full other empty.
* @param <code>_st</code> st-numbers of graph
* @param <code>to_father</code> predecessors in DFS-tree of bushform
* @param <code>G</code> graph tested
* @param <code>q_fail</code> Q-node at which reduction failed
* @param <code></code>
*/
void case_E (
node* nodes,
pq_leaf** leaves,
st_number& _st,
node_map<edge> to_father,
graph& G,
q_node* q_fail);
list<edge> ob_edges;
list<node> ob_nodes;
planar_embedding embedding;
bool planar;
bool emp;
bool kup;
bool bip;
};
__GTL_END_NAMESPACE
#endif // PLANARITY_H
//--------------------------------------------------------------------------
// end of file
//--------------------------------------------------------------------------
Documentation generated by raitner@hyperion on Tue Mar 7 10:13:50 CET 2000
|
Kdoc |