//==========================================================================
//
//   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
//--------------------------------------------------------------------------
