################################################################################
#
#       This file is part of Gato (Graph Algorithm Toolbox) 
#
#	file:   PlanarityTest.py
#	author: Ramazan Buzdemir (buzdemir@zpr.uni-koeln.de)
#
#       Copyright (C) 1998-2005, Alexander Schliep, Winfried Hochstaettler and 
#       Copyright 1998-2001 ZAIK/ZPR, Universitaet zu Koeln
#                                   
#       Contact: schliep@molgen.mpg.de, wh@zpr.uni-koeln.de             
#
#       Information: http://gato.sf.net
#
#       This library is free software; you can redistribute it and/or
#       modify it under the terms of the GNU Library General Public
#       License as published by the Free Software Foundation; either
#       version 2 of the License, or (at your option) any later version.
#
#       This library is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#       Library General Public License for more details.
#
#       You should have received a copy of the GNU Library General Public
#       License along with this library; if not, write to the Free
#       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
#
#       This file is version $Revision: 1.1 $ 
#                       from $Date: 2007/10/04 14:36:39 $
#             last change by $Author: edream $.
#
################################################################################


###############################################################################
###############################################################################
###############################################################################
#                                                                             #
#                            AN IMPLEMENTATION OF                             #
#                           THE HOPCROFT AND TARJAN                           #
#                    PLANARITY TEST AND EMBEDDING ALGORITHM                   #
#                                                                             #
###############################################################################
#                                                                             #
# References:                                                                 #
#                                                                             #
# [Meh84] K.Mehlhorn.                                                         #
#         "Data Structures and Efficient Algorithms."                         #
#         Springer Verlag, 1984.                                              #
# [MM94]  K.Mehlhorn and P.Mutzel.                                            #
#         "On the embedding phase of the Hopcroft and Tarjan planarity        #
#          testing algorithm."                                                #
#         Technical report no. 117/94, mpi, Saarbruecken, 1994                #
#                                                                             #
###############################################################################



#=============================================================================#
from copy import deepcopy
from DataStructures import Stack
from tkMessageBox import showinfo
#=============================================================================#



#=============================================================================#
class List:
    def __init__(self,el=[]):
        elc=deepcopy(el)
        self.elements=elc
        
        # a) Access Operations
    def length(self):
        return len(self.elements)
        
    def empty(self):
        if self.length()==0:
            return 1
        else:
            return 0
            
    def head(self):
        return self.elements[0]
        
    def tail(self):
        return self.elements[-1]
        
        # b)Update Operations
    def push(self,x):
        self.elements.insert(0,x)
        return x
        
    def Push(self,x):
        self.elements.append(x)
        return x
        
    def append(self,x):
        self.Push(x)
        
    def pop(self):
        x=self.elements[0]
        self.elements=self.elements[1:]
        return x
        
    def Pop(self):
        x=self.elements[-1]
        self.elements=self.elements[:-1]
        return x
        
    def clear(self):
        self.elements=[]
        
    def conc(self,A):
        self.elements=self.elements+A.elements
        A.elements=[]
        #=============================================================================#
        
        
        
        #=============================================================================#
class pt_graph:

    def __init__(self):
        self.V        = []
        self.E        = []
        self.adjEdges = {}
        
        # a) Access operations
    def source(self,e):
        return e[0]
        
    def target(self,e):
        return e[1]
        
    def number_of_nodes(self):
        return len(self.V)
        
    def number_of_edges(self):
        return len(self.E)
        
    def all_nodes(self):
        return self.V
        
    def all_edges(self):
        return self.E
        
    def adj_edges(self,v):
        return self.adjEdges[v]
        
    def adj_nodes(self,v):
        nodelist=[]
        for e in self.adj_edges(v):
            nodelist.append(e[1])
        return nodelist
        
    def first_node(self):
        return self.V[0]
        
    def last_node(self):
        return self.V[-1]
        
    def first_edge(self):
        return self.E[0]
        
    def last_edge(self):
        return self.E[-1]
        
    def first_adj_edge(self,v):
        if len(self.adj_edges(v))>0:
            return self.adj_edges(v)[0]
        else:
            return None
            
    def last_adj_edge(self,v):
        if len(self.adj_edges(v))>0:
            return self.adj_edges(v)[-1]
        else:
            return None
            
            # b) Update operations
    def new_node(self,v):
        self.V.append(v)
        self.adjEdges[v]=[]
        return v
        
    def new_edge(self,v,w):
        if v==w: # Loop
            raise GraphNotSimpleError
        if (v,w) in self.E: # Multiple edge
            raise GraphNotSimpleError
        self.E.append((v,w))
        self.adjEdges[v].append((v,w))
        return (v,w)
        
    def del_node(self,v):
        try:
            for k in self.V:
                for e in self.adj_edges(k):
                    if source(e)==v or target(e)==v:
                        self.adjEdges[k].remove(e)
            self.V.remove(v)
            for e in self.E:
                if source(e)==v or target(e)==v:
                    self.E.remove(e)
        except KeyError:
            raise NoSuchVertexError
            
    def del_edge(self,e):
        try:
            self.E.remove(e)
            self.adjEdges[source(e)].remove((source(e),target(e)))
        except KeyError:
            raise NoSuchEdgeError
            
    def del_nodes(self,node_list): # deletes all nodes in list L from self
        L=deepcopy(node_list)
        for l in L:
            self.del_node(l)
            
    def del_edges(self,edge_list): # deletes all edges in list L from self
        L=deepcopy(edge_list)
        for l in L:
            self.del_edge(l)
            
    def del_all_nodes(self): # deletes all nodes from self
        self.del_nodes(self.all_nodes())
        
    def del_all_edges(self): # deletes all edges from self
        self.del_edges(self.all_edges())
        
    def sort_edges(self, cost):
        def up(x,y):
            if x[1]<y[1]: return -1
            if x[1]==y[1]: return 0
            return 1
        sorted_list=cost.items()
        sorted_list.sort(up)
        self.del_all_edges()
        for i in sorted_list:
            self.new_edge(source(i[0]),target(i[0]))
            
def source(e):
    return e[0]
    
def target(e):
    return e[1]
    
def reversal(e):
    return (e[1],e[0])
    #=============================================================================#
    
    
    
    #=============================================================================#
class block:
# The constructor takes an edge and a list of attachments and creates 
# a block having the edge as the only segment in its left side.
#
# |flip| interchanges the two sides of a block.
#
# |head_of_Latt| and |head_of_Ratt| return the first elements 
# on |Latt| and |Ratt| respectively 
# and |Latt_empty| and |Ratt_empty| check these lists for emptyness.
#
# |left_interlace| checks whether the block interlaces with the left 
# side of the topmost block of stack |S|. 
# |right_interlace| does the same for the right side.
#
# |combine| combines the block with another block |Bprime| by simply 
# concatenating all lists.
#
# |clean| removes the attachment |w| from the block |B| (it is 
# guaranteed to be the first attachment of |B|). 
# If the block becomes empty then it records the placement of all 
# segments in the block in the array |alpha| and returns true.
# Otherwise it returns false.
#
# |add_to_Att| first makes sure that the right side has no attachment 
# above |w0| (by flipping); when |add_to_Att| is called at least one 
# side has no attachment above |w0|.
# |add_to_Att| then adds the lists |Ratt| and |Latt| to the output list 
# |Att| and records the placement of all segments in the block in |alpha|.

    def __init__(self,e,A):
        self.Latt=List(); self.Ratt=List() # list of attachments "ints"
        self.Lseg=List(); self.Rseg=List() # list of segments represented by
                                           # their defining "edges"
        self.Lseg.append(e)
        self.Latt.conc(A)  # the other two lists are empty
        
    def flip(self):
        ha=List() # "ints"
        he=List() # "edges"
        
        # we first interchange |Latt| and |Ratt| and then |Lseg| and |Rseg|
        ha.conc(self.Ratt); self.Ratt.conc(self.Latt); self.Latt.conc(ha);
        he.conc(self.Rseg); self.Rseg.conc(self.Lseg); self.Lseg.conc(he);
        
    def head_of_Latt(self):
        return self.Latt.head()
        
    def empty_Latt(self):
        return self.Latt.empty()
        
    def head_of_Ratt(self):
        return self.Ratt.head()
        
    def empty_Ratt(self):
        return self.Ratt.empty()
        
    def left_interlace(self,S):
        # check for interlacing with the left side of the 
        # topmost block of |S|
        if (S.IsNotEmpty() and not((S.contents[-1]).empty_Latt()) and
            self.Latt.tail()<(S.contents[-1]).head_of_Latt()):
            return 1
        else:
            return 0
            
    def  right_interlace(self,S):
        # check for interlacing with the right side of the 
        # topmost block of |S|
        if (S.IsNotEmpty() and not((S.contents[-1]).empty_Ratt()) and
            self.Latt.tail()<(S.contents[-1]).head_of_Ratt()):
            return 1
        else:
            return 0
            
    def combine(self,Bprime):
        # add block Bprime to the rear of |this| block
        self.Latt.conc(Bprime.Latt)
        self.Ratt.conc(Bprime.Ratt)
        self.Lseg.conc(Bprime.Lseg)
        self.Rseg.conc(Bprime.Rseg)
        del Bprime
        
    def clean(self,dfsnum_w,alpha,dfsnum):
        # remove all attachments to |w|; there may be several
        while not(self.Latt.empty()) and self.Latt.head()==dfsnum_w:
            self.Latt.pop()
        while not(self.Ratt.empty()) and self.Ratt.head()==dfsnum_w:
            self.Ratt.pop()
        if not(self.Latt.empty()) or not(self.Ratt.empty()):
            return 0
            
            # |Latt| and |Ratt| are empty;
            #  we record the placement of the subsegments in |alpha|.
        for e in self.Lseg.elements:
            alpha[e]=left
        for e in self.Rseg.elements:
            alpha[e]=right 
        return 1
        
    def add_to_Att(self,Att,dfsnum_w0,alpha,dfsnum):
        # add the block to the rear of |Att|. Flip if necessary
        if not(self.Ratt.empty()) and self.head_of_Ratt()>dfsnum_w0:
            self.flip()
        Att.conc(self.Latt)
        Att.conc(self.Ratt) 
        # This needs some explanation. 
        # Note that |Ratt| is either empty or {w0}.
        # Also if |Ratt| is non-empty then all subsequent
        # sets are contained in {w0}. 
        # So we indeed compute an ordered set of attachments.
        for e in self.Lseg.elements:
            alpha[e]=left
        for e in self.Rseg.elements:
            alpha[e]=right
            #=============================================================================#
            
            
            
            #=============================================================================#
            # GLOBALS:
left=1
right=2
G=pt_graph()

reached={}
dfsnum={}
parent={}
dfs_count=0
lowpt={}
Del=[]
lowpt1={}
lowpt2={}
alpha={}
Att=List()
cur_nr=0
sort_num={}
tree_edge_into={}
#=============================================================================#



#=============================================================================#
def planarity_test(Gin):
# planarity_test decides whether the InputGraph is planar.
# it also order the adjecentLists in counterclockwise.

###    SaveGmlGraph(Gin,"/home/ramazan/Leda/Graphs/rama.gml")

    n=Gin.Order() # number of nodes
    if n<3: return 1
    if not(Gin.QDirected()) and Gin.Size()>3*n-6: return 0 # number of edges
    if Gin.QDirected() and Gin.Size()>6*n-12: return 0
    
    #--------------------------------------------------------------
    # make G a copy of Gin and make G bidirected
    
    global G,cur_nr
    G=pt_graph()
    
    for v in Gin.vertices:
        G.new_node(v)
    for e in Gin.Edges():
        G.new_edge(source(e),target(e))
        
    cur_nr=0
    nr={}
    cost={}
    n=G.number_of_nodes()
    for v in G.all_nodes():
        nr[v]=cur_nr
        cur_nr=cur_nr+1
    for e in G.all_edges():
        if nr[source(e)] < nr[target(e)]:
            cost[e]=n*nr[source(e)] + nr[target(e)]
        else:
            cost[e]=n*nr[target(e)] + nr[source(e)]
    G.sort_edges(cost)
    
    L=List(G.all_edges())
    while not(L.empty()):
        e=L.pop()
        if (not(L.empty()) and source(e)==target(L.head())
            and source(L.head())==target(e)):
            L.pop()
        else:
            G.new_edge(target(e),source(e))
            #--------------------------------------------------------------
            
            
            #--------------------------------------------------------------    
            # make G biconnected
    Make_biconnected_graph()
    #--------------------------------------------------------------
    
    
    #--------------------------------------------------------------
    # make H a copy of G
    #
    # We need the biconnected version of G (G will be further modified
    # during the planarity test) in order to construct the planar embedding.
    # So we store it as a graph H.
    H=deepcopy(G)
    #--------------------------------------------------------------
    
    
    #--------------------------------------------------------------    
    # test planarity
    
    global dfsnum,parent,alpha,Att
    
    dfsnum={}
    parent={}
    for v in G.all_nodes():
        parent[v]=None      
        
    reorder()
    
    alpha={}
    for e in G.all_edges():
        alpha[e]=0
    Att=List()
    alpha[G.first_adj_edge(G.first_node())] = left
    
    if not(strongly_planar(G.first_adj_edge(G.first_node()),Att)):
        return 0
        #--------------------------------------------------------------
        
        
        #--------------------------------------------------------------
        # construct embedding
        
    global sort_num,tree_edge_into
    
    T=List()
    A=List()
    
    cur_nr=0
    sort_num={}
    tree_edge_into={}
    
    embedding(G.first_adj_edge(G.first_node()),left,T,A)
    
    # |T| contains all edges incident to the first node except the
    # cycle edge into it. 
    # That edge comprises |A|.
    T.conc(A)
    
    for e in T.elements:
        sort_num[e]=cur_nr 
        cur_nr=cur_nr+1 
        
    H.sort_edges(sort_num)  
    #--------------------------------------------------------------
    
    return H.all_edges() # ccwOrderedEges
    
    #=============================================================================#
    
    
    #=============================================================================#
def pt_DFS(v):

    global G,reached
    
    S=Stack()
    
    if reached[v]==0:
        reached[v]=1
        S.Push(v)
        
    while S.IsNotEmpty():
        v=S.Pop()
        for w in G.adj_nodes(v):
            if reached[w]==0:
                reached[w]=1
                S.Push(w)
                #=============================================================================#
                
                
                #=============================================================================#
def Make_biconnected_graph():
# We first make it connected by linking all roots of a DFS-forest.
# Assume now that G is connected.
# Let a be any articulation point and let u and v be neighbors
# of a belonging to different biconnected components.
# Then there are embeddings of the two components with the edges
# {u,a} and {v,a} on the boundary of the unbounded face.
# Hence we may add the edge {u,v} without destroying planarity.
# Proceeding in this way we make G biconnected.

    global G,reached,dfsnum,parent,dfs_count,lowpt
    
    #-------------------------------------------------------------- 
    # We first make G connected by linking all roots of the DFS-forest.
    reached={}
    for v in G.all_nodes():
        reached[v]=0
    u=G.first_node()
    
    for v in G.all_nodes():
        if  not(reached[v]):
            # explore the connected component with root v
            pt_DFS(v)
            if u!=v:
                # link v's component to the first component
                G.new_edge(u,v)
                G.new_edge(v,u)
                #-------------------------------------------------------------- 
                
                
                #-------------------------------------------------------------- 
                # We next make G biconnected.
    for v in G.all_nodes():
        reached[v]=0
    dfsnum={}
    parent={}
    for v in G.all_nodes():
        parent[v]=None
    dfs_count=0
    lowpt={}
    dfs_in_make_biconnected_graph(G.first_node())
    #--------------------------------------------------------------
    
    
    #=============================================================================#
    
    
    
    #=============================================================================#
def dfs_in_make_biconnected_graph(v):
# This procedure determines articulation points and adds appropriate
# edges whenever it discovers one.

    global G,reached,dfsnum,parent,dfs_count,lowpt
    
    dfsnum[v]=dfs_count
    dfs_count=dfs_count+1
    lowpt[v]=dfsnum[v]
    reached[v]=1
    
    if not(G.first_adj_edge(v)): return # no children
    
    u=target(G.first_adj_edge(v)) # first child
    
    for e in G.adj_edges(v):
        w=target(e)
        if not(reached[w]):
            # e is a tree edge
            parent[w]=v
            dfs_in_make_biconnected_graph(w)
            if lowpt[w]==dfsnum[v]:
                # v is an articulation point. We now add an edge.
                # If w is the first child and v has a parent then we 
                # connect w and parent[v], if w is a first child and v 
                # has no parent then we do nothing.
                # If w is not the first child then we connect w to the
                # first child.
                # The net effect of all of this is to link all children
                # of an articulation point to the first child and the
                # first child to the parent (if it exists).
                if w==u and parent[v]:
                    G.new_edge(w,parent[v])
                    G.new_edge(parent[v],w)
                if w!=u:
                    G.new_edge(u,w)
                    G.new_edge(w,u)
                    
            lowpt[v]=min(lowpt[v],lowpt[w])
            
        else:
            lowpt[v]=min(lowpt[v],dfsnum[w]) # non tree edge
            #=============================================================================#
            
            
            
            #=============================================================================#
def reorder():
# The procedure reorder first performs DFS to compute dfsnum, parent
# lowpt1 and lowpt2, and the list Del of all forward edges and all
# reversals of tree edges.
# It then deletes the edges in Del and finally reorders the edges.

    global G,dfsnum,parent,reached,dfs_count,Del,lowpt1,lowpt2
    
    reached={}
    for v in G.all_nodes():
        reached[v]=0
    dfs_count = 0
    Del=[]
    lowpt1={}
    lowpt2={}
    
    dfs_in_reorder(G.first_node())
    
    #--------------------------------------------------------------       
    # remove forward and reversals of tree edges
    for e in Del:
        G.del_edge(e)
        #--------------------------------------------------------------
        
        
        #--------------------------------------------------------------     
        # we now reorder adjacency lists 
    cost={}
    for e in G.all_edges():
        v = source(e)
        w = target(e)
        if dfsnum[w]<dfsnum[v]:
            cost[e]=2*dfsnum[w]
        elif lowpt2[w]>=dfsnum[v]:
            cost[e]=2*lowpt1[w]
        else:
            cost[e]=2*lowpt1[w]+1
    G.sort_edges(cost)
    #--------------------------------------------------------------
    
    
    #=============================================================================#
    
    
    
    #=============================================================================#
def dfs_in_reorder(v):

    global G,dfsnum,parent,reached,dfs_count,Del,lowpt1,lowpt2
    
    #--------------------------------------------------------------    
    dfsnum[v]=dfs_count
    dfs_count=dfs_count+1
    lowpt1[v]=lowpt2[v]=dfsnum[v]
    reached[v]=1
    for e in G.adj_edges(v):
        w = target(e);
        if not(reached[w]):
            # e is a tree edge
            parent[w]=v
            dfs_in_reorder(w)
            lowpt1[v]=min(lowpt1[v],lowpt1[w])
        else:
            lowpt1[v]=min(lowpt1[v],dfsnum[w]) # no effect for forward edges
            if dfsnum[w]>=dfsnum[v] or w==parent[v]: 
               # forward edge or reversal of tree edge
                Del.append(e) 
                #--------------------------------------------------------------
                
                
                #--------------------------------------------------------------
                # we know |lowpt1[v]| at this point and now make a second pass over all
                # adjacent edges of |v| to compute |lowpt2|
    for e in G.adj_edges(v):
        w = target(e)
        if parent[w]==v:
            # tree edge
            if lowpt1[w]!=lowpt1[v]:
                lowpt2[v]=min(lowpt2[v],lowpt1[w])
            lowpt2[v]=min(lowpt2[v],lowpt2[w])
        else:
            # all other edges 
            if lowpt1[v]!=dfsnum[w]:
                lowpt2[v]=min(lowpt2[v],dfsnum[w])
                #--------------------------------------------------------------
                
                
                #=============================================================================#
                
                
                
                #=============================================================================#
def strongly_planar(e0,Att):
# We now come to the heart of the planarity test: procedure strongly_planar.
# It takes a tree edge e0=(x,y) and tests whether the segment S(e0) is 
# strongly planar. 
# If successful it returns (in Att) the ordered list of attachments of S(e0) 
# (excluding x); high DFS-numbers are at the front of the list.
# In alpha it records the placement of the subsegments.
#
# strongly_planar operates in three phases.
# It first constructs the cycle C(e0) underlying the segment S(e0). 
# It then constructs the interlacing graph for the segments emanating >from the
# spine of the cycle.
# If this graph is non-bipartite then the segment S(e0) is non-planar.
# If it is bipartite then the segment is planar.
# In this case the third phase checks whether the segment is strongly planar 
# and, if so, computes its list of attachments.

    global G,alpha,dfsnum,parent
    
    #--------------------------------------------------------------
    # DETERMINE THE CYCLE C(e0)
    # We determine the cycle "C(e0)" by following first edges until a back 
    # edge is encountered. 
    # |wk| will be the last node on the tree path and |w0|
    # is the destination of the back edge.
    x=source(e0)
    y=target(e0)
    e=G.first_adj_edge(y)
    wk=y
    
    while dfsnum[target(e)]>dfsnum[wk]:  # e is a tree edge
        wk=target(e)
        e=G.first_adj_edge(wk)
    w0=target(e)
    #--------------------------------------------------------------
    
    
    #--------------------------------------------------------------
    # PROCESS ALL EDGES LEAVING THE SPINE
    # The second phase of |strongly_planar| constructs the connected 
    # components of the interlacing graph of the segments emananating 
    # from the the spine of the cycle "C(e0)". 
    # We call a connected component a "block". 
    # For each block we store the segments comprising its left and 
    # right side (lists |Lseg| and |Rseg| contain the edges defining 
    # these segments) and the ordered list of attachments of the segments
    # in the block; 
    # lists |Latt| and |Ratt| contain the DFS-numbers of the attachments; 
    # high DFS-numbers are at the front of the list. 
    #
    # We process the edges leaving the spine of "S(e0)" starting at 
    # node |wk| and working backwards. 
    # The interlacing graph of the segments emanating from
    # the cycle is represented as a stack |S| of blocks.
    w=wk
    S=Stack()
    
    while w!=x:
        count=0
        for e in G.adj_edges(w):
            count=count+1
            
            if count!=1: # no action for first edge
                # TEST RECURSIVELY
                # Let "e" be any edge leaving the spine. 
                # We need to test whether "S(e)" is strongly planar 
                # and if so compute its list |A| of attachments.
                # If "e" is a tree edge we call our procedure recursively 
                # and if "e" is a back edge then "S(e)" is certainly strongly 
                # planar and |target(e)| is the only attachment.
                # If we detect non-planarity we return false and free
                # the storage allocated for the blocks of stack |S|.
                A=List()
                if dfsnum[w]<dfsnum[target(e)]: 
                    # tree edge
                    if not(strongly_planar(e,A)):
                        while S.IsNotEmpty(): S.Pop()
                        return 0                    
                else:
                    A.append(dfsnum[target(e)]) # a back edge
                    
                    # UPDATE STACK |S| OF ATTACHMENTS
                    # The list |A| contains the ordered list of attachments 
                    # of segment "S(e)". 
                    # We create an new block consisting only of segment "S(e)" 
                    # (in its L-part) and then combine this block with the 
                    # topmost block of stack |S| as long as there is interlacing. 
                    # We check for interlacing with the L-part. 
                    # If there is interlacing then we flip the two sides of the 
                    # topmost block. 
                    # If there is still interlacing with the left side then the 
                    # interlacing graph is non-bipartite and we declare the graph 
                    # non-planar (and also free the storage allocated for the
                    # blocks).
                    # Otherwise we check for interlacing with the R-part. 
                    # If there is interlacing then we combine |B| with the topmost
                    # block and repeat the process with the new topmost block.
                    # If there is no interlacing then we push block |B| onto |S|.
                B=block(e,A)
                
                while 1:
                    if B.left_interlace(S): (S.contents[-1]).flip()
                    if B.left_interlace(S): 
                        del B
                        while S.IsNotEmpty(): S.Pop() 
                        return 0
                    if B.right_interlace(S): B.combine(S.Pop())
                    else: break
                S.Push(B)
                
                # PREPARE FOR NEXT ITERATION
                # We have now processed all edges emanating from vertex |w|. 
                # Before starting to process edges emanating from vertex
                # |parent[w]| we remove |parent[w]| from the list of attachments
                # of the topmost 
                # block of stack |S|. 
                # If this block becomes empty then we pop it from the stack and 
                # record the placement for all segments in the block in array
                # |alpha|.
        while (S.IsNotEmpty() and
               (S.contents[-1]).clean(dfsnum[parent[w]],alpha,dfsnum)):
            S.Pop()
            
        w=parent[w]
        #--------------------------------------------------------------
        
        
        #--------------------------------------------------------------
        # TEST STRONG PLANARITY AND COMPUTE Att
        # We test the strong planarity of the segment "S(e0)". 
        # We know at this point that the interlacing graph is bipartite. 
        # Also for each of its connected components the corresponding block 
        # on stack |S| contains the list of attachments below |x|. 
        # Let |B| be the topmost block of |S|. 
        # If both sides of |B| have an attachment above |w0| then 
        # "S(e0)" is not strongly planar. 
        # We free the storage allocated for the blocks and return false.
        # Otherwise (cf. procedure |add_to_Att|) we first make sure that 
        # the right side of |B| attaches only to |w0| (if at all) and then 
        # add the two sides of |B| to the output list |Att|.
        # We also record the placements of the subsegments in |alpha|.
    Att.clear()
    
    while S.IsNotEmpty():
        B = S.Pop()
        
        if (not(B.empty_Latt()) and not(B.empty_Ratt()) and
            B.head_of_Latt()>dfsnum[w0] and B.head_of_Ratt()>dfsnum[w0]):
            del B
            while S.IsNotEmpty(): S.Pop()
            return 0
        B.add_to_Att(Att,dfsnum[w0],alpha,dfsnum)
        del B
        
        # Let's not forget that "w0" is an attachment
        # of "S(e0)" except if w0 = x.
    if w0!=x: Att.append(dfsnum[w0])
    
    return 1
    #--------------------------------------------------------------
    
    
    #=============================================================================#
    
    
    
    #=============================================================================#
def embedding(e0,t,T,A):
# embed: determine the cycle "C(e0)" 
#
# We start by determining the spine cycle.
# This is precisley as in |strongly_planar|. 
# We also record for the vertices w_r+1, ...,w_k, and w_0 the 
# incoming cycle edge either in |tree_edge_into| or in the local
# variable |back_edge_into_w0|.

    global G,dfsnum,cur_nr,sort_num,tree_edge_into,parent
    
    x=source(e0)
    y=target(e0)
    tree_edge_into[y]=e0
    e=G.first_adj_edge(y)
    wk=y
    
    while (dfsnum[target(e)]>dfsnum[wk]):  # e is a tree edge
        wk=target(e)
        tree_edge_into[wk]=e
        e=G.first_adj_edge(wk)
        
    w0=target(e)
    back_edge_into_w0=e
    
    
    # process the subsegments
    w=wk
    Al=List()
    Ar=List()
    Tprime=List()
    Aprime=List()
    
    T.clear()  
    T.append(e)    # |e=(wk,w0)| at this point
    
    while w!=x:
        count=0
        for e in G.adj_edges(w):
            count=count+1
            if count!=1: # no action for first edge
                # embed recursively
                if dfsnum[w]<dfsnum[target(e)]:
                    # tree edge
                    if t==alpha[e]:
                        tprime=left
                    else:
                        tprime=right
                    embedding(e,tprime,Tprime,Aprime)
                else: 	
                    # back edge
                    Tprime.append(e)
                    Aprime.append(reversal(e))
                    
                    # update lists |T|, |Al|, and |Ar|
                if t==alpha[e]:
                    Tprime.conc(T)
                    T.conc(Tprime) # T = Tprime conc T
                    Al.conc(Aprime) # Al = Al conc Aprime
                else:	
                    T.conc(Tprime) # T = T conc Tprime
                    Aprime.conc(Ar)
                    Ar.conc(Aprime) # Ar = Aprime conc Ar
                    
                    # compute |w|'s adjacency list and prepare for next iteration
        T.append(reversal(tree_edge_into[w])) # (w_j-1,w_j)
        for e in T.elements:
            sort_num[e]=cur_nr
            cur_nr=cur_nr+1
            
            # |w|'s adjacency list is now computed; we clear |T| and 
            # prepare for the next iteration by moving all darts incident
            # to |parent[w]| from |Al| and |Ar| to |T|.
            # These darts are at the rear end of |Al| and at the front end
            # of |Ar|.
        T.clear()
        
        while not(Al.empty()) and source(Al.tail())==parent[w]: 
        # |parent[w]| is in |G|, |Al.tail| in |H|
            T.push(Al.Pop()) # Pop removes from the rear
            
        T.append(tree_edge_into[w]) # push would be equivalent
        
        while not(Ar.empty()) and source(Ar.head())==parent[w]: 
            T.append(Ar.pop()); # pop removes from the front
            
        w=parent[w]
        
        # prepare the output
    A.clear()
    A.conc(Ar)
    A.append(reversal(back_edge_into_w0))
    A.conc(Al)
    #=============================================================================#
    
    
    
    
    
    
    
    
    
    
    
    
    ###############################################################################
    # DEBUG                                                                       #
    ###############################################################################
    
    #=============================================================================#
def PrintGraph(G):
    print "============================================================"
    print "V : "
    for v in G.all_nodes():
        print "[%i]" %(v-1)
    print
    
    print "E : "
    for e in G.all_edges():
        print "[%i]---->[%i]" %((source(e)-1),(target(e)-1))
    print
    
    for v in G.all_nodes():
        print "[%i] : " %(v-1)
        for e in G.adj_edges(v):
            print "    [%i]---->[%i]" %((source(e)-1),(target(e)-1))
    print
    #=============================================================================#
    
    #=============================================================================#
def SaveGmlGraph(G, fileName):    
    file = open(fileName, 'w')
    
    file.write("graph [ \n")
    file.write("  directed 1 \n \n")
    
    for v in G.vertices:
        file.write("  node [ id ")
        n=str(v)
        file.write(n)
        file.write(" ] \n")
        
    file.write("\n")
    
    for e in G.Edges():
        file.write("  edge [ \n")
        
        file.write("    source ")
        k=str(e[0])
        file.write(k)
        file.write("\n")
        
        file.write("    target ")
        k=str(e[1])
        file.write(k)
        file.write("\n")
        
        file.write("  ] \n")
        
    file.write("] \n")
    #=============================================================================#
